选项
public final static int TCP_NODELAY = 0x0001; public final static int SO_REUSEADDR = 0x04; public final static int SO_LINGER = 0x0080; public final static int SO_TIMEOUT = 0x1006; public final static int SO_SNDBUF = 0x1001; public final static int SO_RCVBUF = 0x1002; public final static int SO_KEEPALIVE = 0x0008; public final static int SO_OOBINLINE = 0x1003;
一。TCP_NODELAY
在默认false下,客户端向服务器发送数据时,会根据数据包的大小决定是否立即发送。当数据包中的数据很少时,如只有1个字节,而数据包的头却有几十个字
节(IP头+TCP头)时,系统会在发送之前先将较小的包合并到软大的包后,一起将数据发送出去。在发送下一个数据包时,系统会等待服务器对前一个数据包
的响应,当收到服务器的响应后,再发送下一个数据包,这就是所谓的Nagle算法
二。SO_REUSEADDR
如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息, 抛出“Address
already in use: JVM_Bind”。如果你的服务程序停止后想立即重启,不等60秒,而新套接字依旧 使用同一端口,此时 SO_REUSEADDR 选项非常有用。
在Windows平台,多个Socket新建立对象可以绑定在同一个端口上,这些新连接是非TIME_WAIT状态的。这样做并没有多大意义。
在Linux平台,只有TCP状态位于 TIME_WAIT ,才可以重用 端口。这才是正确的行为。
publicclass Test { public static void main(String[] args) { try { ServerSocket socket1 = new ServerSocket(); ServerSocket socket2 = new ServerSocket(); socket1.setReuseAddress(true); socket1.bind(new InetSocketAddress("127.0.0.1", 8899)); System.out.println("socket1.getReuseAddress():" + socket1.getReuseAddress()); socket2.setReuseAddress(true); socket2.bind(new InetSocketAddress("127.0.0.1", 8899)); System.out.println("socket2.getReuseAddress():" + socket1.getReuseAddress()); } catch (Exception e) { e.printStackTrace(); } } }
使用SO_REUSEADDR选项时有两点需要注意:
1. 必须在调用bind方法之前使用setReuseAddress方法来打开SO_REUSEADDR选项。因此,要想使用SO_REUSEADDR选项,就不能通过Socket
类的构造方法来绑定端口。
2. 必须将绑定同一个端口的所有的Socket对象的SO_REUSEADDR选项都打开才能起作用。socket1和socket2都使用了setReuseAddress方法打开
了各自的SO_REUSEADDR选项。
在Windows平台表现的特点是不正确的:
socket1.getReuseAddress():true socket2.getReuseAddress():true
在Linux平台表现的特点是正确的: 因为第一个连接不是TIME_WAIT状态的,第二个连接就不能使用8899端口
socket1.getReuseAddress():true java.net.BindException: Address already in use at java.net.PlainSocketImpl.socketBind(Native Method) at java.net.PlainSocketImpl.bind(PlainSocketImpl.java:383) at java.net.ServerSocket.bind(ServerSocket.java:328) at java.net.ServerSocket.bind(ServerSocket.java:286) at com.Test.main(Test.java:15)
三。SO_LINGER
这个Socket选项可以影响close方法的行为。在默认情况下,当调用close方法后,将立即返回;如果这时仍然有未被送出的数据包,那么这些数据包将
被丢弃。
如果将linger参数设为一个正整数n时(n的值最大是65,535),在调用close方法后,将最多被阻塞n秒。在这n秒内,系统将尽量将未送出的数
据包发送出去;如果超过了n秒,如果还有未发送的数据包,这些数据包将全部被丢弃;而close方法会立即返回。
如果将linger设为0,和关闭SO_LINGER选项的作用是一样的。
如果底层的Socket实现不支持SO_LINGER都会抛出SocketException例外。
当给linger参数传递负数值时,setSoLinger还会抛出一个IllegalArgumentException例外。可以通过getSoLinger方法得到延迟关闭的时间,如果
返回-1,则表明SO_LINGER是关闭的。例如,下面的代码将延迟关闭的时间设为1分钟:
if(socket.getSoLinger() == -1) socket.setSoLinger(true, 60);
四。SO_TIMEOUT
可以通过这个选项来设置读取数据超时。当输入流的read方法被阻塞时,如果设置timeout,那么系统在等待了timeout毫秒后会抛出
InterruptedIOException。在抛出后,输入流并未关闭,你可以继续通过read方法读取数据。
如果将timeout设为0,就意味着read将会无限等待下去,直到服务端程序关闭这个Socket。这也是timeout的默认值。
五。SO_SNDBUF
在默认情况下,输出流的发送缓冲区是8096个字节(8K)。这个值是Java所建议的输出缓冲区的大小。如果这个默认值不能满足要求,可以用
setSendBufferSize方法来重新设置缓冲区的大小。但最好不要将输出缓冲区设得太小,否则会导致传输数据过于频繁,从而降低网络传输的效率。
六。SO_RCVBUF
The value of SO_RCVBUF is also used to set the TCP receive window that is advertized to the remote peer. Generally, the window size
can be modified at any time when a socket is connected. However, if a receive window larger than 64K is required then this must be
requested before the socket is connected to the remote peer. There are two cases to be aware of:
- For sockets accepted from a ServerSocket, this must be done by calling
ServerSocket.setReceiveBufferSize(int)
before the
ServerSocket is bound to a local address.
2. For client sockets, setReceiveBufferSize() must be called before connecting the socket to its remote peer.
七。SO_KEEPALIVE
如果将这个选项打开,客户端Socket每隔段的时间就会利用空闲的连接向服务器发送一个数据包。这个数据包并没有其它的作用,只是为了检
测一下服务器是否仍处于活动状态。如果服务器未响应这个数据包,那么客户端Socket将关闭。
如果将这个选项关闭,客户端Socket在服务器无效的情况下可能会长时间不会关闭。