在Java的Socket中,主要包含了如下可设置的TCP参数。java
属性算法 |
说明编程 |
默认值服务器 |
SO_TIMEOUT网络 |
对ServerSocket来讲表示等待链接的最长空等待时间; 对Socket来讲表示读数据最长空等待时间。socket |
0测试 |
TCP_NODELAY字体 |
是否一有数据就立刻发送。this |
falsespa |
SO_LINGER |
优雅地关闭套接字,或者马上关闭。 |
-1 |
SO_SNDBUF |
发送数据的缓冲区大小。 |
8K |
SO_RCVBUF |
接收数据的缓冲区大小。 |
8K |
SO_KEEPALIVE |
是否启用心跳机制。 |
false |
SO_REUSEADDR |
是否地址重用。 |
false |
BACKLOG |
服务端处理线程全忙后,容许多少个新请求进入等待。 |
50 |
BACKLOG用于构造服务端套接字ServerSocket对象,标识当服务器请求处理线程全满时,用于临时存放已完成三次握手的请求的队列的最大长度。若是未设置或所设置的值小于1,Java将使用默认值50。
ServerSocket serverSocket = new ServerSocket(8080, 100); |
在TCP/IP协议中,不管发送多少数据,老是要在数据前面加上协议头,同时,对方接收到数据,也须要发送ACK表示确认。为了尽量的利用网络带宽,TCP老是但愿尽量的发送足够大的数据。这里就涉及到一个名为Nagle的算法,该算法的目的就是为了尽量发送大块数据,避免网络中充斥着许多小数据块。
TCP_NODELAY选项,就是用于启用或关于Nagle算法。若是要求高实时性,有数据发送时就立刻发送,就将该选项设置为true关闭Nagle算法;若是要减小发送次数减小网络交互,就设置为false等累积必定大小后再发送。默认为false。
Socket中操做该属性的方法以下:
void setTcpNoDelay(boolean on) 启用/禁用 TCP_NODELAY(启用/禁用 Nagle 算法)。 boolean getTcpNoDelay() 测试是否启用 TCP_NODELAY。 |
关于Nagle算法介绍,请参考附录部分。
对于服务端套接字ServerSocket来讲,SO_TIMEOUT表示服务端accept方法空等待客户端链接的最长时间;对于客户端套接字Socket来讲,SO_TIMEOUT表示输入流读取数据read方法的最长等待时间。一旦超过设置的SO_TIMEOUT,程度将抛出超时异常。
ServerSocket/Socket中操做该属性的方法以下:
int getSoTimeout() 返回 SO_TIMEOUT 的设置。 void setSoTimeout(int timeout) 启用/禁用带有指定超时值的 SO_TIMEOUT,以毫秒为单位。 |
使用示例:
ServerSocket serverSocket = new ServerSocket(8080); serverSocket.setSoTimeout(30000);
Socket clientSocket = serverSocket.accept(); clientSocket.setSoTimeout(20000); |
当调用closesocket关闭套接字时,SO_LINGER将决定系统如何处理残存在套接字发送队列中的数据。处理方式无非两种:丢弃或者将数据继续发送至对端,优雅关闭链接。事实上,SO_LINGER并不被推荐使用,大多数状况下咱们推荐使用默认的关闭方式(即下方表格中的第一种状况)。
下方代码段显示linger结构语法,表格为不一样参数状况下的套接字行为。
typedef struct linger { u_short l_onoff; //开关,零或者非零 u_short l_linger; //优雅关闭最长时限 } linger; |
各字段与对应行为以下表所示。
l_onoff |
l_linger |
closesocket行为 |
发送队列 |
底层行为 |
零 |
忽略 |
当即返回。 |
保持直至发送完成。 |
系统接管套接字并保证将数据发送至对端。 |
非零 |
零 |
当即返回。 |
当即放弃。 |
直接发送RST包,自身当即复位,不用通过2MSL状态。对端收到复位错误号。 |
非零 |
非零 |
阻塞直到l_linger时间超时或数据发送完成。(套接字必须设置为阻塞) |
在超时时间段内保持尝试发送,若超时则当即放弃。 |
超时则同第二种状况,若发送完成则皆大欢喜。 |
Socket中操做该属性的方法以下:
void setSoLinger(boolean on, int linger) 启用/禁用具备指定逗留时间(以秒为单位)的SO_LINGER。 Linger最大取值为65535。 int getSoLinger() 返回 SO_LINGER 的设置。默认值为-1。 |
因为getSoLinger()方法返回的-1没有太多意思,咱们查看到Java的默认实现PlainSocketImpl.c文件中,赋值操做代码片断以下所示。
/* * Class: java_net_PlainSocketImpl * Method: socketSetOption * Signature: (IZLjava/lang/Object;)V */ JNIEXPORT void JNICALL Java_java_net_PlainSocketImpl_socketSetOption(JNIEnv *env, jobject this, jint cmd, jboolean on, jobject value) { … switch (cmd) { case java_net_SocketOptions_SO_SNDBUF : case java_net_SocketOptions_SO_RCVBUF : case java_net_SocketOptions_SO_LINGER : case java_net_SocketOptions_IP_TOS : { … if (cmd == java_net_SocketOptions_SO_LINGER) { if (on) { optval.ling.l_onoff = 1; optval.ling.l_linger = (*env)->GetIntField(env, value, fid); } else { optval.ling.l_onoff = 0; optval.ling.l_linger = 0; } optlen = sizeof(optval.ling); } else { optval.i = (*env)->GetIntField(env, value, fid); optlen = sizeof(optval.i); }
break; }
/* Boolean -> int */ default : optval.i = (on ? 1 : 0); optlen = sizeof(optval.i);
} … } |
从蓝色字体部分代码能够看出,只要赋值为false,则底层linger结构中的l_onoff和l_linger的值均为0,符合表中的第一种状况。
发送缓冲区的大小设置,默认为8K。
Socket中操做该属性的方法以下:
void setSendBufferSize(int size) 将此 Socket 的 SO_SNDBUF 选项设置为指定的值。 int getSendBufferSize() 获取此 Socket 的 SO_SNDBUF 选项的值,该值是平台在 Socket 上输出时使用的缓冲区大小。 |
接收缓冲区大小设置,默认为8K。该属性既能够在ServerSocket实例中设置,也能够在Socket实例中设置。
ServerSocket/Socket中操做该属性的方法以下:
void setReceiveBufferSize(int size) 将此 Socket 的 SO_RCVBUF 选项设置为指定的值。 int getReceiveBufferSize() 获取此 Socket 的 SO_RCVBUF 选项的值,该值是平台在 Socket 上输入时使用的缓冲区大小。 |
套接字自己是有一套心跳保活机制的,不过默认的设置并不像咱们一厢情愿的那样有效。在双方TCP套接字创建链接后(即都进入ESTABLISHED状态)而且在两个小时左右上层没有任何数据传输的状况下,这套机制才会被激活。
不少人认为两个小时的时间设置得很不合理。为何不设置成为10分钟,或者更短的时间?(能够经过SO_KEEPALIVE选项设置。)可是这样作其实并不被推荐。实际上这套机制只是操做系统底层使用的一个被动机制,原理上不该该被上层应用层使用。当系统关闭一个由KEEPALIVE机制检查出来的死链接时,是不会主动通知上层应用的,只有在调用相应的IO操做在返回值中检查出来。
在《UNIX网络编程第1卷》中也有详细的阐述:
SO_KEEPALIVE 保持链接检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP链接的输入。设置该选项后,若是2小时内在此套接口的任一方向都没有数据交换,TCP就自动给对方 发一个保持存活探测分节(keepalive probe)。这是一个对方必须响应的TCP分节.它会致使如下三种状况:对方接收一切正常:以指望的ACK响应。2小时后,TCP将发出另外一个探测分节。对方已崩溃且已从新启动:以RST响应。套接口的待处理错误被置为ECONNRESET,套接口自己则被关闭。对方无任何响应:源自berkeley的TCP发送另外8个探测分节,相隔75秒一个,试图获得一个响应。在发出第一个探测分节11分钟 15秒后若仍无响应就放弃。套接口的待处理错误被置为ETIMEOUT,套接口自己则被关闭。如ICMP错误是“host unreachable(主机不可达)”,说明对方主机并无崩溃,可是不可达,这种状况下待处理错误被置为 EHOSTUNREACH。
所以,忘记SO_KEEPALIVE,在应用层本身写一套保活机制比较靠谱。
Socket中操做该属性的方法以下:
boolean getKeepAlive() 测试是否启用 SO_KEEPALIVE。 void setKeepAlive(boolean on) 启用/禁用 SO_KEEPALIVE。 |
是否重用处于TIME_WAIT状态的地址。默认为false。
Socket中操做该属性的方法以下:
boolean getReuseAddress() 测试是否启用 SO_REUSEADDR。 void setReuseAddress(boolean on) 启用/禁用 SO_REUSEADDR 套接字选项。 |