TCP - 首部
这篇文章来讲讲 TCP 报文首部相关的概念,这些头部都是支撑TCP复杂功能的基石
我们通过网络抓包 Wireshark 来抓一个TCP包
源端口、目标端口
计算机上的进程要和其他进程通信是要通过计算机端口的,而一个计算机端口某个时刻只能被一个进程占用,所以通过指定源端口和目标端口,就可以知道是哪两个进程需要通信。源端口、目标端口是用16位表示的,可推算计算机的端口个数为2^16个
(源IP,源端口,目的IP,目的端口) 四元组确定唯一一个TCP连接;(IP,端口)也称为一个插口(socket)
在上图中,首先看到的源端口 (Src Port)和目标端口(Dst Port), 上图本地源端口号为 49876,目标端口 443
TCP 报文头部里没有源 ip 和目标 ip 地址,只有源端口号和目标端口号
TCP 的报文里是没有源 ip 和目标 ip 的,因为那是 IP 层协议的事情,TCP 层只有源端口和目标端口。
序列号(Sequence number)
TCP 是面向字节流的协议,通过 TCP 传输的字节流的每个字节都分配了序列号,序列号(Sequence number)指的是本报文段第一个字节的序列号。
例如 100kb 的 HTML 文档数据,一共102400(100 * 1024)个字节,那么每一个字节就都有了编号,整个文档的编号范围是 0 ~ 102399
序号字段值指的是本报文段所发送的数据的第一个字节的序号。
如果整个 100kb 的HTML文档分割成死歌等分之后
第一个 TCP 报文段包含的是第一个 25kb 的数据,0 ~ 25599 字节, 该报文的序号的值就是:0
第二个 TCP 报文段包含的是第二个 25kb 的数据,25600 ~ 51199 字节,该报文的序号的值就是:25600
序列号加上报文的长度,就可以确定传输的是哪一段数据。序列号是一个 32 位的无符号整数,达到 2^32-1 后循环到 0。
在 SYN 报文中,序列号用于交换彼此的初始序列号,在其它报文中,序列号用于保证包的顺序。
因为网络层(IP 层)不保证包的顺序,TCP 协议利用序列号来解决网络包乱序、重复的问题,以保证数据包以正确的顺序组装传递给上层应用。
确认号(Acknowledgemt Number)
TCP 使用确认号(Acknowledgemt Number)来告知对方下一个期望接收的序列号,小于此确认号的所有字节都已经收到。
注意点:
- 不是所有的包都需要确认的
- 不是收到了数据包就立马需要确认的,可以延迟一会在确认
- ACK 本身不需要被确认,否则就会无穷无尽死循环
- 确认号永远是表示小于此确认号的字节都已经收到
首部长度
首部长度包含4位,TCP首部选项字段的长度是可变的,首部最大长度为1111*4=60字节;没有选项的情况下,正常的长度是20字节;
TCP 标志位(Flags)
TCP 有很多种标记,有些用来发起连接同步初识序列号,有些用来确认数据包,还有些用来结束连接。TCP 定义了一个8位的字段用来表示 flags, 大部分都只用了后6个
下面是 wireshark ACK包的 flags
我们通常所说的 SYN、ACK、FIN、RST 其实只是把 flags 对应的 bit 位置为 1 而已,这些标记可以组合使用,比如 SYN+ACK,FIN+ACK 等
常见的如下:
- URG(URGent) 当URG=1时,表明紧急指针字段有效;
- SYN(Synchronize):用于发起连接数据包同步双方的初始序列号
- ACK(Acknowledge):确认数据包
- RST(Reset):这个标记用来强制断开连接,通常是之前建立的连接已经不在了、包不合法、或者实在无能为力处理
- FIN(Finish):通知对方我发完了所有数据,准备断开连接,后面我不会再发数据包给你了。
- PSH(Push):告知对方这些数据包收到以后应该马上交给上层应用,不能缓存起来
窗口大小
TCP的流量控制由连接的每一端通过声明窗口的大小来提供;窗口大小为字节数,起始于确认序号字段指明的值,这个值是接收端正期望接收的字节数;窗口大小是一个16位字段,因而窗口大小最大为65535字节;
检验和
跟UDP一样,TCP检验和的计算内容:伪首部 + 首部 + 数据
伪首部:占用12字节,仅在计算检验和时起作用,并不会传递给网络层
紧急指针
紧急指针是一个正的偏移量,和序号字段中的值相加标识紧急数据最后一个字节的序号;紧急指针只有在URG标志位设置为1时才有效;TCP紧急指针是发送端向另一端发送紧急数据的一种方式;
可选项
常用的选项有以下几个:
- MSS:最大段大小选项,是 TCP 允许的从对方接收的最大报文段
- SACK:选择确认选项
- Window Scale:窗口缩放选项