TCP协议探究(一):报文格式与连接建立终止
一 TCP:传输控制协议报文格式
1 TCP服务
-
提供面向连接、可靠的字节流服务
-
面向连接意味着两方通信,不支持多播和广播
-
可靠性的支持:
- 应用数据被分割成TCP认为最适合发送的数据块。由TCP传递给IP的信息单位称为报文段或段(segment)。
- 当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
- 当TCP收到发自TCP连接另一端的数据,它将响应一个确认。
- TCP将保持它首部和数据的检验和。
- TCP将对收到的数据进行重新排序,将收到的数据以正确的顺序交给应用层。
- IP数据报会发生重复,TCP的接收端必须丢弃重复的数据。
- TCP还能提供流量控制,TCP连接的每一方都有固定大小的缓冲空间,防止较快主机致使较慢主机的缓冲区溢出。
- TCP对字节流的内容不作任何解释。对字节流的解释由TCP连接双方的应用层解释。
2 TCP首部
- TCP首部为20个字节
- 源端口号和目的端口号 :4个字节,用于寻找发端与收端的应用进程。
- 32位序号:4个字节,用于识别从TCP发端向收端发送的数据字节流,循环利用,逐个递增。
- 32位确认序号:4个字节,上次已成功收到数据字节序号加1(只有在ACK标志为1时,该字段才有效)
- 首部长度:4位,用于标识首部的长度,即为5 * 4~15 * 4字节等于20~60字节(固定报头(20字节) + 可选选项)
- 标志比特:
- URG:紧急指针
- ACK:确认序号有效
- PSH:接受方应尽快将报文段交给应用层
- RST:重建连接
- SYN:同步序号用来发起一个连接
- FIN:发端完成发送任务
- 窗口大小:用于流量控制,最大为65535字节(16位)
- 检验和:覆盖整个TCP报文段(TCP首部和数据),发端计算和存储,收端验证
- 紧急指针:当URG为1时,才有效。
二 TCP连接的建立与终止
1 连接的建立与终止
(1)建立连接协议(三次握手连接)
- Client发送SYN段指明打算连接Serer的端口以及初始序号(ISN),SYN为1
- Server响应Server的初始序号(ISN)作为应答,同时将确认序号设置为Client的ISN+1以对Client的SYN报文进行确认。
- Client将确认序号设置为Server的ISN+1以对Server的SYN报文进行确认。
(2)WireShark抓包分析三次握手连接
# client的SYN报文
Transmission Control Protocol, Src Port: 1682, Dst Port: 1883, Seq: 0, Len: 0
Source Port: 1682 # 源端口
Destination Port: 1883 # 目标端口
Sequence number: 0 (relative sequence number) # 序号
Acknowledgment number: 0 # 确认序号
1000 .... = Header Length: 32 bytes (8) # 首部长度
Flags: 0x002 (SYN) # 标识符
Window size value: 64240 # 窗口大小
Checksum: 0xa6f3 [unverified] # 检验和
Urgent pointer: 0 # 由于URG为0,所以紧急指针为0
Options: (12 bytes), Maximum segment size, No-Operation (NOP), Window scale, No-Operation (NOP), No-Operation (NOP), SACK permitted # 可选选项,后续讲解
TCP Option - Maximum segment size: 1460 bytes # 最大报文端长度
TCP Option - No-Operation (NOP) # 无操作
TCP Option - Window scale: 8 (multiply by 256) # 窗口扩大因子
TCP Option - No-Operation (NOP)
TCP Option - No-Operation (NOP)
TCP Option - SACK permitted
# server的响应报文和SYN报文
Transmission Control Protocol, Src Port: 1883, Dst Port: 1682, Seq: 0, Ack: 1, Len: 0
Source Port: 1883
Destination Port: 1682
Sequence number: 0 (relative sequence number)
Acknowledgment number: 1 (relative ack number) # client的Squence number plus one
1000 .... = Header Length: 32 bytes (8)
Flags: 0x012 (SYN, ACK) # 设置SYN和ACK(响应)
Window size value: 14600
Checksum: 0xfd7c [unverified]
Urgent pointer: 0
Options: (12 bytes), Maximum segment size, No-Operation (NOP), No-Operation (NOP), SACK permitted, No-Operation (NOP), Window scale
TCP Option - Maximum segment size: 1412 bytes
TCP Option - No-Operation (NOP)
TCP Option - No-Operation (NOP)
TCP Option - SACK permitted
TCP Option - No-Operation (NOP)
TCP Option - Window scale: 6 (multiply by 64)
# client的响应报文
Transmission Control Protocol, Src Port: 1682, Dst Port: 1883, Seq: 1, Ack: 1, Len: 0
Source Port: 1682
Destination Port: 1883
Sequence number: 1 (relative sequence number)
Acknowledgment number: 1 (relative ack number)
0101 .... = Header Length: 20 bytes (5)
Flags: 0x010 (ACK)
Window size value: 259
Checksum: 0xa6e7 [unverified]
Urgent pointer: 0
(3)连接终止协议
- Client发送FIN报文,此时Client不再发送业务数据到Server,并等待Server的响应报文(Client应用程序关闭)
- Server收到Client的报文,响应ACK报文。
- Server发送FIN报文,此时双方都不再发送业务数据数据,并等待Client的响应报文(Server应用程序关闭))
- Server收到Client的ACK报文即终止连接。
(4)WireShark抓包分析正常关闭
# Client发送FIN报文
Transmission Control Protocol, Src Port: 1682, Dst Port: 1883, Seq: 3, Ack: 1, Len: 0
Source Port: 1682
Destination Port: 1883
Sequence number: 3 (relative sequence number)
Acknowledgment number: 1 (relative ack number)
0101 .... = Header Length: 20 bytes (5)
Flags: 0x011 (FIN, ACK) # ACK是由于需要响应先前的交互
Window size value: 259
Checksum: 0xa6e7 [unverified]
Urgent pointer: 0
# Server发送ACK
Transmission Control Protocol, Src Port: 1883, Dst Port: 1682, Seq: 1, Ack: 4, Len: 0
Source Port: 1883
Destination Port: 1682
Sequence number: 1 (relative sequence number)
Acknowledgment number: 4 (relative ack number)
0101 .... = Header Length: 20 bytes (5)
Flags: 0x011 (FIN, ACK)
Window size value: 229
Checksum: 0x759f [unverified]
Urgent pointer: 0
# Server发送FIN
Transmission Control Protocol, Src Port: 1883, Dst Port: 1682, Seq: 1, Ack: 4, Len: 0
Source Port: 1883
Destination Port: 1682
Sequence number: 1 (relative sequence number)
Acknowledgment number: 4 (relative ack number)
0101 .... = Header Length: 20 bytes (5)
Flags: 0x011 (FIN, ACK)
Window size value: 229
Checksum: 0x759f [unverified]
Urgent pointer: 0
# Client发送ACK
Transmission Control Protocol, Src Port: 1682, Dst Port: 1883, Seq: 4, Ack: 2, Len: 0
Source Port: 1682
Destination Port: 1883
Sequence number: 4 (relative sequence number)
Acknowledgment number: 2 (relative ack number)
0101 .... = Header Length: 20 bytes (5)
Flags: 0x010 (ACK)
Window size value: 259
Checksum: 0xa6e7 [unverified]
Urgent pointer: 0
2 连接建立的超时重试
- 第一次超时
- 6秒后重试
- 24秒后重试
- ......
- 非固定,可调整
3 最大报文段长度:MSS
- 最大报文长度(MSS)表示TCP传往另一端的最大块数据的长度。
- 默认536字节,超出需要分节(Segment),该值来源于IPv4的最小重组缓冲区大小(576字节),IP首部+TCP首部为40字节,所以传输数据为576-40=536字节。
4 TCP的半关闭
- TCP提供了连接的一端在结束它的发送后还能接受来自另一端数据的能力。
- 即我已经完成了数据传送,因此发送一个文件结束(FIN)给另一端,但我还想接收另一端发来的数据,直到它给我发来文件结束(FIN)
5 复位报文段:RST
(1)发到不存在的端口的连接请求
(2)异常终止一个连接
# 某一端直接关闭连接
Transmission Control Protocol, Src Port: 2446, Dst Port: 1883, Seq: 1, Ack: 1, Len: 0
Source Port: 2446
Destination Port: 1883
Sequence number: 1 (relative sequence number)
Acknowledgment number: 1 (relative ack number)
0101 .... = Header Length: 20 bytes (5)
Flags: 0x014 (RST, ACK) # 复位报文
Window size value: 0
Checksum: 0xa6e7 [unverified]
Urgent pointer: 0
- 优点
- 应用程序丢弃任何代发的数据并立即发送复位报文段
- RST的接收方会区分另一端执行的是异常关闭还是正常关闭
(3)检测半打开连接
- 半打开:一方终止,一方还不知道。
- 出现原因:掉电(模拟机直接关闭即可)
6 同时打开
- 双方同时执行主动打开,即每一方都发送SYN,但最终只建立了一个连接。
- 模拟这个可以通过辣鸡VPN进行测试,如亚马逊的免费云(即往返时间比较长,便于模拟)。
7 同时关闭
- 同时关闭与正常关闭使用的段交换数目相同。
8 TCP选项
- 选项表结束:kind=0(1字节)
- 无操作:kind=1(1字节)
- 最大报文长度:kind=2(1字节),len=4(1字节),最大报文段长度(2字节)
- 窗口扩大因子:kind=3(1字节),len=3(1字节),移位数(1字节)
- 时间戳:kind=8(1字节),len=10(1字节),时间戳值(4字节),时间戳回显应答(4字节)
# 发送MSS
TCP Option - Maximum segment size: 1460 bytes
Kind: Maximum Segment Size (2)
Length: 4
MSS Value: 1460
# NOP
TCP Option - No-Operation (NOP)
Kind: No-Operation (1)
# 窗口扩大因子
TCP Option - Window scale: 8 (multiply by 256)
Kind: Window Scale (3)
Length: 3
Shift count: 8
三 未来计划
- 学习TCP是为了更好地理解MQTT协议(即自顶向下的学习方式,以前自底向上学过一段时间,效果一般,因为没有应用场景)
- TCP学完之后,将补充HTTP协议(基于TCP协议)
- HTTP协议之后,将学习WebSocket协议,并最终回到MQTT协议(能够基于WebSocket协议进行传输)