频道栏目
首页 > 资讯 > Java > 正文

Java网络编程从入门到精通(29):服务端Socket的选项

11-03-01        来源:[db:作者]  
收藏   我要投稿

ServerSocket类有以下三个选项:

1.       SO_TIMEOUT: 设置accept方法的超时时间。

2.       SO_REUSEADDR:设置服务端同一个端口是否可以多次绑定。

3.       SO_RECBUF:设置接收缓冲区的大小。

一、SO_TIMEOUT选项

可以通过SeverSocket类的两个方法(setSoTimeout和getSoTimeout)来设置和获得SO_TIMEOUT选项的值,这两个方法的定义如下:


public synchronized void setSoTimeout(int timeout) throws SocketException
public synchronized int getSoTimeout() throws IOException


setSoTimeout方法的timeout参数表示accept方法的超时时间,单位是毫秒。在通常情况下,ServerSocket类的accept方法在等待客户端请求时处于无限等待状态。如HTTP服务器在没有用户访问网页时会一直等待用户的请求。一般不需要对服务端设置等待客户端请求超时,但在某些特殊情况下,服务端规定客户端必须在一定时间内向服务端发出请求,这时就要设置等待客户端请求超时,也就是accept方法的超时时间。当设置客户端请求超时后,accept方法在等待超时时间后抛出一个SocketTimeoutException异常。下面的代码演示了如何设置和获得SO_TIMEOUT选项的值,超时时间通过命令行参数方式传入AcceptTimeout。


package server;

import java.net.*;

public class AcceptTimeout
{
    public static void main(String[] args) throws Exception
    {
        if (args.length == 0)
            return;
        ServerSocket serverSocket = new ServerSocket(1234);
        int timeout = Integer.parseInt(args[0]);
       
        serverSocket.setSoTimeout(Integer.parseInt(args[0]));
        System.out.println((timeout > 0) ? "accept方法将在"
                + serverSocket.getSoTimeout() + "毫秒后抛出异常!" : "accept方法永远阻塞!");;
        serverSocket.accept();
    }
}

执行下面的命令:


java server.AcceptTimeout 3000

运行结果:

accept方法将在3000毫秒后抛出异常!
Exception in thread "main" java.net.SocketTimeoutException: Accept timed out
    at java.net.PlainSocketImpl.socketAccept(Native Method)
    at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:384)
    at java.net.ServerSocket.implAccept(ServerSocket.java:450)
    at java.net.ServerSocket.accept(ServerSocket.java:421)
    at chapter5.AcceptTimeout.main(AcceptTimeout.java:16)

setSoTimeout方法可以在ServerSocket对象绑定端口之前调用,也以在绑定端口之后调用。如下面的代码也是正确的:

ServerSocket serverSocket = new ServerSocket();
serverSocket.setSoTimeout(3000);
serverSocket.bind(new InetSocketAddress(1234));

二、SO_REUSEADDR选项

SO_REUSEADDR选项决定了一个端口是否可以被绑定多次。可以通过SeverSocket类的两个方法(setReuseAddres和getReuseAddress)来设置和获得SO_TIMEOUT选项的值,这两个方法的定义如下:


public void setReuseAddress(boolean on) throws SocketException
public boolean getReuseAddress() throws SocketException


在大多数操作系统中都不允许一个端口被多次绑定。如果一个ServerSocket对象绑定了已经被占用的端口,那么ServerSocket的构造方法或bind方法就会抛出一个BindException异常。

Java提供这个选项的主要目的是为了防止由于频繁绑定释放一个固定端口而使系统无法正常工作。当ServerSocket对象关闭后,如果ServerSocket对象中仍然有未处理的数据,那么它所绑定的端口可能在一段时间内不会被释放。这就会造成其他的ServerSocket对象无法绑定这个端口。在设置这个选项时,如果某个端口是第一次被绑定,无需调用setReuseAddress方法,而再次绑定这个端口时,必须使用setReuseAddress方法将这个选项设为true。而且这个方法必须在调用bind方法之前调用。下面的代码演示了如何设置和获得这个选项的值:


package server;

import java.net.*;

public class TestReuseAddr1
{
    public static void main(String[] args) throws Exception
    {
        ServerSocket serverSocket1 = new ServerSocket(1234);
        System.out.println(serverSocket1.getReuseAddress());
       
        ServerSocket serverSocket2 = new ServerSocket();
        serverSocket2.setReuseAddress(true);
        serverSocket2.bind(new InetSocketAddress(1234));
       
        ServerSocket serverSocket3 = new ServerSocket();
        serverSocket3.setReuseAddress(true);
        serverSocket3.bind(new InetSocketAddress(1234));
    }
}


运行结果:false


在上面代码中第一次绑定端口1234,因此,serverSocket1对象无需设置SO_REUSEADDR选项(这个选项在大多数操作系统上的默认值是false)。而serverSocket2和serverSocket3并不是第一次绑定端口1234,因此,必须设置这两个对象的SO_REUSEADDR值为true。在设置SO_REUSEADDR选项时要注意,必须在ServerSocket对象绑定端口之前设置这个选项。

    也许有的读者可能有这样的疑问。如果多个ServerSocket对象同时绑定到一个端口上,那么当客户端向这个端口发出请求时,该由哪个ServerSocket对象来接收客户端请求呢?在给出答案之前,让我们先看看下面的代码的输出结果是什么。


package server;

import java.net.*;

public class TestReuseAddr2 extends Thread
{
    String s;
    public void run()
    {
        try
        {
            ServerSocket serverSocket = new ServerSocket();
            serverSocket.setReuseAddress(true);
            serverSocket.bind(new InetSocketAddress(1234));
            Socket socket = serverSocket.accept();
            System.out.println(s + ":" + socket);
            socket.close();
            serverSocket.close();
        }
        catch (Exception e)
        {
        }
    }
    public TestReuseAddr2(String s)
    {
        this.s = s;
    }
    public static void main(String[] args)
    {
        for (int i = 1; i <= 5; i++)
            new TestReuseAddr2("ServerSocket" + i).start();
    }
}

 

执行下面的命令:

java server.TestReuseAddr2


    连续执行5次下面的命令:


telnet localhost 1234

执行结果:


ServerSocket1:Socket[addr=/127.0.0.1,port=11724,localport=1234]
ServerSocket3:Socket[addr=/127.0.0.1,port=11725,localport=1234]
ServerSocket5:Socket[addr=/127.0.0.1,port=11726,localport=1234]
ServerSocket2:Socket[addr=/127.0.0.1,port=11727,localport=1234]
ServerSocket4:Socket[addr=/127.0.0.1,port=11728,localport

相关TAG标签
上一篇:Java网络编程从入门到精通(30):定制accept方法
下一篇:Java网络编程从入门到精通(28):获取ServerSocket信息的方法及FTP原理
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站