最近在新的平台上测试程序,之前一些没有注意到的问题都成为了性能瓶颈,经过设置一些TCP/IP选项可以解决一部分问题,固然根本的解决方法是重构代码,从新设计服务器框架。先列出几个TCP/IP选项:
选项
man 7 socket:
SO_REUSEADDR
SO_RECVBUF/SO_SNDBUF
SO_KEEPALIVE
SO_LINGER
man 7 tcp:
TCP_CORK
TCP_NODELAY
TCP_DEFER_ACCEPT
TCP_KEEPCNT/TCP_KEEPIDLE/TCP_KEEPINTVL
SO_REUSEADDRphp
man 命令的 领域 名称 说明
1 用户命令, 可由任何人启动的。
2 系统调用, 即由内核提供的函数。
3 例程, 即库函数。
4 设备, 即/dev目录下的特殊文件。
5 文件格式描述, 例如/etc/passwd。
6 游戏, 不用解释啦!
7 杂项, 例如宏命令包、惯例等。
8 系统管理员工具, 只能由root启动。
9 其余(Linux特定的), 用来存放内核例行程序的文档。html
SO_REUSEADDR选项:node
在服务器程序中,SO_REUSEADDR socket选项一般在调用bind()以前被设置。
SO_REUSEADDR能够用在如下四种状况下:
(摘自《Unix网络编程》卷一,即UNPv1)
一、当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。
二、SO_REUSEADDR容许同一port上启动同一服务器的多个实例(多个进程)。但每一个实例绑定的IP地址是不能相同的。在有多块网卡或用IP Alias技术的机器能够测试这种状况。
三、SO_REUSEADDR容许单个进程绑定相同的端口到多个socket上,但每一个socket绑定的ip地址不一样。这和2很类似,区别请看UNPv1。
四、SO_REUSEADDR容许彻底相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。linux
TCP_NODELAY/TCP_CHORK选项:
TCP_NODELAY/TCP_CHORK
TCP_NODELAY和TCP_CORK基本上控制了包的“Nagle化”,Nagle化在这里的含义是采用Nagle算法把较小的包组装为更大的帧。TCP_NODELAY和TCP_CORK都禁掉了Nagle算法,只不过他们的行为不一样而已。
TCP_NODELAY 不使用Nagle算法,不会将小包进行拼接成大包再进行发送,直接将小包发送出去,会使得小包时候用户体验很是好。算法
Nagle算法参见本身的博客:http://blog.163.com/xychenbaihu@yeah/blog/static/132229655201231214038740/
当在传送大量数据的时候,为了提升TCP发送效率,能够设置TCP_CORK,CORK顾名思义,就是"塞子"的意思,它会尽可能在每次发送最大的数据量。当设置了TCP_CORK后,会有阻塞200ms,当阻塞时间事后,数据就会自动传送。
详细的资料能够查看参考文献5。编程
SO_LINGER选项:
SO_LINGER
linger,顾名思义是延迟延缓的意思,这里是延缓面向链接的socket的close操做。服务器
默认,close当即返回,可是当发送缓冲区中还有一部分数据的时候,系统将会尝试将数据发送给对端。SO_LINGER能够改变close的行为。网络
控制SO_LINGER经过下面一个结构:
struct linger
{
int l_onoff; /*0=off, nonzero=on*/
int l_linger; /*linger time, POSIX specifies units as seconds*/
};
经过结构体中成员的不一样赋值,能够表现为下面几种状况:
1. l_onoff设置为0,选项被关闭。l_linger值被忽略,就是上面的默认情形,close当即返回。
2. l_onoff设置为非0,l_linger被设置为0,则close()不被阻塞当即执行,丢弃socket发送缓冲区中的数据,并向对端发送一个RST报文。并发
这种关闭方式称为“强制”或“失效”关闭。
3. l_onoff设置为非0,l_linger被设置为非0,则close()调用阻塞进程,直到所剩数据发送完毕或超时。框架
这种关闭称为“优雅的”关闭。
注意:
这个选项须要谨慎使用,尤为是强制式关闭,会丢失服务器发给客户端的最后一部分数据。UNP中:
The TIME_WAIT state is our friend and is there to help us(i.e., to let the old duplicate segments expire in the network).
TCP_DEFER_ACCEPT选项:
TCP_DEFER_ACCEPT
defer accept,从字面上理解是推迟accept,其实是当接收到第一个数据以后,才会建立链接,三次握手完成,链接尚未创建。
对于像HTTP等非交互式的服务器,这个颇有意义,能够用来防护空链接攻击(只是创建链接,可是不发送任何数据)。
使用方法以下:
val = 5; setsockopt(srv_socket->fd, SOL_TCP, TCP_DEFER_ACCEPT, &val, sizeof(val)) ; |
里面 val 的单位是秒,注意若是打开这个功能,kernel 在 val 秒以内尚未收到数据,不会继续唤醒进程,而是直接丢弃链接。若是服务器设置TCP_DEFER_ACCEPT选项后,服务器受到一个CONNECT请求后,三次握手以后,新的socket状态依然为SYN_RECV,而不是ESTABLISHED,操做系统不会Accept。
因为设置TCP_DEFER_ACCEPT选项以后,三次握手后状态没有达到ESTABLISHED,而是SYN_RECV。这个时候,若是客户端一直没有发送"数据"报文,服务器将重传SYN/ACK报文,重传次数受net.ipv4.tcp_synack_retries参数控制,达到重传次数以后,才会再次进行setsockopt中设置的超时值,所以会出现SYN_RECV生存时间比设置值大一些的状况。
关于SYN_RECV状态能够查看参考文献7。
SO_KEEPALIVE选项:
SO_KEEPALIVE/TCP_KEEPCNT/TCP_KEEPIDLE/TCP_KEEPINTVL
若是一方已经关闭或异常终止链接,而另外一方殊不知道,咱们将这样的TCP链接称为半打开的。
TCP经过保活定时器(KeepAlive)来检测半打开链接。
在高并发的网络服务器中,常常会出现漏掉socket的状况,对应的结果有一种状况就是出现大量的CLOSE_WAIT状态的链接。这个时候,能够经过设置KEEPALIVE选项来解决这个问题,固然还有其余的方法能够解决这个问题,详细的状况能够查看参考资料8。
使用方法以下:
//Setting For KeepAlive int keepalive = 1; setsockopt(incomingsock,SOL_SOCKET,SO_KEEPALIVE,(void*)(&keepalive),(socklen_t)sizeof(keepalive)); int keepalive_time = 30; setsockopt(incomingsock, IPPROTO_TCP, TCP_KEEPIDLE,(void*)(&keepalive_time),(socklen_t)sizeof(keepalive_time)); int keepalive_intvl = 3; setsockopt(incomingsock, IPPROTO_TCP, TCP_KEEPINTVL,(void*)(&keepalive_intvl),(socklen_t)sizeof(keepalive_intvl)); int keepalive_probes= 3; setsockopt(incomingsock, IPPROTO_TCP, TCP_KEEPCNT,(void*)(&keepalive_probes),(socklen_t)sizeof(keepalive_probes)); |
设置SO_KEEPALIVE选项来开启KEEPALIVE,而后经过TCP_KEEPIDLE、TCP_KEEPINTVL和TCP_KEEPCNT设置keepalive的开始时间、间隔、次数等参数。
固然,也能够经过设置/proc/sys/net/ipv4/tcp_keepalive_time、tcp_keepalive_intvl和tcp_keepalive_probes等内核参数来达到目的,可是这样的话,会影响全部的socket,所以建议使用setsockopt设置。参考:1. man 7 socket2. man 7 tcp3. 《Unix Network Programming》 Section 7.5 "Generic Socket Options"4. http://www.linuxsir.org/bbs/showthread.php?t=557385. http://blog.chinaunix.net/u/12592/showart_1723934.html6. http://blog.csdn.net/fullsail/archive/2009/08/09/4429102.aspx7. http://www.kernelchina.org/?q=node/1108. http://blog.chinaunix.net/u/12592/showart.php?id=2059174