`

通讯系统经验谈TCP连接状态分析:SYNC_RECV,CLOSE_WAIT,TIME_WAIT

 
阅读更多

转:http://maoyidao.iteye.com/blog/1744277

 

面试时看到应聘者简历中写精通网络,TCP编程,我常问一个问题,TCP建立连接需要几次握手?95%以上的应聘者都能答对是3次。问TCP断开连接需要几次握手,70%的应聘者能答对是4次通讯。再问CLOSE_WAIT,TIME_WAIT是什么状态,怎么产生的,对服务有什么影响,如何消除?有一部分同学就回答不上来。不是我扣细节,而是在通讯为主的前端服务器上,必须有能力处理各种TCP状态。比如统计在本厂的一台前端机上高峰时间TCP连接的情况,统计命令:

 

 

Linux shell代码  收藏代码
  1. netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'  

 

 

结果:

 

除了ESTABLISHED,可以看到连接数比较多的几个状态是:FIN_WAIT1, TIME_WAIT, CLOSE_WAIT, SYN_RECV和LAST_ACK;下面的文章就这几个状态的产生条件、对系统的影响以及处理方式进行简单描述。

 

TCP状态

TCP状态如下图所示:

可能有点眼花缭乱?再看看这个时序图


 

下面看下大家一般比较关心的三种TCP状态

SYN_RECV 

服务端收到建立连接的SYN没有收到ACK包的时候处在SYN_RECV状态。有两个相关系统配置:

 

1,net.ipv4.tcp_synack_retries :INTEGER

默认值是5

对于远端的连接请求SYN,内核会发送SYN + ACK数据报,以确认收到上一个 SYN连接请求包。这是所谓的三次握手( threeway handshake)机制的第二个步骤。这里决定内核在放弃连接之前所送出的 SYN+ACK 数目。不应该大于255,默认值是5,对应于180秒左右时间。通常我们不对这个值进行修改,因为我们希望TCP连接不要因为偶尔的丢包而无法建立。

2,net.ipv4.tcp_syncookies

一般服务器都会设置net.ipv4.tcp_syncookies=1来防止SYN Flood攻击。假设一个用户向服务器发送了SYN报文后突然死机或掉线,那么服务器在发出SYN+ACK应答报文后是无法收到客户端的ACK报文的(第三次握手无法完成),这种情况下服务器端一般会重试(再次发送SYN+ACK给客户端)并等待一段时间后丢弃这个未完成的连接,这段时间的长度我们称为SYN Timeout,一般来说这个时间是分钟的数量级(大约为30秒-2分钟)。

 

这些处在SYNC_RECV的TCP连接称为半连接,并存储在内核的半连接队列中,在内核收到对端发送的ack包时会查找半连接队列,并将符合的requst_sock信息存储到完成三次握手的连接的队列中,然后删除此半连接。大量SYNC_RECV的TCP连接会导致半连接队列溢出,这样后续的连接建立请求会被内核直接丢弃,这就是SYN Flood攻击。

 

 

 

 

能够有效防范SYN Flood攻击的手段之一,就是SYN Cookie。SYN Cookie原理由D. J. Bernstain和 Eric Schenk发明。SYN Cookie是对TCP服务器端的三次握手协议作一些修改,专门用来防范SYN Flood攻击的一种手段。它的原理是,在TCP服务器收到TCP SYN包并返回TCP SYN+ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。在收到TCP ACK包时,TCP服务器在根据那个cookie值检查这个TCP ACK包的合法性。如果合法,再分配专门的数据区进行处理未来的TCP连接。

 

观测服务上SYN_RECV连接个数为:7314,对于一个高并发连接的通讯服务器,这个数字比较正常。

CLOSE_WAIT

发起TCP连接关闭的一方称为client,被动关闭的一方称为server。被动关闭的server收到FIN后,但未发出ACK的TCP状态是CLOSE_WAIT。出现这种状况一般都是由于server端代码的问题,如果你的服务器上出现大量CLOSE_WAIT,应该要考虑检查代码。

TIME_WAIT

根据TCP协议定义的3次握手断开连接规定,发起socket主动关闭的一方 socket将进入TIME_WAIT状态。TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),在Windows下默认为4分钟,即240秒。TIME_WAIT状态下的socket不能被回收使用. 具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的socket, 甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的socket,停止服务。

 

为什么需要TIME_WAIT?TIME_WAIT是TCP协议用以保证被重新分配的socket不会受到之前残留的延迟重发报文影响的机制,是必要的逻辑保证。

 

和TIME_WAIT状态有关的系统参数有一般由3个,本厂设置如下:

net.ipv4.tcp_tw_recycle = 1

net.ipv4.tcp_tw_reuse = 1

net.ipv4.tcp_fin_timeout = 30

 

net.ipv4.tcp_fin_timeout,默认60s,减小fin_timeout,减少TIME_WAIT连接数量。

 

net.ipv4.tcp_tw_reuse = 1表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;

net.ipv4.tcp_tw_recycle = 1表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。

 

(二)

 

Linux系统资源限制

1. 最大文件数

查看进程允许打开的最大文件句柄数:ulimit -n

查看进程所占的文件描述符: lsof -p xxx | wc -l

设置进程能打开的最大文件句柄数:ulimit -n xxx 

 

2. ulimit -n vs. file-max ?

简单的说, ulimit -n控制进程级别能够打开的文件句柄的数量, 而max-file表示系统级别的能够打开的文件句柄的数量。 

 

ulimit -n的设置在重启机器后会丢失,因此需要修改limits.conf的限制,limits.conf中有两个值soft和hard,soft代表只警告,hard代表真正的限制

 

Cat /etc/security/limits.conf代码  收藏代码
  1. *               soft    nofile          150000  
  2. *               hard    nofile          150000  

这里我们把soft和hard设置成一样的。 

 

 

“cat /proc/sys/fs/file-max”,或“sysctl -a | grep fs.file-max”查看系统能打开的最大文件数。查看和设置例如:

 

Shell代码  收藏代码
  1. [root@vm014601 ~]# sysctl -a |grep fs.file-max  
  2. fs.file-max = 200592  
  3. [root@vm014601 ~]# echo "fs.file-max = 2005920" >> /etc/sysctl.conf   
  4. [root@vm014601 ~]# sysctl -p  
  5. [root@vm014601 ~]# cat /proc/sys/fs/file-max                          
  6. 2005920  

file-nr是只读文件,第一个数代表了目前分配的文件句柄数;第二个数代表了系统分配的最大文件句柄数;比如线上系统查看结果:

 

Shell代码  收藏代码
  1. # cat /proc/sys/fs/file-max  
  2. 1106537  
  3. # cat /proc/sys/fs/file-nr       
  4. 1088  0       1106537  
  5. # lsof | wc -l  
  6. 1506  

可以看到file-nr和lsof的值不是很一致,但是数量级一致。为什么会不一致?原因如下:

 

 写道
lsof是列出系统所占用的资源,但是这些资源不一定会占用打开文件号的.
比如共享内存,信号量,消息队列,内存映射.等,虽然占用了这些资源,但不占用打开文件号.

 

我曾经在前端机上很长时间都无法得到lsof | wc -l 的结果,这个时候可以通过file-nr粗略的估算一下打开的文件句柄数。

3. sysckernel.threads-max

指定了内核所能使用的线程(所有进程打开线程之和)的最大数目,通过命令 “cat /proc/sys/kernel/threads-max” 查看当前值。查看和设置例如:

 

Shell代码  收藏代码
  1. sysctl -a | grep threads  
  2. vm.nr_pdflush_threads = 2  
  3. kernel.threads-max = 229376  

 

本厂系统配置允许打开的线程数 > 229k

 

如果此值设小了会导致:-bash: fork: Resource temporarily unavailable

 

4. 为什么有限制?

为什么Linux内核对文件句柄数、线程和进程的最大打开数进行了限制?以及如果我们把它调的太大,会产生什么样的后果?

 

原因1 - 资源问题:the operating system needs memory to manage each open file, and memory is a limited resource - especially on embedded systems.

原因2 - 安全问题:if there were no limits, a userland software would be able to create files endlessly until the server goes down.

 

最主要的是资源问题,为防止某一单一进程打开过多文件描述符而耗尽系统资源,对进程打开文件数做了限制。

 

5. 设置成多少比较合适?

网上有朋友给了估算公式:file-max number = RAM size/10k;

I am not a kernel expert, but as far as I can see, the default for file-max seems to be RAM size divided by 10k. As the real memory used per file handler should be much smaller (size of struct file plus some driver dependent memory), this seems a quite conservative limit. – jofel Apr 19 at 16:43

 

那么一个12G RAM 的前端机可以打开接近1M的文件。真的可以打开1百万个文件吗?

为了试验,基于MINA写了一个NIO服务,在一个服务上创建了50万活跃率约为1%的TCP连接。观察内存使用 < 4.5G,可以粗略认为单个socket连接使用内存小于10K。因此可以用上面的公式来简单估算。

 

6. Orphan socket

不属于任何进程的socket叫orphan socket。这里顺便一下讨论orphan socket,因为很多网络资源不足导致的错误都和“孤儿socket”有关。

 

6.1 Orphan socket是怎么产生的呢?

网上没有明确的说明,我们做一个线上调查:

 

Shell代码  收藏代码
  1. [maoyidao@03701 ~]# netstat -nap | awk '/^tcp/  {++S[$NF]} END {for(a in S) print a, S[a]}'  
  2. 2976/sshd 1  
  3. 11065/gearman 1  
  4. - 2166  
  5. 32726/java 31455  
  6. 25554/scribed 4  
  7.   
  8. [maoyidao@03701 ~]# netstat -nap | awk '/^tcp/ {if($NF == "-") {++S[$6]}}  END {for(a in S) print a, S[a]}'  
  9. TIME_WAIT 451  
  10. FIN_WAIT1 655  
  11. ESTABLISHED 118  
  12. FIN_WAIT2 102  
  13. SYN_RECV 249  
  14. CLOSING 2  
  15. LAST_ACK 619  

 

可以看到任何一个TCP stat情况下都有可能产生“orphan socket”,但多数是在建立过程当中,以及断开连接中的socket。

 

可以通过以下参数减少orphan socket的产生。

sysctl -a | grep orphan

net.ipv4.tcp_orphan_retries = 0

net.ipv4.tcp_max_orphans = 65536

 

6.2 Orphan socket要消耗多少系统资源?

网上的说法是一个orphan socket需要占用64K内存,查到该说法的出路来自这里:http://www.kernel.org/doc/man-pages/online/pages/man7/tcp.7.html

tcp_max_orphans (integer; default: see below; since Linux 2.4)

              The maximum number of orphaned (not attached to any user file handle)

              TCP sockets allowed in the system.  When this number is exceeded, the

              orphaned connection is reset and a warning is printed.  This limit

              exists only to prevent simple denial-of-service attacks.  Lowering this

              limit is not recommended.  Network conditions might require you to

              increase the number of orphans allowed, but note that each orphan can

              eat up to ~64K of unswappable memory.  The default initial value is set

              equal to the kernel parameter NR_FILE.  This initial default is

              adjusted depending on the memory in the system.

 

难道Orphan Socket占用的资源比普通socket多?maoyidao还没有来得及试验,但从理论上讲,占用资源多少和是否是orphan socket是无关的。

 

7. Out of socket memory

谈到Orphan Socket就需要提一下“Out of socket memory”错误。网上已有很清晰的说明:http://jaseywang.me/2012/05/09/%E5%85%B3%E4%BA%8E-out-of-socket-memory-%E7%9A%84%E8%A7%A3%E9%87%8A-2/

摘录一段:

cat /proc/sys/net/ipv4/tcp_mem

69618   92825   139236

第一个数字表示,当 tcp 使用的 page 少于 69618 时,kernel 不对其进行任何的干预

第二个数字表示,当 tcp 使用了超过 92825 的 pages 时,kernel 会进入 “memory pressure”

第三个数字表示,当 tcp 使用的 pages 超过 139236 时,我们就会看到题目中显示的信息

 

maoyidao注:tcp_mem里的数字是page数,转换成字节需要看下系统的page size有多大:

getconf PAGESIZE

4096

 

看看本厂的设置:

cat /proc/sys/net/ipv4/tcp_mem

196608  262144  393216

 

可以看到我们设置的比较大,这是针对前端机的一些优化。再看一下系统当前的情况

[maoyidao@03701 ~]# cat /proc/net/sockstat

sockets: used 30755

TCP: inuse 31684 orphan 1166 tw 600 alloc 31866 mem 5838

UDP: inuse 4 mem 0

RAW: inuse 0

FRAG: inuse 0 memory 0

 

(三)

 

 

上篇介绍了和系统设置有关的几个参数,在一台系统上,连接到一个服务时的本地端口是有限的。由于端口是16位整数,也就只能是0到65535,而0到1023是预留端口,所以能分配的只是 1024到65534,也就是64511个。也就是说,一台机器只能创建六万多个长连接。查看本机端口设置:

maoyidao 写道
sysctl -a | grep port_range
net.ipv4.ip_local_port_range = 1024 65000

/etc/sysctl.conf配置

1. syncookies

net.ipv4.tcp_syncookies = 1

 防止 SYN Flooding攻击,详见:http://maoyidao.iteye.com/admin/blogs/1744277

2. syn-ack

#net.ipv4.tcp_synack_retries=
# net.ipv4.tcp_syn_retries=

maoyidao注:syn-ack握手状态重试次数,默认5,网上有朋友说可改为1或2;通常我的建议是如果没有试验证明在特定场景下明显优势,不要修改默认值,因此本场此参数未指定

3. 内核消息/内存管理设置

kernel.msgmnb = 65536

kernel.msgmax = 65536

 

指定内核中消息队列中消息的最大值(msgmax=64k),default:8192

所有在消息队列中的消息总和的最大值(msgmnb=64k),默认值:16384

即从一个进程发送到另一个进程的消息的最大长度(bytes),进程间的消息传递是在内核的内存中进行的,不会交换到磁盘上,所以如果增加该值,则将增加操作系统所使用的内存数量。

 

maoyidao注:增大消息队列内存值,提高IPC效率

 

kernel.shmmax = 68719476736

Shmmax 是核心参数中最重要的参数之一,用于定义单个共享内存段的最大值,shmmax 设置应该足够大,能在一个共享内存段下容纳下整个的SGA ,设置的过低可能会导致需要创建多个共享内存段,这样可能导致系统性能的下降 。32bit系统中可以设置为系统可支撑的最大值:4G;本系统中设置为64G(这个值实际上超过了前端机的最大内存12G),内存型服务器,比如96G,或128G服务器应该设置的更大。

 

kernel.shmall = 4294967296

含义:系统中共享内存页总

getconf PAGESIZE

4096

total bytes = 4294967296 * 4096 = 16G

4. 网络设置

# 三个整数的向量: min, default, max

net.ipv4.tcp_rmem = 8192 87380 8738000

# min = 4k (same as pagesize), default = 64k, max = 6.25m

net.ipv4.tcp_wmem = 4096 65536 6553600

#

#

net.core.rmem_max = 16777216

net.core.wmem_max = 16777216

 

以上参数的配置可以参考:

 

http://fasterdata.es.net/host-tuning/linux/ 写道
# increase TCP max buffer size settable using setsockopt()
net.core.rmem_max = 16777216 
net.core.wmem_max = 16777216 
# increase Linux autotuning TCP buffer limit 
net.ipv4.tcp_rmem = 4096 87380 16777216
net.ipv4.tcp_wmem = 4096 65536 16777216

 

# 安全策略

net.ipv4.conf.all.accept_redirects = 0

net.ipv4.conf.default.accept_redirects = 0

net.ipv4.conf.all.send_redirects = 0

net.ipv4.conf.default.send_redirects = 0

net.ipv4.conf.default.arp_ignore = 1

net.ipv4.conf.default.arp_announce = 2

net.ipv4.conf.all.arp_ignore = 1

net.ipv4.conf.all.arp_announce = 2

net.ipv4.ip_no_pmtu_disc = 1

 

# 含义:当启用keepalive的时候,TCP发送keepalive消息的频度

net.ipv4.tcp_keepalive_time = 1800

 

# Controls IP packet forwarding

# 作为前端服务器,禁制IP转发,这个没什么好说的;

net.ipv4.ip_forward = 0

 

# Controls source route verification

# 开启反向路径过滤

net.ipv4.conf.default.rp_filter = 1

 

# 含义:记录的那些尚未收到客户端确认信息的链接请求的最大值。 默认值为4096 如果设置这个值大于4096最好同时调

# 整include/net/tcp.h中的TCP_SYNQ_HSIZE,保证TCP_SYNQ_HSIZE*16 小于或等于tcp_max_syn_backlo,

# 然后重新编译内核。

net.ipv4.tcp_max_syn_backlog = 8192

分享到:
评论

相关推荐

    CLOSE_WAIT错误详解

    在TCP/IP协议栈中,CLOSE_WAIT是一个非常关键的连接状态,它涉及到客户端和服务器之间的通信。这个状态在处理网络连接时可能出现的问题时尤其重要。本文将深入探讨CLOSE_WAIT错误的含义、原因以及如何解决。 首先,...

    关于网站的SYN_RECV攻击的防范措施.txt

    SYN_RECV状态表示TCP连接建立过程中的一个阶段,当客户端向服务器发起连接请求时,会发送一个SYN数据包,服务器接收到该数据包后,会进入SYN_RECV状态,并响应一个SYN+ACK数据包。正常情况下,客户端会回应一个ACK...

    tcp通讯协议握手分析.zip_Linux tcp通讯详细介绍_tcp 握手 源码_tcp 通讯_握手_通讯握手协议

    这个过程涉及到TCP套接字状态的多次变迁,例如从FIN_WAIT1到TIME_WAIT,再到CLOSED。 五、安全性与优化 在实际应用中,TCP连接可能会受到各种攻击,如SYN洪泛攻击。为了防止这些攻击,Linux内核实现了SYN cookies...

    socket_recv函数使用心得.

    * SO_DEBUG:只能对 TCP 套接字使用,设置该选项后系统将保存TCP 发送和接收的所有数据相关信息。 * SO_REUSEADDR:绑定套接字用到。套接字正常关闭或异常退出后的一段时间内,端口依然维持原来的绑定状态,设置此...

    _storage_emulated_0_android_data_com.tencent.mobileqq_Tencent_QQfile_recv_实训四.zip

    - `_Tencent_QQfile_recv_`:这部分揭示了文件的子目录,"QQfile_recv" 指示这是QQ应用用来接收文件的特定存储区域。 5. **文件夹内容**: - "实训四":这可能是一个文件夹名称,可能包含用户在某个实训活动中...

    tcp_send_and_recv_files.zip_QT_QT TCP_QT-TCP_Qt 远程_spin5wd

    7. **事件驱动编程**:Qt采用信号和槽机制进行事件处理,当TCP连接状态改变、数据到达或发送完成时,可以触发相应的信号,通过连接这些信号到对应的槽函数,实现异步通信。 8. **错误处理**:在实际的网络编程中,...

    TIME_WAIT.rar_C-means_linux 网络状态_linux c wait_tcp_unix 网络编程

    在Linux系统中,进行网络编程时,TCP连接的TIME_WAIT状态是至关重要的一个环节。TIME_WAIT状态是TCP连接生命周期中的最后一个阶段,对于理解和优化网络应用性能有着直接的影响。本资源"TIME_WAIT.rar"包含了关于这个...

    sama5d3x-ek: rs485_recv_send.c

    针对爱特梅尔的SAMA5D3X-EK评估版,RS485串口操作例子。

    poco_recv_mail_test.zip

    在本文中,我们将详细探讨Poco邮件库如何用于接收邮件,以及在`poco_recv_mail_test1.cpp`、`poco_recv_mail_test2.cpp`和`poco_recv_mail_test3.cpp`这些文件中可能涉及的关键知识点。 1. Poco库介绍: Poco库是...

    com_recv_send.zip_com_recv

    而在Linux或Unix系统中,通常会使用`open`、`write`、`read`等系统调用。 对于C/C++开发,可以使用开源库如libserialport(由libudev提供支持)、SerialPort类库等。在Python中,可以使用pySerial库来方便地操作...

    obex_recv_file.zip

    通过分析"obex_recv_file"源码,我们可以了解到Linux下蓝牙文件接收的具体实现细节。这不仅有助于理解OBEX协议的工作原理,也为开发者提供了一个实际的示例,帮助他们在自己的项目中实现类似功能。 在实际应用中,...

    两台设备的TCP通讯_Codesys的两台设备之前的TCP通讯_

    4. 关闭连接:当通讯完成时,使用`TCP_CLOSE`函数关闭TCP连接,释放系统资源。 在实际应用中,你可能还需要处理错误情况,例如连接失败、数据传输错误或超时。为了确保通讯的可靠性,可以使用心跳机制定期发送小量...

    TCP_Connection_Status.zip

    在实际应用中,结合这两个文件,IT管理员可以定期检查服务器的TCP连接情况,例如检测是否有大量半开连接(SYN_SENT或SYN_RECV)、是否存在长时间未关闭的连接(FIN_WAIT或CLOSE_WAIT),或者是否因为连接资源耗尽而...

    serial_recv.rar_serial recv

    《串口接收程序serial_recv详解》 在计算机通信领域,串口通信是一种常见的数据传输方式,尤其在嵌入式系统和工业控制中扮演着重要角色。本文将详细讲解一款名为"serial_recv"的串口接收工具,该工具主要用于调试...

    epoll机制epoll_create、epoll_ctl、epoll_wait、close(在epoll的ET模式下,read和write或send和recv当返回值0且errno=EAGAIN - linking530的专栏 - CSDN博客.mht

    epoll机制epoll_create、epoll_ctl、epoll_wait、close(在epoll的ET模式下,read和write或send和recv当返回值0且errno=EAGAIN - linking530的专栏 - CSDN博客.mht

    python网络编程调用recv函数完整接收数据的三种方法

    ### Python网络编程调用recv函数完整接收数据的三种方法 在网络编程中,特别是TCP通信中,确保能够完整地接收来自远程主机的数据是非常重要的。Python的`socket`库提供了`recv()`函数来接收数据,但在实际应用中...

    ir_recv.rar_IR_IR mtk_MTK IR_ir module_ir receive

    标题中的"ir_recv.rar_IR_IR mtk_MTK IR_ir module_ir receive"暗示了这是一个与红外(IR)接收相关的软件资源,特别针对MTK(MediaTek)平台。MTK是一家知名的半导体公司,其芯片广泛应用于手机、电视和其他电子...

    usbip_common.rar_USBIP_V2

    usbip status string for Linux v2.13.6.

    Netstat命令详解如何关闭TIME_WAIT连接如何查看nginx的访问流量[归类].pdf

    5. FIN_WAIT1:主动关闭(active close)端应用程序调用 close,于是其 TCP发出 FIN 请求主动关闭连接,之后进入 FIN_WAIT1 状态。 6. CLOSE_WAIT:被动关闭(passive close)端 TCP 接到 FIN。 7. FIN_WAIT2:主动...

Global site tag (gtag.js) - Google Analytics