Linux下高性能网络编程中的几个TCP/IP选项

转自:http://blog.chinaunix.net/u/12592/showart.php?id=2064847
最近在新的平台上测试程序,以前一些没有注意到的问题都成为了性能瓶颈,通过设置一些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_REUSEADDR
在服务器程序中,SO_REUSEADDR socket选项通常在调用bind()之前被设置。
SO_REUSEADDR可以用在以下四种情况下。
(摘自《Unix网络编程》卷一,即UNPv1)
1、当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。
2、SO_REUSEADDR允许同一port上启动同一服务器的多个实例(多个进程)。但每个实例绑定的IP地址是不能相同的。在有多块网卡或用IP Alias技术的机器可以测试这种情况。
3、SO_REUSEADDR允许单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。这和2很相似,区别请看UNPv1。
4、SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。

TCP_NODELAY/TCP_CHORK
TCP_NODELAY和TCP_CORK基本上控制了包的“Nagle化”,Nagle化在这里的含义是采用Nagle算法把较小的包组装为更大的帧。TCP_NODELAY和TCP_CORK都禁掉了Nagle算法,只不过他们的行为不同而已。
TCP_NODELAY 不使用Nagle算法,不会将小包进行拼接成大包再进行发送,直接将小包发送出去,会使得小包时候用户体验非常好。
当在传送大量数据的时候,为了提高TCP发送效率,可以设置TCP_CORK,CORK顾名思义,就是"塞子"的意思,它会尽量在每次发送最大的数据量。当设置了TCP_CORK后,会有阻塞200ms,当阻塞时间过后,数据就会自动传送。

详细的资料可以查看参考文献5。

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
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/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 socket
2. man 7 tcp
3. 《Unix Network Programming》 Section 7.5 "Generic Socket Options"
4. http://www.linuxsir.org/bbs/showthread.php?t=55738
5. http://blog.chinaunix.net/u/12592/showart_1723934.html
6. http://blog.csdn.net/fullsail/archive/2009/08/09/4429102.aspx
7. http://www.kernelchina.org/?q=node/110
8. http://blog.chinaunix.net/u/12592/showart.php?id=2059174

Linux下高性能网络编程中的几个TCP/IP选项相关推荐

  1. Linux下高性能网络编程中的几个TCP/IP选项_SO_REUSEADDR、SO_RECVBUF、SO_SNDBUF、SO_KEEPALIVE、SO_LINGER、TCP_CORK、TCP_NODE

    最近在新的平台上测试程序,以前一些没有注意到的问题都成为了性能瓶颈,通过设置一些TCP/IP选项能够解决一部分问题,当然根本的解决方法是重构代码,重新设计服务器框架.先列出几个TCP/IP选项: 选项 ...

  2. linux下socket编程中setsockopt的作用

    如题所示,在linux进行socket编程的时候,一般而言,socket,bind,listen三步曲之后,就开始接收客户端请求,然后实现收发数据. 如下所示的代码,是没有setsockopt的情况: ...

  3. Linux下Python3编程中shebang的正确设置

    Linux下一般情况下输入python或者python2会出现python2.7的IDLE,输入python3会出现python3.x的交互式界面,这些都是以软链接的形式存在于PATH中的,但是我们的 ...

  4. Linux下多线程编程中信号量介绍及简单使用

    在Linux中有两种方法用于处理线程同步:信号量和互斥量. 线程的信号量是一种特殊的变量,它可以被增加或减少,但对其的关键访问被保证是原子操作.如果一个程序中有多个线程试图改变一个信号量的值,系统将保 ...

  5. linux中快捷脚本,Linux下Shell编程快捷键大全(日常整理)

    有时候,我们需要在 Bash 中重复执行先前的命令.你当然可以使用上方向键来查看之前曾经运行过的命令.但这里有一些更好的方式,例如,数量掌握以下的快捷键. 1) !!:重复执行上一条指令 2) !a: ...

  6. Linux下Socket编程

    Linux下Socket编程    网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符.Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的S ...

  7. linux下多进程编程简介

    两年前的文章,拿过来充充门面. ------------------------ linux下多进程编程简介 ( 作者:mikespook | 发布日期:2002-12-8 | 浏览次数:272 ) ...

  8. Linux下网络编程

    Linux下网络编程初步 Linux以其源代码公开闻名于世,并以其稳定性和可靠性雄霸操作系统领域,在网络应用技术方面使用得更加广泛.很久以来它就是Windows的重要对手之一.随着网络时代的来临,Li ...

  9. linux实验串行端口程序设计,Linux下串口编程心得(转)

    最近一段时间,需要完成项目中关于Linux下使用串口的一个部分,现在开帖记录过程点滴. 项目的要求是这样的,Qt应用程序主要完成数据采集和发送功能,一开始在google中海搜关键字"Qt串口 ...

最新文章

  1. 如何显示jQuery中的加载微调器?
  2. [BZOJ4403]序列统计
  3. 每天一道LeetCode-----最长回文子串/序列,从头开始的最长回文子串长度
  4. 加时间水印_如何在手机照片上添加时间和日期?打开这个设置即可添加,超方便...
  5. 吃鸡服务器不接受响应,绝地求生:蓝洞优化服务器性能,从此告别掉帧延迟!...
  6. mysql pid_mysql pid文件是什么用途
  7. 简单 局部 整体光照模型计算机图形学,计算机图形学北大光照模型.ppt
  8. Delphi Access Violation错误的分析
  9. octave深度学习_【深度学习笔记】(一)Octave
  10. 【转】关于Eclipse创建Android项目时,会多出一个appcompat_v7的问题
  11. 大学计算机课程复习--汇编语言
  12. c语言的数学题目及答案,C语言练习题
  13. 【Java代码笔记】
  14. 锁定计算机屏幕的快捷键是什么,锁住电脑屏幕的快捷键_打开电脑屏幕的快捷键...
  15. IPV6IPV4网址
  16. Aho-Corasic多模匹配算法的学习、理解和应用(Python环境下)
  17. Tomcat是一个Servlet容器?
  18. UVA 1471 Defense Lines 单调队列优化
  19. 《Android源码设计模式解析与实战》读书笔记(七)——策略模式
  20. 线段树的进阶:多种信息的维护与传递

热门文章

  1. mysql 5.5 主从双向同步,请教mysql 定时 双向 主从同步問題
  2. C++知识点39——运算符的重载概念与分数类实现(上)
  3. 记录一下水下相机标定
  4. 数据结构排序算法实验报告_数据结构与算法-堆排序
  5. MongoDB 4.0 事务实现解析
  6. 9款Android经常使用的高速开发框架
  7. Docker Container同时启动多服务 supervisor
  8. Mongodb 请求处理流程
  9. 【ASP.NET Web API教程】2.3.3 创建Admin控制器
  10. Ubuntu Server 命令行下的默认语言 中文乱码