最近项目需要做单机100万长连接与高并发的服务器,我们开发完服务器以后,通过自己搭的高速压测框架压测服务端的时候,发生了奇怪的现象,就是服务端莫名其妙的少接收了连接,造成了数据包的丢失,通过网上查资料,和自己的实践,下面是我做实验,抓包分析的过程如下:
总共5个连接
其中全连接队列somaxconn参数为1表示监听队列的总长度(实际可以完成somaxconn+1个连接的建立)
半连接队列tcp_max_syn_backlog为1(实际可以将tcp_max_syn_backlog+1个syn分节放入其中)
当全连接队列已满且半连接队列未满的情况下:
当客户端发起一个syn分节时,服务端不会丢弃该syn分节,而是直接响应ack和syn,这时客户端响应ack,并成为established状态,而服务端收到ack响应后,试图将该syn分节从半连接队列中移除,并加入全链接队列,然后由于全连接队列已经满了,这时,在默认情况下,服务端啥也不做,而且不会将该连接由SYN_RECV变成ESTABLISHED,服务端仅仅只是创建一个定时器,以固定间隔重传syn和ack到服务端,直到到达系统默认的synack重传阖值,然后服务端将处于半连接队列里面的syn分节丢弃,此时服务端只剩2个连接。这个时候客户端一侧的连接仍然有5个established
当全连接已满且半连接队列也满的情况下:
当客户端发起一个syn分节时,服务端发现半连接队列已经满了,同时该syn分节尚未重传过,服务端直接丢弃该syn分节,然后客户端过了4秒重传syn分节,这个时候服务端发现半连接队列已满同时,该syn分节已经重传过了,服务端收下了该syn分节,并响应客户端syn+ack,客户端收到syn+ack后,响应ack。客户端三次握手已经完成,而服务端收到ack之后,发现全连接队列是满的,这个时候不会将该连接从SYN_RECV转换成ESTABLISHED,服务端创建定时器,定时重传syn+ack, 直到到达系统默认的synack重传阖值,然后服务端将处于半连接队列里面的syn分节丢弃
具体如下
使用tcpdump -i eth0 -l -nn -S port 8000 -c 40 -w out.txt
如下所示,测试环境在Linux 2.6.18-164.el5
全连接队列 cat /proc/sys/net/core/somaxconn 1
半连接队列 cat /proc/sys/net/ipv4/tcp_max_syn_backlog 1
监听端口 8000
以客户端建立5条连接为例
1.临时端口号为53991的客户端连接向监听端口为8000的服务端连接发起syn分节,客户端状态变为SYN_SEND状态
服务端收到来自客户端的syn分节后,这个时候由于半连接队列未满,直接将syn分节放入半连接队列,同时向客户端发送了一个ack+syn分节,此时服务端进入SYN_RECV状态
客户端收到服务端发送的syn分节之后,向服务端回传了ack确认分节,客户端进入ESTABLISHED状态
服务端收到来自客户端的ack确认之后,就试图把syn分节从半连接队列中移除,并加入到全连接队列,由于全连接队列未满,此时该syn分节成功转入全连接队列,服务端的状
态从SYN_RECV转换成了ESTABLISHED状态
2.临时端口号为53992的客户端连接和临时端口号为53991的连接同样顺利的完成3次握手
根据以上连接基本可以得出一个结论,能建立起全连接队列长度+1个长连接
3.临时端口号为53993的客户端连接向监听端口为8000的服务端连接发起syn分节,客户端状态变为SYN_SEND状态
服务端收到来自客户端的syn分节后,这个时候由于半连接队列未满,直接将syn分节放入半连接队列,同时向客户端发送一个ack+syn分节,此时服务端进入SYN_RECV状态
客户端收到服务端发送的syn分节之后,向服务端回传了ack确认分节,客户端进入ESTABLISHED状态
服务端收到来自客户端的ack确认之后,就试图把syn分节从半连接队列中移除,并加入全连接队列,由于全连接队列已经满了,此时该syn分节无法转入全连接队列,也就不会
从SYN_RECV转换为ESTABLISHED状态系统默认会把该syn标记为acked模式,并建立定时器,定时从服务端重传syn+ack分节,客户端此时已经处于ESTABLISHED状态,
不过在收到服务端发送的syn+ack分节后,仍然响应服务端ack。服务端继续上面的过程,直到到达synack(cat /proc/sys/net/ipv4/tcp_synack_retries )重传的阖值,然后抛弃
该syn分节
4.临时端口号为53994的客户端连接和临时端口号为53993的连接同样的过程
根据以上连接基本可以得出一个结论,半连接队列能容下半连接队列长度+1个syn分节
5.临时端口号为53995的客户端连接向监听端口为8000的服务端连接发起syn分节,客户端状态变为SYN_SEND状态
服务端收到来自客户端的syn分节后,这个时候半连接队列已经满了,服务端发现该syn分节尚未重传过,默认直接丢弃该syn分节,客户端在超时时间内并未收到来自服务端的
ack响应,大约过了3秒,客户端重传了syn分节,这个时候服务端收到syn分节之后,发现该分节已经收到过,这次服务端收下了syn分节,并向客户端发送syn+ack。客户端
收到来自服务端的ack+syn之后,向服务端发送ack,客户端进入ESTABLISHED状态,服务端收到来自客户端的ack之后,试图把syn分节从半连接队列中移除,并添加到全连
接队列,由于半连接队列已满,加入全连接队列失败,和临时端口号53993以及53994的行为是一样的
1.
20:25:57.637476 IP 192.168.172.128.53991 > 192.168.172.128.8000: S 3828162025:3828162025(0) win 32792 <mss 16396,sackOK,timestamp 250818 0,nop,wscale 6>
20:25:57.637483 IP 192.168.172.128.8000 > 192.168.172.128.53991: S 3826007261:3826007261(0) ack 3828162026 win 32768 <mss 16396,sackOK,timestamp 250818 250818,nop,wscale 6>
20:25:57.637486 IP 192.168.172.128.53991 > 192.168.172.128.8000: . ack 3826007262 win 513 <nop,nop,timestamp 250818 250818>
2.
20:25:58.638894 IP 192.168.172.128.53992> 192.168.172.128.8000: S 3822825008:3822825008(0) win 32792 <mss 16396,sackOK,timestamp 251820 0,nop,wscale 6>
20:25:58.638984 IP 192.168.172.128.8000 > 192.168.172.128.53992: S 3829073969:3829073969(0) ack 3822825009 win 32768 <mss 16396,sackOK,timestamp 251820 251820,nop,wscale 6>
20:25:58.638995 IP 192.168.172.128.53992 > 192.168.172.128.8000: . ack 3829073970 win 513 <nop,nop,timestamp 251820 251820>
3.
20:25:59.640701 IP 192.168.172.128.53993> 192.168.172.128.8000: S 3828629154:3828629154(0) win 32792 <mss 16396,sackOK,timestamp 252821 0,nop,wscale 6>
20:25:59.640713 IP 192.168.172.128.8000 > 192.168.172.128.53993: S 3833556338:3833556338(0) ack 3828629155 win 32768 <mss 16396,sackOK,timestamp 252821 252821,nop,wscale 6>
20:25:59.640719 IP 192.168.172.128.53993 > 192.168.172.128.8000: . ack 3833556339 win 513 <nop,nop,timestamp 252821 252821>
4.
20:26:00.642260 IP 192.168.172.128.53994 > 192.168.172.128.8000: S 3820886884:3820886884(0) win 32792 <mss 16396,sackOK,timestamp 253823 0,nop,wscale 6>
20:26:00.642345 IP 192.168.172.128.8000> 192.168.172.128.53994: S 3831669364:3831669364(0) ack 3820886885 win 32768 <mss 16396,sackOK,timestamp 253823 253823,nop,wscale 6>
20:26:00.642356 IP 192.168.172.128.53994> 192.168.172.128.8000: . ack 3831669365 win 513 <nop,nop,timestamp 253823 253823>
5.
20:26:01.645291 IP 192.168.172.128.53995> 192.168.172.128.8000: S 3824454212:3824454212(0) win 32792 <mss 16396,sackOK,timestamp 254826 0,nop,wscale 6>
3.
20:26:03.040319 IP 192.168.172.128.8000 > 192.168.172.128.53993: S 3833556338:3833556338(0) ack 3828629155 win 32768 <mss 16396,sackOK,timestamp 256221 252821,nop,wscale 6>
20:26:03.040332 IP 192.168.172.128.53993 > 192.168.172.128.8000: . ack 3833556339 win 513 <nop,nop,timestamp 256221 256221,nop,nop,sack 1 {3833556338:3833556339}>
4.
20:26:03.842281 IP 192.168.172.128.8000 > 192.168.172.128.53994: S 3831669364:3831669364(0) ack 3820886885 win 32768 <mss 16396,sackOK,timestamp 257023 253823,nop,wscale 6>
20:26:03.842295 IP 192.168.172.128.53994 > 192.168.172.128.8000: . ack 3831669365 win 513 <nop,nop,timestamp 257023 257023,nop,nop,sack 1 {3831669364:3831669365}>
5.
20:26:04.645946 IP 192.168.172.128.53995 > 192.168.172.128.8000: S 3824454212:3824454212(0) win 32792 <mss 16396,sackOK,timestamp 257826 0,nop,wscale 6>
20:26:04.645962 IP 192.168.172.128.8000 > 192.168.172.128.53995: S 3832652478:3832652478(0) ack 3824454213 win 32768 <mss 16396,sackOK,timestamp 257826 257826,nop,wscale 6>
20:26:04.645971 IP 192.168.172.128.53995 > 192.168.172.128.8000: . ack 3832652479 win 513 <nop,nop,timestamp 257826 257826>
20:26:08.846521 IP 192.168.172.128.8000 > 192.168.172.128.53995: S 3832652478:3832652478(0) ack 3824454213 win 32768 <mss 16396,sackOK,timestamp 262026 257826,nop,wscale 6>
20:26:08.846534 IP 192.168.172.128.53995 > 192.168.172.128.8000: . ack 3832652479 win 513 <nop,nop,timestamp 262026 262026,nop,nop,sack 1 {3832652478:3832652479}>
3.
20:26:09.246438 IP 192.168.172.128.8000 > 192.168.172.128.53993: S 3833556338:3833556338(0) ack 3828629155 win 32768 <mss 16396,sackOK,timestamp 262426 256221,nop,wscale 6>
20:26:09.246452 IP 192.168.172.128.53993 > 192.168.172.128.8000: . ack 3833556339 win 513 <nop,nop,timestamp 262426 262426,nop,nop,sack 1 {3833556338:3833556339}>
4.
20:26:09.853232 IP 192.168.172.128.8000 > 192.168.172.128.53994: S 3831669364:3831669364(0) ack 3820886885 win 32768 <mss 16396,sackOK,timestamp 263026 257023,nop,wscale 6>
20:26:09.853246 IP 192.168.172.128.53994 > 192.168.172.128.8000: . ack 3831669365 win 513 <nop,nop,timestamp 263026 263026,nop,nop,sack 1 {3831669364:3831669365}>
5.
20:26:15.055318 IP 192.168.172.128.8000 > 192.168.172.128.53995: S 3832652478:3832652478(0) ack 3824454213 win 32768 <mss 16396,sackOK,timestamp 268228 262026,nop,wscale 6>
20:26:15.055335 IP 192.168.172.128.53995 > 192.168.172.128.8000: . ack 3832652479 win 513 <nop,nop,timestamp 268228 268228,nop,nop,sack 1 {3832652478:3832652479}>
3.
20:26:21.260288 IP 192.168.172.128.8000 > 192.168.172.128.53993: S 3833556338:3833556338(0) ack 3828629155 win 32768 <mss 16396,sackOK,timestamp 274430 262426,nop,wscale 6>
20:26:21.260308 IP 192.168.172.128.53993 > 192.168.172.128.8000: . ack 3833556339 win 513 <nop,nop,timestamp 274430 274430,nop,nop,sack 1 {3833556338:3833556339}>