详解TCP三次握手的首次握手协议数据

我们之前学习计算机网络,都会提到TCP的三次握手、四次挥手,相信不少小伙伴都能简单描述但一直似懂非懂。那么双方主机到底是如何进行协议交互的?今天带领大家从二进制协议数据层面进行深入分析。

一、环境准备

我们在测试机上搭建一个web服务,端口为8087,访问/hello,返回hello,world

1.1 机器信息

#本机
IP:10.242.16.130
MAC:dc:a9:04:98:87:d2

#远程web服务所在的测试机
IP:192.168.167.248
MAC:94:28:2e:5e:18:04
复制代码

1.2 使用工具

  • tcpdump
  • Wireshark

我们使用tcpdump进行抓包:

sudo tcpdump -n host 192.168.167.248 -w ~/Desktop/http.pcap
curl "http://192.168.167.248:8087/hello"
复制代码

抓包结果已上传至gitee,详见http.pcap

该结果包含了完整的三次握手、数据传输、四次挥手流程,由于结果较多,我们仅以三次握手的首次握手(以下简称1号报文)来进行详细分析,其他流程类似,这里不再详细分析。

二、1号报文分析

我们知道,网络协议是分层的,上层的首部和数据区一起作为下层的数据区,结构如下: image

1号报文的整体二进制数据如下:

0000   94 28 2e 5e 18 04 dc a9 04 98 87 d2 08 00 45 00   .(.^..........E.
0010   00 40 00 00 40 00 40 06 b6 a3 0a f2 10 82 c0 a8   .@..@.@.........
0020   a7 f8 d0 f3 1f 97 3f db f8 f4 00 00 00 00 b0 02   ......?.........
0030   ff ff 30 d1 00 00 02 04 05 b4 01 03 03 06 01 01   ..0.............
0040   08 0a 9d 00 bb ba 00 00 00 00 04 02 00 00         ..............
复制代码

Wireshark分析结果如下: 1号报文分析

Frame 1: 78 bytes on wire (624 bits), 78 bytes captured (624 bits) on interface en0, id 0 (outbound)
    Interface id: 0 (en0)
        Interface name: en0
    Packet flags: 0x00000002
        .... .... .... .... .... .... .... ..10 = Direction: Outbound (0x2)
        .... .... .... .... .... .... ...0 00.. = Reception type: Not specified (0)
        .... .... .... .... .... ...0 000. .... = FCS length: 0
        .... .... .... .... 0000 000. .... .... = Reserved: 0
        .... ...0 .... .... .... .... .... .... = CRC error: Not set
        .... ..0. .... .... .... .... .... .... = Packet too long error: Not set
        .... .0.. .... .... .... .... .... .... = Packet too short error: Not set
        .... 0... .... .... .... .... .... .... = Wrong interframe gap error: Not set
        ...0 .... .... .... .... .... .... .... = Unaligned frame error: Not set
        ..0. .... .... .... .... .... .... .... = Start frame delimiter error: Not set
        .0.. .... .... .... .... .... .... .... = Preamble error: Not set
        0... .... .... .... .... .... .... .... = Symbol error: Not set
    Encapsulation type: Ethernet (1)
    Arrival Time: Sep 16, 2020 09:44:34.693011000 CST
    [Time shift for this packet: 0.000000000 seconds]
    Epoch Time: 1600220674.693011000 seconds
    [Time delta from previous captured frame: 0.000000000 seconds]
    [Time delta from previous displayed frame: 0.000000000 seconds]
    [Time since reference or first frame: 0.000000000 seconds]
    Frame Number: 1
    Frame Length: 78 bytes (624 bits)
    Capture Length: 78 bytes (624 bits)
    [Frame is marked: False]
    [Frame is ignored: False]
    Point-to-Point Direction: Sent (0)
    [Protocols in frame: eth:ethertype:ip:tcp]
    [Coloring Rule Name: TCP SYN/FIN]
    [Coloring Rule String: tcp.flags & 0x02 || tcp.flags.fin == 1]
复制代码

可以看到,整体报文大小为78B,其中:

  • 链路层首部14B;
  • IP首部20B;
  • TCP首部44B;
  • TCP数据区为零。

下面我们将对这几部分进行详细分析。

2.1 链路层首部(14B)

首先,我们看下链路层首部的协议结构: image

扫描二维码关注公众号,回复: 14463019 查看本文章

报文中链路层首部的数据如下:

0000   94 28 2e 5e 18 04 dc a9 04 98 87 d2 08 00         .(.^..........
复制代码

Wireshark解析如下:

Ethernet II, Src: Apple_98:87:d2 (dc:a9:04:98:87:d2), Dst: NewH3CTe_5e:18:04 (94:28:2e:5e:18:04)
    Destination: NewH3CTe_5e:18:04 (94:28:2e:5e:18:04)
        Address: NewH3CTe_5e:18:04 (94:28:2e:5e:18:04)
        .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
    Source: Apple_98:87:d2 (dc:a9:04:98:87:d2)
        Address: Apple_98:87:d2 (dc:a9:04:98:87:d2)
        .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
        .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
    Type: IPv4 (0x0800)
复制代码
  • 0x94282e5e1804:目标MAC地址,6B。这里为192.168.167.248的MAC地址94:28:2e:5e:18:04
  • 0xdca9049887d2:源MAC地址,6B。这里为10.242.16.130 的MAC地址dc:a9:04:98:87:d2
  • 0x0800:上层数据类型,0X0800代表是IP数据报,具体类型详见:ETHER TYPES

2.2 IP首部(20B)

首先,我们看IP首部的协议结构image

报文中IP首部的数据如下:

0000   45 00 00 40 00 00 40 00 40 06 b6 a3 0a f2 10 82   E..@..@.@.......
0010   c0 a8 a7 f8                                       ....
复制代码

Wireshark解析如下:

Internet Protocol Version 4, Src: 10.242.16.130, Dst: 192.168.167.248
    0100 .... = Version: 4
    .... 0101 = Header Length: 20 bytes (5)
    Differentiated Services Field: 0x00 (DSCP: CS0, ECN: Not-ECT)
        0000 00.. = Differentiated Services Codepoint: Default (0)
        .... ..00 = Explicit Congestion Notification: Not ECN-Capable Transport (0)
    Total Length: 64
    Identification: 0x0000 (0)
    Flags: 0x4000, Don't fragment
        0... .... .... .... = Reserved bit: Not set
        .1.. .... .... .... = Don't fragment: Set
        ..0. .... .... .... = More fragments: Not set
    Fragment offset: 0
    Time to live: 64
    Protocol: TCP (6)
    Header checksum: 0xb6a3 [validation disabled]
    [Header checksum status: Unverified]
    Source: 10.242.16.130
    Destination: 192.168.167.248
复制代码
  • 0x4:版本号,4代表IPV4;
  • 0x5:IP首部长度,以4B为单位,所以这里是5*4B=20B;
  • 0b000000:Differentiated Services Codepoint(DSCP),6bit,默认为0;
  • 0b00:Explicit Congestion Notification(ECN,2bit,Not ECN-Capable Transport (0);
  • 0x0040:总长度4*16=64,包括首部和数据;
  • 0x0000:标识,2B;
  • 0b010,标志,3bit;
  • 0b0000000000000:片偏移,13bit,这里没有分片;
  • 0x40:生存时间TTL(每经过一个路由器TTL会减1),1B,这里为64;
  • 0x06:上层协议,1B,0x06代表TCP,0x11代表UDP,0x01代表ICMP,详见:Protocol Numbers
  • 0xb6a3:首部校验和,2B。路由器会校验,由于每经过1个路由器TTL会减1,所以需要重新计算首部校验和;
  • 0x0af21082:源IP,4B,这里为10.242.16.130
  • 0xc0a8a7f8,目标IP,4B,这里为192.168.167.248

注:

  • IP数据长度即为TCP的总长度(TCP首部+TCP数据),其长度为总长度64B-IP首部长度20B=44B。

2.3 TCP首部(20B)

首先,我们看TCP首部的协议结构image

报文中TCP首部的数据如下:

0000   d0 f3 1f 97 3f db f8 f4 00 00 00 00 b0 02 ff ff   ....?...........
0010   30 d1 00 00 02 04 05 b4 01 03 03 06 01 01 08 0a   0...............
0020   9d 00 bb ba 00 00 00 00 04 02 00 00               ............
复制代码

Wireshark解析如下:

Transmission Control Protocol, Src Port: 53491, Dst Port: 8087, Seq: 0, Len: 0
    Source Port: 53491
    Destination Port: 8087
    [Stream index: 0]
    [TCP Segment Len: 0]
    Sequence number: 0    (relative sequence number)
    Sequence number (raw): 1071380724
    [Next sequence number: 1    (relative sequence number)]
    Acknowledgment number: 0
    Acknowledgment number (raw): 0
    1011 .... = Header Length: 44 bytes (11)
    Flags: 0x002 (SYN)
        000. .... .... = Reserved: Not set
        ...0 .... .... = Nonce: Not set
        .... 0... .... = Congestion Window Reduced (CWR): Not set
        .... .0.. .... = ECN-Echo: Not set
        .... ..0. .... = Urgent: Not set
        .... ...0 .... = Acknowledgment: Not set
        .... .... 0... = Push: Not set
        .... .... .0.. = Reset: Not set
        .... .... ..1. = Syn: Set
            [Expert Info (Chat/Sequence): Connection establish request (SYN): server port 8087]
                [Connection establish request (SYN): server port 8087]
                [Severity level: Chat]
                [Group: Sequence]
        .... .... ...0 = Fin: Not set
        [TCP Flags: ··········S·]
    Window size value: 65535
    [Calculated window size: 65535]
    Checksum: 0x30d1 [unverified]
    [Checksum Status: Unverified]
    Urgent pointer: 0
    Options: (24 bytes), Maximum segment size, No-Operation (NOP), Window scale, No-Operation (NOP), No-Operation (NOP), Timestamps, SACK permitted, End of Option List (EOL)
        TCP Option - Maximum segment size: 1460 bytes
            Kind: Maximum Segment Size (2)
            Length: 4
            MSS Value: 1460
        TCP Option - No-Operation (NOP)
            Kind: No-Operation (1)
        TCP Option - Window scale: 6 (multiply by 64)
            Kind: Window Scale (3)
            Length: 3
            Shift count: 6
            [Multiplier: 64]
        TCP Option - No-Operation (NOP)
            Kind: No-Operation (1)
        TCP Option - No-Operation (NOP)
            Kind: No-Operation (1)
        TCP Option - Timestamps: TSval 2634070970, TSecr 0
            Kind: Time Stamp Option (8)
            Length: 10
            Timestamp value: 2634070970
            Timestamp echo reply: 0
        TCP Option - SACK permitted
            Kind: SACK Permitted (4)
            Length: 2
        TCP Option - End of Option List (EOL)
            Kind: End of Option List (0)
    [Timestamps]
        [Time since first frame in this TCP stream: 0.000000000 seconds]
        [Time since previous frame in this TCP stream: 0.000000000 seconds]
复制代码

这44B中有20B为固定首部总数,24B为选项数据。

2.3.1 固定首部

这部分为固定长度,即每次TCP传输都要携带20B的数据。

  • 0xd0f3:源端口,2B,这里为53491;
  • 0x1f97:目标端口,2B,这里为8087;
  • 0x3fdbf8f4:seq序号,4B,这里为1071380724。RFC1948 中提出了一个较好的初始化序列号 ISN 随机生成算法,ISN = M + F (localhost, localport, remotehost, remoteport), M是一个计时器,这个计时器每隔 4 毫秒加 1;F是一个 Hash 算法,根据源 IP、目的 IP、源端口、目的端口生成一个随机数值。要保证 Hash 算法不能被外部轻易推算得出,用 MD5 算法是一个比较好的选择;
  • 0x00000000:ack号,4B,这里为0(只有首次握手不携带ack信息);
  • 0xb:首部总长度,4bit,以4B为单位,11*4B=44B(其中20B为固定长度,24B为选项长度);
  • 0b000:保留标志位;
  • 0b000000010:标志位,NS、CWR、ECE、URG、ACK、PSH、RST、SYN、FIN。可以看到,这里只有SYN标志位置位;
  • 0xffff:滑动窗口大小,4B,这里为65535;
  • 0x30d1:校验和,4B;
  • 0x0000:紧急指针,4B。当URG置位时,此指针表示紧急字节下一个字节的偏移。

2.3.2 选项

这部分为额外的选项信息,长度不定,每次TCP传输按需携带。常见的选项格式如下: image 是不有有点类似序列化中的TLV?是的,它就是!万物同根生,TLV是协议中非常常见的一种格式。

  • 0x020405b4:MSS大小选项,4B。0x02表示为MSS选项,0x04代表MSS选项总长度为4B(即0x020405b4),0x05b4即为MSS大小1460B;
  • 0x010303060x01代表nop,用于填充,以便4B对齐;0x03表示为Window scale选项,0x03表示Window scale选项总长度为3(即0x030306),这里滑动窗口放大倍数=2^6=64;
  • 0x0101080a 9d00bbba 000000000x0101为两个nop,用于4B对齐;0x08表示为Timestamp and echo of previous timestamp选项(用于计算RTT),0x0a表示选项总长度为10(即0x080a 9d00bbba 00000000),0x9d00bbba表示当前时间戳为2634070970,0x00000000表示回复时间戳为0(因为是首次请求);
  • 0x040200000x04表示Selective Acknowledgement permitted选项,0x02表示选项总长度为2B(即0x0402);0x0000表示EOF(即选项的结束标记)。

三、总结

本文通过tcpdump抓包,并使用强大的wireshark工具对三次握手的1号报文进行抽丝剥茧,有利于大家对网络协议有更深层次的认识。

  • 1号报文78B中,只传了链路层首部14B+IP首部20B+TCP首部44B(固定部分20B+选项部分24B),TCP数据为空;
  • IP首部中的TTL字段,由于每经过1个路由器TTL会减1,所以需要重新计算首部校验和;
  • TCP数据长度的计算方式为:IP首部中的总长度 - IP首部长度 - TCP首部长度;
  • RFC1948 中提出了一个较好的初始化序列号 ISN 随机生成算法,ISN = M + F (localhost, localport, remotehost, remoteport)

巨人的肩膀

  1. 实战!我用“大白鲨”让你看见 TCP
  2. 硬不硬你说了算!近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题
  3. my.oschina.net/u/1859679/b…

关于作者

杜云杰,高级架构师,转转架构部负责人,转转技术委员会执行主席,腾讯云TVP。负责服务治理、MQ、云平台、APM、IM、分布式调用链路追踪、监控系统、配置中心、分布式任务调度平台、分布式ID生成器、分布式锁等基础组件。微信号:waterystone,欢迎建设性交流。

转转研发中心及业界小伙伴们的技术学习交流平台,定期分享一线的实战经验及业界前沿的技术话题。

关注公众号「转转技术」(综合性)、「大转转FE」(专注于FE)、「转转QA」(专注于QA),更多干货实践,欢迎交流分享~

猜你喜欢

转载自juejin.im/post/7130062216355086372