zoukankan      html  css  js  c++  java
  • tcpdump——分析tcp关闭4次过程

    tcp连接在关闭的时候,需要进行4次传输过程,图片如下:

    首先是client发送FIN到Server,通常是client调用了close,client进入FIN_WAIT1状态。

    Server的tcp在收到FIN之后,立刻返回ACK给客户端,同时Server进入CLOSE_WAIT状态。

    client在收到server的ack之后,进入FIN_WAIT2状态。

    server在发送ACK之后,通常主动调用close来关闭与客户端连接的socket,这个是时候发送FIN给客户端,server进入LAST_ACK状态。

    客户端收到server的FIN之后,进入TIME_WAIT, 同事返回ACK,server收到ACK后进入CLOSED状态

    现在来写程序验证一下这个过程,同时用tcpdump进行抓包分析。

    1、client主动关闭socket,server不关闭对应的socket。

    server程序:

    addr = ('127.0.0.1', 9988)
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(addr)
    server.listen(10)
    
    while True:
            connection, address = server.accept()
            print 'connection ip:', address

    client程序:

    addr = ('127.0.0.1', 9988)
    client = socket(AF_INET, SOCK_STREAM)
    client.connect(addr)
    client.send('hello server123456789')
    client.close()

    先运行server,再运行client,用tcpdump抓包(只贴出tcp关闭部分)。

    16:41:03.411439 IP localhost.40464 > localhost.9988: Flags [F.], seq 4077955702, ack 4081074301, win 513, options [nop,nop,TS val 19893050 ecr 19893050], length 0
    
    16:41:03.450137 IP localhost.9988 > localhost.40464: Flags [.], ack 4077955703, win 512, options [nop,nop,TS val 19893060 ecr 19893050], length 0

    从抓获的数据包来看,client给server发送了FIN,并且server端返回了ACK,然后就没有其他的数据包了。

    用netstat -an | grep 9988查看现在的套接字信息是这样的:

    tcp        0      0 127.0.0.1:9988          0.0.0.0:*               LISTEN     
    tcp       22      0 127.0.0.1:9988          127.0.0.1:40464         CLOSE_WAIT 
    tcp        0      0 127.0.0.1:40464         127.0.0.1:9988          FIN_WAIT2  

    客户端的端口处于FIN_WAIT2, server与client连接的端口处于CLOSE_WAIT状态。这个是符合一开始的状态图的。

    由于server并没有主动关闭本地的socket,所以没有发送FIN给client,client一直等待server的FIN,所以一直处在FIN_WAIT2.

    2、server在客户端断开连接后也调用close

    server程序:

    addr = ('127.0.0.1', 9988)
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(addr)
    server.listen(10)
    
    while True:
            connection, address = server.accept()
            print 'connection ip:', address
            while True:
                    readBuf = connection.recv(1024)
                    if len(readBuf) == 0:
                            connection.close()
                            print 'connection close'
                            break
                    print readBuf

    client程序还是使用之前的。

    server需要在client关闭的时候,也调用close,那么有一个问题就是server如何知道client调用了close。在tcp协议里,如果client关闭了,那么server在read的时候会返回长度为0的数据。

    server程序为了测试,只是很简单的对一个客户端sock进行read,如果read到的buf长度是0,就认为client关闭连接了,然后server也关闭对应的sock。

    运行程序,抓包结果如下:

    17:06:10.932798 IP localhost.47835 > localhost.9988: Flags [F.], seq 1945810449, ack 1949135243, win 513, options [nop,nop,TS val 20269930 ecr 20269930], length 0
    0x0000: 4500 0034 fd7c 4000 4006 3f45 7f00 0001 E..4.|@.@.?E....
    0x0010: 7f00 0001 badb 2704 73fa b611 742d 718b ......'.s...t-q.
    0x0020: 8011 0201 fe28 0000 0101 080a 0135 4b6a .....(.......5Kj
    0x0030: 0135 4b6a .5Kj


    17:06:10.932885 IP localhost.9988 > localhost.47835: Flags [F.], seq 1949135243, ack 1945810450, win 512, options [nop,nop,TS val 20269930 ecr 20269930], length 0
    0x0000: 4500 0034 3ae1 4000 4006 01e1 7f00 0001 E..4:.@.@.......
    0x0010: 7f00 0001 2704 badb 742d 718b 73fa b612 ....'...t-q.s...
    0x0020: 8011 0200 fe28 0000 0101 080a 0135 4b6a .....(.......5Kj
    0x0030: 0135 4b6a .5Kj


    17:06:10.932923 IP localhost.47835 > localhost.9988: Flags [.], ack 1949135244, win 513, options [nop,nop,TS val 20269930 ecr 20269930], length 0
    0x0000: 4500 0034 fd7d 4000 4006 3f44 7f00 0001 E..4.}@.@.?D....
    0x0010: 7f00 0001 badb 2704 73fa b612 742d 718c ......'.s...t-q.
    0x0020: 8010 0201 fe28 0000 0101 080a 0135 4b6a .....(.......5Kj
    0x0030: 0135 4b6a .5Kj

    分析抓获的数据包我们能看到,服务器返回了[F.],然后客户端就ACK了。

    通常来说tcp关闭连接是4次过程,这里我们只看到了3次,是因为server返回给client的ACK和FIN合并成一个分组发给client了。

    为了验证是这样的情况,我们分析一下tcp数据包头。我们知道IP数据包头是20个字节,后面开始是tcp数据包头,也就是说从2704 badb开始是tcp数据包。

    前12个字节是两边的端口号和数据包序列号,最重要的是第13个字节,也就是tcpdump抓取的“8011”,转换成二进制就是“1000 0000 0001 0001”

    后面6个字节也就是"010001" 对应"urg ack psh rst syn fin",也就是说ACK=1,FIN=1,可以说明这个数据包是将ACK和FIN合并发送的。

     

    为了清晰的看见tcp关闭的4次过程,我们修改一下server程序,在connection.close()调用之前,sleep 1秒。

    这样的情况下,在进行抓包,我们能看到完成的4次分组数据包。

    17:53:07.149572 IP localhost.40583 > localhost.9988: Flags [F.], seq 3147749708, ack 3163302012, win 513, options [nop,nop,TS val 20973985 ecr 20973983], length 0
        0x0000:  4500 0034 d0d6 4000 4006 6beb 7f00 0001  E..4..@.@.k.....
        0x0010:  7f00 0001 9e87 2704 bb9e d94c bc8c 287c  ......'....L..(|
        0x0020:  8011 0201 fe28 0000 0101 080a 0140 09a1  .....(.......@..
        0x0030:  0140 099f                                .@..
    17:53:07.153457 IP localhost.9988 > localhost.40583: Flags [.], ack 3147749709, win 512, options [nop,nop,TS val 20973986 ecr 20973985], length 0
        0x0000:  4500 0034 21e3 4000 4006 1adf 7f00 0001  E..4!.@.@.......
        0x0010:  7f00 0001 2704 9e87 bc8c 287c bb9e d94d  ....'.....(|...M
        0x0020:  8010 0200 fe28 0000 0101 080a 0140 09a2  .....(.......@..
        0x0030:  0140 09a1                                .@..
    17:53:08.151568 IP localhost.9988 > localhost.40583: Flags [F.], seq 3163302012, ack 3147749709, win 512, options [nop,nop,TS val 20974235 ecr 20973985], length 0
        0x0000:  4500 0034 21e4 4000 4006 1ade 7f00 0001  E..4!.@.@.......
        0x0010:  7f00 0001 2704 9e87 bc8c 287c bb9e d94d  ....'.....(|...M
        0x0020:  8011 0200 fe28 0000 0101 080a 0140 0a9b  .....(.......@..
        0x0030:  0140 09a1                                .@..
    17:53:08.152182 IP localhost.40583 > localhost.9988: Flags [.], ack 3163302013, win 513, options [nop,nop,TS val 20974235 ecr 20974235], length 0
        0x0000:  4500 0034 0000 4000 4006 3cc2 7f00 0001  E..4..@.@.<.....
        0x0010:  7f00 0001 9e87 2704 bb9e d94d bc8c 287d  ......'....M..(}
        0x0020:  8010 0201 1f82 0000 0101 080a 0140 0a9b  .............@..
        0x0030:  0140 0a9b                                .@..

    不过为什么server在发送FIN的时候还是发送了一个ACK,暂时我也不知道为什么,后面清楚的时候,进行更新。

  • 相关阅读:
    P3811乘法逆元
    P4549裴蜀定理
    备用代码区
    其他板子整理
    DP
    图论板子整理
    约数
    浅谈假学习假努力
    质数
    P1019 单词接龙
  • 原文地址:https://www.cnblogs.com/chobits/p/2669391.html
Copyright © 2011-2022 走看看