Java Socket重要参数讲解
Socket参数定义在public interface SocketOptions中
TCP_NODELAY
SO_BINDADDR
SO_REUSEADDR
SO_LINGER
SO_TIMEOUT
SO_SNDBUF/SO_RCVBUF
SO_KEEPALIVE
SO_OOBINLINE
除TCP_NODELAY这个参数为,其它都以SO开头,SO即Socket Options首字母吧。
1.TCP_NODELAY
禁用纳格算法,将数据立即发送出去。纳格算法是以减少封包传送量来增进TCP/IP网络的效能,当我们调用下面代码,如:
Socket socket = new Socket();
socket.connect(new InetSocketAddress(host, 8000));
OutputStream out = socket.getOutputStream();
String head = "hello ";
String body = "world\r\n";
out.write(head.getBytes());
out.write(body.getBytes());
我们发送了hello,当hello没有收到ack确认(TCP是可靠连接,发送的每一个数据都要收到对方的一个ack确认,否则就要重发)的时候,根据纳格算法,world不会立马发送,会等待,要么等到ack确认(最多等100ms对方会发过来的),要么等到TCP缓冲区内容>=MSS,很明显这里没有机会,我们写了world后再也没有写数据了,所以只能等到hello的ack我们才会发送world,除非我们禁用纳格算法,数据就会立即发送了。
{TcpNoDelay和缓冲区的联系??????} out.write不会发送吧??啥时候发送数据??
在默认情况下,客户端向服务器发送数据时,会根据数据包的大小决定是否立即发送。当数据包中的数据很少时,如只有1个字节,而数据包的头却有几十个字节(IP头+TCP头)时,系统会在发送之前先将较小的包合并到软大的包后,一起将数据发送出去。在发送下一个数据包时,系统会等待服务器对前一个数据包的响应,当收到服务器的响应后,再发送下一个数据包,这就是所谓的Nagle算法;在默认情况下,Nagle算法是开启的。
这种算法虽然可以有效地改善网络传输的效率,但对于网络速度比较慢,而且对实时性的要求比较高的情况下(如游戏、Telnet等),使用这种方式传输数据会使得客户端有明显的停顿现象。因此,最好的解决方案就是需要Nagle算法时就使用它,不需要时就关闭它。而使用setTcpToDelay正好可以满足这个需求。当使用setTcpNoDelay(true)将Nagle算法关闭后,客户端每发送一次数据,无论数据包的大小都会将这些数据发送出去。
2.SO_REUSEADDR
当接收方通过socket close方法关闭socket时,如果网络上还有发送到这个socket数据,底层socket不会立即释放本地端口,而是等待一段时间,确保接收到了网络上发送过来的延迟数据,然后在释放端口。socket接收到延迟数据后,不会对这些数据作任何处理。socket接收延迟数据目的是确保这些数据不会被其他碰巧绑定到同样的端口的新进程收到。
客户端一般采用随机端口,因此出现两个客户端绑定到同样的端口可能性不大,而服务器端都是使用固定端口,当服务器端程序关闭后,有可能他的端口还会被占用一段时间,如果此时立刻在此主机上重启服务器程序,由于服务器端口被占用,使得服务器程序无法绑定改端口,启动失败。
为了确保一个进程关闭socket后,即使它还没释放端口,同一主机上的其他进程可以立刻重用该端口,可以调用socket的setReuseAddress(true)
需要注意的是setReuseAddress(boolean on)方法必须在socket还未绑定到一个本地端口之前调用,否则无效
3.SO_LINGER
当我们调用socket.close()返回时,socket已经write的数据未必已经发送到对方了,例如
Socket socket = new Socket();
socket.connect(new InetSocketAddress(host, 8000));
OutputStream out = socket.getOutputStream();
String head = "hello ";
String body = "world\r\n";
out.write(head.getBytes());
out.write(body.getBytes());
socket.close();
这里调用了socket.close()返回时,hello和world未必已经成功发送到对方了,如果我们设置了linger而不小于0,如:
....
socket.setSoLinger(true, 100)
......
socket.close();
那么close会等到发送的数据已经确认了才返回。但是如果对方宕机,超时,那么会根据linger设定的时间返回。
http://blog.csdn.net/a19881029/article/details/52382956 细说Java Socket中的setSoLinger方法
1>setSoLinger(true, 0)
当网卡收到关闭连接请求后,无论数据是否发送完毕,立即发送RST包关闭连接
2>setSoLinger(true, delay_time)
当网卡收到关闭连接请求后,等待delay_time
如果在delay_time过程中数据发送完毕,正常四次挥手关闭连接
如果在delay_time过程中数据没有发送完毕,发送RST包关闭连接
4.SO_TIMEOUT
设置socket调用InputStream读数据的超时时间,以毫秒为单位,如果超过这个时候,会抛出java.net.SocketTimeoutException。
默认值为0,表示无限等待
5.SO_SNDBUF/SO_RCVBUF
TCP发送缓存区和接收缓存区,默认是8192(8K),一般情况下足够了,而且就算你增加了发送缓存区,对方没有增加它对应的接收缓冲,那么在TCP三握手时,最后确定的最大发送窗口还是双方最小的那个缓冲区,就算你无视,发了更多的数据,那么多出来的数据也会被丢弃。除非双方都协商好。
这可以减少传输数据的次数,提高传输效率
6.SO_KEEPALIVE
keepalive不是说TCP的常连接,当我们作为服务端,一个客户端连接上来,如果设置了keeplive为true,当对方没有发送任何数据过来,超过一个时间(看系统内核参数配置),那么我们这边会发送一个ack探测包发到对方,探测双方的TCP/IP连接是否有效(对方可能断点,断网),在Linux好像这个时间是75秒。如果不设置,那么客户端宕机时,服务器永远也不知道客户端宕机了,仍然保存这个失效的连接。
7.SO_OOBINLINE
如果这个Socket选项打开,可以通过Socket类的sendUrgentData方法向服务器发送一个单字节的数据。这个单字节数据并不经过输出缓冲区,而是立即发出。虽然在客户端并不是使用OutputStream向服务器发送数据,但在服务端程序中这个单字节的数据是和其它的普通数据混在一起的。因此,在服务端程序中并不知道由客户端发过来的数据是由OutputStream还是由sendUrgentData发过来的。下面是sendUrgentData方法的声明:
public void sendUrgentData(int data) throws IOException
虽然sendUrgentData的参数data是int类型,但只有这个int类型的低字节被发送,其它的三个字节被忽略。
http://www.cnblogs.com/ggjucheng/archive/2012/01/06/2314679.html