浅谈tcp的半打开连接( 二 )

可以看到对于第三个客户端nc,连接状态为ESTABLISHED,表示3次握手已经正确完成 。而对于服务端,当前的连接状态为SYN_RECV,表示半连接状态,因为当前积压队列已经满,没有空间再存放ESTABLISHED连接,所以该连接无法从SYN_RECV状态变为ESTABLISHED状态,虽然能正确接收到nc端的第三个ACK段 。
此时使用tcpdump进行抓包:
zuchunlei@ubuntu14:~$ sudo tcpdump -i any tcp port 10000 -nntcpdump: verbose output suppressed, use -v or -vv for full protocol decodelistening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes20:50:15.739292 IP 127.0.0.1.10000 > 127.0.0.1.59896: Flags [S.], seq 2458870060, ack 3925261891, win 43690, options [mss 65495,sackOK,TS val 1340001 ecr 1339751,nop,wscale 7], length 020:50:15.739301 IP 127.0.0.1.59896 > 127.0.0.1.10000: Flags [.], ack 1, win 342, options [nop,nop,TS val 1340001 ecr 1339751], length 020:50:17.738724 IP 127.0.0.1.10000 > 127.0.0.1.59896: Flags [S.], seq 2458870060, ack 3925261891, win 43690, options [mss 65495,sackOK,TS val 1340501 ecr 1340001,nop,wscale 7], length 020:50:17.738772 IP 127.0.0.1.59896 > 127.0.0.1.10000: Flags [.], ack 1, win 342, options [nop,nop,TS val 1340501 ecr 1339751], length 020:50:21.739110 IP 127.0.0.1.10000 > 127.0.0.1.59896: Flags [S.], seq 2458870060, ack 3925261891, win 43690, options [mss 65495,sackOK,TS val 1341501 ecr 1340501,nop,wscale 7], length 020:50:21.739158 IP 127.0.0.1.59896 > 127.0.0.1.10000: Flags [.], ack 1, win 342, options [nop,nop,TS val 1341501 ecr 1339751], length 020:50:29.738975 IP 127.0.0.1.10000 > 127.0.0.1.59896: Flags [S.], seq 2458870060, ack 3925261891, win 43690, options [mss 65495,sackOK,TS val 1343501 ecr 1341501,nop,wscale 7], length 020:50:29.739022 IP 127.0.0.1.59896 > 127.0.0.1.10000: Flags [.], ack 1, win 342, options [nop,nop,TS val 1343501 ecr 1339751], length 020:50:45.739231 IP 127.0.0.1.10000 > 127.0.0.1.59896: Flags [S.], seq 2458870060, ack 3925261891, win 43690, options [mss 65495,sackOK,TS val 1347501 ecr 1343501,nop,wscale 7], length 020:50:45.739310 IP 127.0.0.1.59896 > 127.0.0.1.10000: Flags [.], ack 1, win 342, options [nop,nop,TS val 1347501 ecr 1339751], length 0对于SYN_RECV状态的连接,linux会启动定时器进行重传三次握手的第二段[S.],在4次重传后,如果当前listen socket已连接队列中依然没有空间,则将SYN_RECV状态的连接丢弃 。
等待4次重传后,使用netstat查看10000端口状态:
Every 1.0s: sudo netstat -tnpoa|sed -n -e 2p -e /10000/pSat Dec 16 20:58:20 2017Proto Recv-Q Send-Q Local AddressForeign AddressStatePID/Program name Timertcp00 0.0.0.0:100000.0.0.0:*LISTEN1578/pythonoff (0.00/0/0)tcp00 127.0.0.1:59890127.0.0.1:10000ESTABLISHED 6301/ncoff (0.00/0/0)tcp00 127.0.0.1:10000127.0.0.1:59890ESTABLISHED -off (0.00/0/0)tcp00 127.0.0.1:59896127.0.0.1:10000ESTABLISHED 15954/ncoff (0.00/0/0)tcp00 127.0.0.1:10000127.0.0.1:59892ESTABLISHED -off (0.00/0/0)tcp00 127.0.0.1:59892127.0.0.1:10000ESTABLISHED 6379/ncoff (0.00/0/0)server端将SYN_RECV状态的连接丢弃后,此时第三个nc客户端连接就已经成为了半打开连接 。
 
对半打开连接进行send/recv操作时的影响:
如果此时,第三个nc客户端发送数据,则因为连接对对端不存在,对端会回复RST段,本端收到RST段后也会将连接重置 。
如果第三个nc客户端只接收数据的话,则这个客户端永远阻塞在recv调用中无法返回 。为了有效解决这种问题,客户端可以启动tcp的keepalive,因为默认tcp发送keepalive probe的间隔时间较长,应用可以通过设置socket option(
TCP_KEEPDILE/TCP_KEEPINTVL/TCP_KEEPCNT)将发送keepalive probe的时间设短些 。
 
今早我测试了一下最新版ubuntu16.04的实现,发现如果listen socket的积压队列满后,新来客户端的连接不再成为ESTABLISHED状态,而是在SYN_SENT状态进行进行SYN段的超时重传,而服务端不返回任何tcp段 。
新版的测试环境:
zuchunlei@box:~$ uname -aLinux box 4.10.0-28-generic #32~16.04.2-Ubuntu SMP Thu Jul 20 10:19:48 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux与之前的测试场景一样,当前只关注第三个nc客户端连接的状态 。
使用netstat查看10000端口的状态:
Every 1.0s: sudo netstat -tnpoa|sed -n -e 2p -e /10000/pSat Dec 16 21:21:57 2017Proto Recv-Q Send-Q Local AddressForeign AddressStatePID/Program name Timertcp00 0.0.0.0:100000.0.0.0:*LISTEN2022/pythonoff (0.00/0/0)tcp00 127.0.0.1:36516127.0.0.1:10000ESTABLISHED 2347/ncoff (0.00/0/0)tcp01 127.0.0.1:36520127.0.0.1:10000SYN_SENT2522/ncon (5.18/3/0)tcp00 127.0.0.1:10000127.0.0.1:36518ESTABLISHED -off (0.00/0/0)tcp00 127.0.0.1:36518127.0.0.1:10000ESTABLISHED 2388/ncoff (0.00/0/0)tcp00 127.0.0.1:10000127.0.0.1:36516ESTABLISHED -off (0.00/0/0)此时,第三个nc客户端连接状态为SYN_SENT,进行超时重传SYN段 。


推荐阅读