meishaoming / blog

MIT License
1 stars 2 forks source link

用 tcpdump 观察 TCP 通讯 #89

Open meishaoming opened 4 years ago

meishaoming commented 4 years ago

发现一个看包格式的好地方 INACON Protocol

发起连接

监听命令:

sudo tcpdump tcp -nn -vvv -i lo

参数说明:

22:13:20.924569 IP (tos 0x0, ttl 64, id 43515, offset 0, flags [DF], proto TCP (6), length 60)
    127.0.0.1.58224 > 127.0.0.1.2013: Flags [S], cksum 0xfe30 (incorrect -> 0x0427), seq 3788373325, win 43690, options [mss 65495,sackOK,TS val 2179486902 ecr 0,nop,wscale 7], length 0

第一行是 IP 包头,格式如下:

image

每个字段的意义,详细的见 IPv4

第二行是 TCP 信息。TCP 包头详见 TCP

image

    127.0.0.1.58224 > 127.0.0.1.2013: Flags [S], cksum 0xfe30 (incorrect -> 0x0427), seq 3788373325, win 43690, options [mss 65495,sackOK,TS val 2179486902 ecr 0,nop,wscale 7], length 0

详细来看这一句的意思:

  1. 从本地 58224 端口向 2013 端口发送
  2. Flags [S] 表示是个 SYN 包(head 里的 SYN=1)
  3. cksum 0xfe30 (incorrect -> 0x0427) 错误的 checksum 原因是发包的时候 checksum 是交由网卡来计算的。这里数据包没有经过网卡,所以这里显示是错的。加上参数 --dont-verify-checksums 就不会显示了
  4. seq 就是发送方的序号
  5. win 43690 发送窗口大小
  6. options可选项:
    • mss 是 max segment size
    • sackOK - Selective acknowledgement permitted 连续发多个包的时候如果中间丢了某个包,就要用这个 selective ack 来告诉发送方要重发那个包
    • TS val 2179486902 ecr 0 - Timestamp,ecr是指 timestamp echo reply。发送时填好 Timestamp,然后在 ACK 中把这个 Timestamp 填到 Tecr 字段中。收到之后对比当前时间和 Tecr 就可以估算出 RTT(round-trip time)
    • nop - No-operation 只占一个 byte,用于让其它 options 4 字节对齐
    • wscale 7 - Window scale 指定发送和接收窗口的一个放大系数,这样单次可以收发更多数据,提高吞吐量
  7. length 0 最后这个length 表示这个TCP包没有payload

三次握手

监听命令:sudo tcpdump tcp -nn -vvv --dont-verify-checksums -i lo

// client 发起连接,发送 SYN

22:37:28.268573 IP (tos 0x0, ttl 64, id 5802, offset 0, flags [DF], proto TCP (6), length 60)
    127.0.0.1.58268 > 127.0.0.1.2013: Flags [S], seq 3615980748, win 43690, options [mss 65495,sackOK,TS val 2180934246 ecr 0,nop,wscale 7], length 0

// server 收到连接请求,返回 ACK+SYN

22:37:28.268587 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    127.0.0.1.2013 > 127.0.0.1.58268: Flags [S.], seq 553639227, ack 3615980749, win 43690, options [mss 65495,sackOK,TS val 2180934246 ecr 2180934246,nop,wscale 7], length 0

// client 收到 ACK+SYN,返回一个 ACK (作为SYN的回应)

22:37:28.268599 IP (tos 0x0, ttl 64, id 5803, offset 0, flags [DF], proto TCP (6), length 52)
    127.0.0.1.58268 > 127.0.0.1.2013: Flags [.], seq 1, ack 1, win 342, options [nop,nop,TS val 2180934246 ecr 2180934246], length 0

发送数据

在这个程序中,client 连接后,server 就立即返回后台的时间

// server 发送 25 个字节的数据,20+20+12+25=77

22:37:28.268911 IP (tos 0x0, ttl 64, id 46938, offset 0, flags [DF], proto TCP (6), length 77)
    127.0.0.1.2013 > 127.0.0.1.58268: Flags [P.], seq 1:26, ack 1, win 342, options [nop,nop,TS val 2180934247 ecr 2180934246], length 25

// client 收到数据,返回 ACK(注意 ack=26,因为已经收到了25字节数据,下一字节的序号是26)
22:37:28.268918 IP (tos 0x0, ttl 64, id 5804, offset 0, flags [DF], proto TCP (6), length 52)
    127.0.0.1.58268 > 127.0.0.1.2013: Flags [.], seq 1, ack 26, win 342, options [nop,nop,TS val 2180934247 ecr 2180934247], length 0

// server 发送 FIN,单边关闭连接。server 进入了 FIN_WAIT_1 状态
22:37:28.268925 IP (tos 0x0, ttl 64, id 46939, offset 0, flags [DF], proto TCP (6), length 52)
    127.0.0.1.2013 > 127.0.0.1.58268: Flags [F.], seq 26, ack 1, win 342, options [nop,nop,TS val 2180934247 ecr 2180934247], length 0

// client 返回 ACK 
22:37:28.308704 IP (tos 0x0, ttl 64, id 5805, offset 0, flags [DF], proto TCP (6), length 52)
    127.0.0.1.58268 > 127.0.0.1.2013: Flags [.], seq 1, ack 27, win 342, options [nop,nop,TS val 2180934287 ecr 2180934247], length 0

最后 client 端也主动结束:

// client 关闭连接,发送 FIN
22:44:09.904648 IP (tos 0x0, ttl 64, id 5806, offset 0, flags [DF], proto TCP (6), length 52)
    127.0.0.1.58268 > 127.0.0.1.2013: Flags [F.], seq 1, ack 27, win 342, options [nop,nop,TS val 2181335882 ecr 2180934247], length 0

// server 端返回 ACK
22:44:09.904656 IP (tos 0x0, ttl 64, id 46940, offset 0, flags [DF], proto TCP (6), length 52)
    127.0.0.1.2013 > 127.0.0.1.58268: Flags [.], seq 27, ack 2, win 342, options [nop,nop,TS val 2181335882 ecr 2181335882], length 0