代码实现TCP三次握手:基本数据结构说明
2019-07-27 本文已影响2人
望月从良
从本节开始,我们用代码实现TCP的三次握手流程,首先我们通过wireshark抓包,看看相关数据包的基本机构。首先打开wireshark,然后用浏览器打开一个网页,接着在wireshark过滤框输入:tcp.port==80,这时我们会把http协议发送的数据包过滤出来,从中就可以看到三次握手对应的数据包,如下图所示:

从当前选中的那行往下三行我们看到数据包SYN, SYN+ACK,ACK,这三个数据包就是TCP三次握手。
接下来我们看看数据包的基本结构。由于TCP数据包既要保证数据传输,又要保证数据发送的灵活性,因此它的结构比较复杂,因此它有20字节的包头以及各种可变长的字段,我们看看其结构:
字段 | 大小(字节) | 说明 |
---|---|---|
发送方端口 | 2 | 通常客户端端口可以是任意值,服务器端口是固定值,例如http服务器一般固定端口80 |
接收方端口 | 2 | 如果接收方是服务器,那么端口往往对应特定值 |
初始序列号 | 4 | 用来标注发送的第一个字节,在握手建立连接时双方会相互通知自己的初始序列号 |
应答序列号 | 4 | 用来告诉对方,本方正常接收到对方发送过来的最后一个字节,同时也告诉对方,本方希望对方下次应该从第几个字节开始发送数据 |
数据偏移 | 0.5(4比特) | 用该值乘以4就得到数据的起始位置,同时它也表示包头的长度 |
保留 | 6比特 | 这六个比特位保留,不产生任何作用 |
控制比特位 | 6比特 | 第一个比特位叫URG,设置为1表示当前数据发送具备优先级;第二个比特位叫ACK,用来告诉对方接收到哪些数据,希望对方下次发送哪些数据;第三个比特位叫PSH,设置成1则表示当前数据包必须立刻发送;第四个比特位叫RST,设置成1表示连接出现异常,希望重新建立连接;第五个比特位叫SYN,表示当前数据包是请求建立连接;第六个比特位叫FIN,设置成1表示告诉对方要断开连接 |
窗口大小 | 2 | 告诉对方本方一次能接受多少字节的数据 |
校验值 | 2 | 对整个数据包的内容做校验以防止数据包在传送过程中出错而不被发现 |
紧急指针 | 2 | 当控制比特位URG设置成1时该字段才有用,它用于催促对方赶紧发送本方需要的数据 |
可选字段 | 可变长 | 每个可选字段由三部分组成,第一部分1字节,表示可选项的类型,第二部分1字节,表示可选项的总长度,这意味着该部分数值减去2就是可选项包含数据的长度,第三部分就是可选项对应的数据。 |
填充 | 可变长 | 如果可选字段的总长度不是4字节的倍数,那么这里就用0填充,使得整个可选项总长度是4字节的倍数 |
发送数据 | 可变长 | 这里包含TCP要发送的数据内容 |
整个数据包的组成可以由下图所示:

接下来我们看看几个常用可选项的具体内容:
可选项类型 | 可选项大小(字节) | 可选项数据 | 说明 |
---|---|---|---|
0 | 无内容 | 无内容 | 它表示可选项的结束 |
1 | 无内容 | 无内容 | 它用来占位,它用于将下一个可选项数据的起始地址与4字节对其 |
2 | 4 | 数据发送最大值 | 它表示数据包一次能发送出去的数据量大小,在发送SYN数据包时设置 |
3 | 3 | 窗口大小滑动控制位 | 在双方需要发送大量数据时使用,如果有该选项,那么窗口的大小可以成倍扩展,将前面说到的窗口大小对应的值乘以该值的二次幂就得到扩展的窗口大小,例如该可选项是6时,数据发送时的数据大小就等于前面的窗口大小对应的值乘以2^6,也就是将头部说明的窗口大小乘以64后得到真正的窗口大小 |
4 | 2 | 无内容 | 用于兼容以前的TCP协议版本 |
5 | 可变长 | 指明要对特定数据块进行应答 | 它用于高级传输功能,这里暂时忽略 |
可选项内容很多,这里我们只列出几种情况,以后有需要时我们再进一步说明。
TCP为了防止数据在传输过程中产生错误,它需要对数据进行校验。在计算校验值时,TCP要为整个数据包加上一个”伪包头“后再进行计算,它的内容如下:
字段名 | 大小(字节) | 说明 |
---|---|---|
源IP | 4 | 数据发送方的IP |
目的IP | 4 | 数据接收方的IP |
保留 | 1 | 该字节为0 |
协议号 | 1 | 固定为数值6 |
TCP数据总长度 | 2 | 整个数据包包含包头和数据后的长度值 |
这个包头只有在计算校验值时有用,在数据发送时不能包含它。因此TCP的校验值计算的内容如下:

在计算校验值时使用伪包头的目的在于:首先确保数据的接收方不会错,第二保证其他协议的数据包不会发送给TCP解析层,确保数据发送的长度不出错。这些检验现在看起来很奇怪,在互联网刚发明的时候,硬件质量差,还真有可能将数据包发给不是接收方的设备,因此这些检验在当时是必要的,下一节我们将用代码实现这些数据结构。
更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:
