TCP 协议(一)报文结构
TCP 协议(二)连接与断开
TCP 协议(三)十种核心机制
TCP 提供面向连接的通信传输,面向连接是指在传送数据之前必须先建立连接,数据传送完成后要释放连接。无论哪一方向另一方发送数据之前,都必须先在双方之间建立一条连接。在TCP/IP协议中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的。
同时由于TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议,TCP是全双工模式,所以需要四次挥手关闭连接。
1.TCP 包首部
网络中传输的数据包由两部分组成:一部分是协议所要用到的首部,另一部分是上一层传过来的数据。首部的结构由协议的具体规范详细定义。在数据包的首部,明确标明了协议应该如何读取数据。反过来说,看到首部,也就能够了解该协议必要的信息以及所要处理的数据。包首部就像协议的脸。
所以我们在学习TCP协议之前,首先要知道TCP在网络传输中处于哪个位置,以及它的协议的规范,下面我们就看看TCP首部的网络传输起到的作用:
下面的图是TCP头部的规范定义,它定义了TCP协议如何读取和解析数据:
TCP首部承载这TCP协议需要的各项信息,下面我们来分析一下:
1.1.TCP 端口号
TCP的连接是需要四个要素确定连接唯一:
(源IP,源端口号)+ (目地IP,目的端口号)
所以TCP首部预留了两个16位作为端口号的存储,而IP地址由上一层IP协议负责传递
源端口号和目地端口各占16位两个字节,也就是端口的范围是2^16=65535
另外1024以下端口是系统保留的,从1024-65535是用户使用的端口范围。
1.2.TCP 的序号和确认号
32位序号 seq:Sequence number 缩写seq ,TCP通信过程中某一个传输方向上的字节流的每个字节的序号,通过这个来确认发送的数据有序,也可以理解为:本次传输数据的起始字节在整个数据流中的位置。比如现在序列号为1000,发送了1000,下一个序列号就是2000。
32位确认号 ack:Acknowledge number 缩写ack,TCP对上一次seq序号做出的确认号,用来响应TCP报文段,给收到的TCP报文段的序号seq加1。也可以理解为: 期望收到下一包的序号,用于确认已经收到数据的偏移序号。
1.3.数据偏移(4位首部长度)
占4位,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远。这个字段指出TCP报文段的首部长度(报头长度)。由于首部中还有长度不确定的选项字段,因此数据偏移字段是必要的,注意,“数据偏移”的单位不是字节,而是4字节。由于4位二进制数能表示的最大十进制数字是15,因此数据偏移的最大值是60字节(15*4字节),这也是TCP首部的最大字节(即选项长度不能超过40字节=60-20)。
首部长度(报头长度) - 20字节 = 选项长度
如果数据偏移的值是5,此时表示整个TCP报头的长度是 4*5 = 20字节(此时相当于没有选项)
如果数据偏移的值是15,此时表示整个TCP报头的长度是4*15 = 60字节(此时相当于选项是40个字节)
1.4.保留
填充为0
1.5.TCP 的标志位
6位标志位,它们中的多个可同时被设置为1。每个TCP段都有一个目的,这是借助于TCP标志位选项来确定的,允许发送方或接收方指定哪些标志应该被使用,以便段被另一端正确处理。
用的最广泛的标志是 SYN,ACK 和 FIN,用于建立连接,确认成功的段传输,最后终止连接。
URG:简写为U,紧急标志位,表示数据包的紧急指针域有效,用来保证连接不被阻断,并督促中间设备尽快处理;
ACK:简写为.,确认标志位,对已接收的数据包进行确认;
PSH:简写为P,推送标志位,表示该数据包被对方接收后应立即交给上层应用,而不在缓冲区排队;
RST:简写为R,重置标志位,用于连接复位、拒绝错误和非法的数据包;
SYN:简写为S,同步标志位,用于建立会话连接,同步序列号;
FIN: 简写为F,完成标志位,表示我已经没有数据要发送了,即将关闭连接;
1.5.1.紧急 URG
当URG=1时,表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应尽快发送(相当于高优先级的数据),而不要按原来的排队顺序来传送。例如,已经发送了很长的一个程序要在远地的主机上运行。但后来发现了一些问题,需要取消该程序的运行,因此用户从键盘发出中断命令。如果不使用紧急数据,那么这两个字符将存储在接收TCP的缓存末尾。只有在所有的数据被处理完毕后这两个字符才被交付接收方的应用进程。这样做就浪费了很多时间。当URG置为1时,发送应用进程就告诉发送方的TCP有紧急数据要传送。于是发送方TCP就把紧急数据插入到本报文段数据的最前面,而在紧急数据后面的数据仍然是普通数据。这时要与首部中紧急指针(Urgent Pointer)字段配合使用。
1.5.2.确认 ACK
仅当 ACK = 1时确认号字段才有效,当 ACK = 0时确认号无效。TCP 规定,在连接建立后所有的传送的报文段都必须把 ACK 置为1。
1.5.3.推送 PSH
当两个应用进程进行交互式的通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应。在这种情况下,TCP 就可以使用推送(push)操作。这时,发送方 TCP 把 PSH 置为1,并立即创建一个报文段发送出去。接收方 TCP 收到 PSH =1的报文段,就尽快地(即“推送”向前)交付接收应用进程。而不用再等到整个缓存都填满了后再向上交付。一般这个不需要手动执标志,TCP 默认实现。
1.5.4.复位 RST
当 RST = 1时,表名 TCP 连接中出现了严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立传输连接。RST 置为1还用来拒绝一个非法的报文段或拒绝打开一个连接。
1.5.5.同步 SYN
在连接建立时用来同步序号。当 SYN = 1而 ACK = 0时,表明这是一个连接请求报文段。对方若同意建立连接,则应在响应的报文段中使 SYN = 1和 ACK = 1,因此 SYN 置为1就表示这是一个连接请求或连接接受报文。
1.5.6.终止 FIN
发送端完成任务,表要求释放运输连接。
1.6.窗口
16位2字节,用于表示滑动窗口大小,窗口大小最大为65535(2^16-1)字节。
接收方的流量控制手段,窗口大小为字节数,起始于确认序号字段指明的值,这个值是接收端正期望接收的字节。告诉发送端,接收端目前允许发送端数据量。大小两字节65535,在客户端与服务端 TCP 都允许的情况下,选项中可存在窗口扩展选项。 示例中:窗口大小65535,代表告诉发送方,从这个下一包0的序号开始,接收方只能接受65535个字节长度了(当然这里还没有算上扩展选项,稍后再讲)
1.7.校验和
16位2字节,检验和覆盖了整个的 TCP 报文段: TCP 首部和 TCP 数据。这是一个强制性的字段,一定是由发端计算和存储,并由收端进行验证。和 UDP 用户数据报一样,在计算检验和时,要在 TCP 报文段的前面加上12字节的伪首部。伪首部的格式和 UDP 用户数据报的伪首部一样。但应把伪首部第4个字段中的17改为6(TCP的协议号是6);把第5字段中的UDP中的长度改为TCP长度。接收方收到此报文段后,仍要加上这个伪首部来计算检验和。若使用TPv6,则相应的伪首部也要改变。
校验和错误的分组丢弃(因为源IP地址、源端口号或者协议字段可能被破坏)。
1.8.紧急指针
16位2字节,在紧急 URG 标志执1的时候有效,代表一个偏移量,和序号字段值相加,代表紧急数据最后一个字节的序号。
1.9.选项
长度可变,最长可达40字节。当没有使用“选项”时,TCP 的首部长度是20字节。其最大长度可根据 TCP 首部长度进行推算。TCP 首部长度用4位数据偏移表示,单位是4字节,那么选项部分最长为:(2^4-1)*4-20=40字节。
TCP 协议最初只规定了一种选项,即最长报文段长度(数据字段加上TCP首部),又称为 MSS。MSS 告诉对方 TCP “我的缓存所能接收的报文段的数据字段的最大长度是 MSS 个字节”。
新的RFC规定有以下几种选型:选项表结束,无操作,最大报文段长度,窗口扩大因子,时间戳。
选项的第一个字段kind说明选项的类型
有的TCP选项没有后面两个字段,仅包含1字节的kind字段
第二个字段length(如果有的话)指定该选项的总长度
该长度包括kind字段和length字段占据的2字节
第三个字段info(如果有的话)是选项的具体信息
TCP选项有7种
1.9.1.kind=0,选项表结束(EOP)选项
一个报文段仅用一次。放在末尾用于填充,用途是说明:首部已经没有更多的消息,应用数据在下一个32位字开始处。
1.9.2.kind=1,空操作(NOP NO-Operation)选项
没有特殊含义,一般用于将 TCP 选项的总长度填充为4字节的整数倍。
它要求选项部分中的每种选项长度必须是4字节的倍数,不足的则用 NOP 填充。同时也可以用来分割不同的选项字段。如窗口扩大选项和SACK 之间用 NOP 隔开。
1.9.3.kind=2,最大报文段长度(MSS)选项
MSS(最大报文段长度 Maxium Segment Size),TCP连接初始化时,通信双方使用该选项来协商最大报文段长度。TCP 模块通常将 MSS 设置为(MTU-40)字节(减掉的这40字节包括20字节的 TCP 头部和20字节的 IP 头部)。这样携带 TCP 报文段的 IP 数据报的长度就不会超过 MTU(假设 TCP 头部和 IP 头部都不包含选项字段,并且这也是一般情况),从而避免本机发生 IP 分片。对以太网而言,MSS值是1460(1500-40)字节。
为什么要有MSS:为了增加网络利用率
一般说来,如果没有分段发生, MSS 大部分时候还是越大越好。报文段越大允许每个报文段传送的数据就越多,相对 IP 和 TCP 首部有更高的网络利用率。
1.9.4.kind=3,窗口扩大因子选项
TCP连接初始化时,通信双方使用该选项来协商接收窗口的扩大因子。在TCP的头部中,接收窗口大小是用16位表示的,故最大为65535字节,但实际上TCP模块允许的接收窗口大小远不止这个数(为了提高TCP通信的吞吐量)。窗口扩大因子解决了这个问题。
假设TCP头部中的接收通告窗口大小是N,窗口扩大因子(移位数)是M,那么TCP报文段的实际接收通告窗口大小是N*2M,或者说N左移M位。注意,M的取值范围是0~14。我们可以通过修改 /proc/sys/net/ipv4/tcp_window_scaling 内核变量来启用或关闭窗口扩大因子选项。
和MSS选项一样,窗口扩大因子选项只能出现在同步报文段中,否则将被忽略。但同步报文段本身不执行窗口扩大操作,即同步报文段头部的接收窗口大小就是该TCP报文段的实际接收窗口大小。当连接建立好之后,每个数据传输方向的窗口扩大因子就固定不变了。
(1)窗口扩大选项占3字节,其中第一字节代表类型,第二字节代表长度,第三字节(shift count)则是扩展移位值S了,新的窗口值等于TCP首部中的窗口位数从16增大到(16+S)。移位值允许使用的最大值是14,相当于窗口最大值增大到2^ (16+14) -1=2^30-1。
(2)窗口扩大选项可以在双方初始建立TCP连接时进行协商。如果连接的某一端实现了窗口扩大,当它不再需要扩大其窗口时,可发送S=0选项,使窗口大小回到16位。
1.9.5.kind=4,选择性确认(Selective Acknowledgment,SACK)选项
TCP通信时,如果某个TCP报文段丢失,则TCP会重传最后被确认的TCP报文段后续的所有报文段,这样原先已经正确传输的TCP报文段也可能重复发送,从而降低了TCP性能。SACK技术正是为改善这种情况而产生的,它使TCP只重新发送丢失的TCP报文段,而不用发送所有未被确认的TCP报文段。选择性确认选项用在连接初始化时,表示是否支持SACK技术。我们可以通过修改 /proc/sys/net/ipv4/tcp_sack 内核变量来启用或关闭选择性确认选项。
1.9.6.kind=5,SACK实际工作的选项
该选项的参数告诉发送方本端已经收到并缓存的不连续的数据块,从而让发送端可以据此检查并重发丢失的数据块。每个块边沿(edge of block)参数包含一个4字节的序号。其中块左边沿表示不连续块的第一个数据的序号,而块右边沿则表示不连续块的最后一个数据的序号的下一个序号。这样一对参数(块左边沿和块右边沿)之间的数据是没有收到的。因为一个块信息占用8字节,所以TCP头部选项中实际上最多可以包含4个这样的不连续数据块(考虑选项类型和长度占用的2字节)。
1.9.7.kind=8,时间戳选项。
该选项提供了较为准确的计算通信双方之间的回路时间(Round Trip Time,RTT)的方法,从而为TCP流量控制提供重要信息。我们可以通过修改 /proc/sys/net/ipv4/tcp_timestamps 内核变量来启用或关闭时间戳选项。
占10字节,其中最主要的字段是时间戳字段(4字节)和时间戳回送回答字段(4字节)。时间戳选项有以下两个概念:
(1)用来计算往返时间RTT(往返时间)。发送方在发送报文段时把当前时钟的时间值放入时间戳字段,接收方在确认该报文段时把时间戳字段复制到时间戳回送回答字段。因此,发送方在收到确认报文后,可以准确地计算出RTT来。为了减少任一端所维持的状态数量,对于每个连接只保持一个时间戳的数值。选择何时,更新这个数值的算法非常简单(《TCP/IP详解》):
1)TCP 跟踪下一个 ACK 中将要发送的时间戳的值(一个名为 tsrecent 的 变 量 ) 以及最后发送的 ACK 中的确认序号(一个名为 lastack 的变量)。这个序号就是接收方期望的序号。
2) 当一个包含有字节号 lastack 的报文段到达时,则该报文段中的时间戳被保存在 tsrecent 中。
3) 无论何时发送一个时间戳选项, tsrecent 就作为时间戳回显应答字段被发送,而序号字段被保存在 lastack 中。
(2)用于处理防止序号绕回 PAWS(TCP序号超过2^32 的情况):TCP报文段的序号只有32位,而每增加 2 ^ 32个序号就会重复使用原来用过的序号。当使用高速网络时,在一次TCP连接的数据传送中序号很可能被重复使用。为了使接收方能够把新的报文段和迟到很久的报文段区分开,则可以在报文段中加上这种时间戳。
1.10.填充
填充字段,用来补位,使整个首部长度是4字节的整数倍。
1.11.数据
TCP负载。
2.抓包分析
Frame 1546: 66 bytes on wire (528 bits), 66 bytes captured (528 bits) on interface \Device\NPF_{13928725-3157-4B45-93AF-47DC1FA99F14}, id 0
Ethernet II, Src: HP_b2:68:82 (c8:5a:cf:b2:68:82), Dst: RuijieNe_81:8d:13 (14:14:4b:81:8d:13)
Internet Protocol Version 4, Src: 10.180.201.67, Dst: 10.49.44.14
Transmission Control Protocol, Src Port: 52234, Dst Port: 5546, Seq: 0, Len: 0
Source Port: 52234
Destination Port: 5546
[Stream index: 13]
[Conversation completeness: Complete, WITH_DATA (47)]
[TCP Segment Len: 0]
Sequence Number: 0 (relative sequence number)
Sequence Number (raw): 1974122170 # 字节序列为
[Next Sequence Number: 1 (relative sequence number)]
Acknowledgment Number: 0
Acknowledgment number (raw): 0
1000 .... = Header Length: 32 bytes (8) # 数据偏移为8,8*4字节=32字节
Flags: 0x002 (SYN)
Window: 64240
[Calculated window size: 64240]
Checksum: 0x5bcf [unverified]
[Checksum Status: Unverified]
Urgent Pointer: 0
Options: (12 bytes), Maximum segment size, No-Operation (NOP), Window scale, No-Operation (NOP), No-Operation (NOP), SACK permitted # 这么多加起来12字节,为啥有这么多
TCP Option - Maximum segment size: 1460 bytes # MSS 4字节
Kind: Maximum Segment Size (2)
Length: 4
MSS Value: 1460
TCP Option - No-Operation (NOP) # NOP 1字节
Kind: No-Operation (1)
TCP Option - Window scale: 8 (multiply by 256) # windows scale 3字节
Kind: Window Scale (3)
Length: 3
Shift count: 8
[Multiplier: 256]
TCP Option - No-Operation (NOP) # NOP 1字节
Kind: No-Operation (1)
TCP Option - No-Operation (NOP) # NOP 1字节
Kind: No-Operation (1)
TCP Option - SACK permitted # SACK 2字节
Kind: SACK Permitted (4)
Length: 2
[Timestamps]
Frame 1548: 66 bytes on wire (528 bits), 66 bytes captured (528 bits) on interface \Device\NPF_{13928725-3157-4B45-93AF-47DC1FA99F14}, id 0
Ethernet II, Src: RuijieNe_81:8d:13 (14:14:4b:81:8d:13), Dst: HP_b2:68:82 (c8:5a:cf:b2:68:82)
Internet Protocol Version 4, Src: 10.49.44.14, Dst: 10.180.201.67
Transmission Control Protocol, Src Port: 5546, Dst Port: 52234, Seq: 0, Ack: 1, Len: 0
Source Port: 5546
Destination Port: 52234
[Stream index: 13]
[Conversation completeness: Complete, WITH_DATA (47)]
[TCP Segment Len: 0]
Sequence Number: 0 (relative sequence number)
Sequence Number (raw): 225567612
[Next Sequence Number: 1 (relative sequence number)]
Acknowledgment Number: 1 (relative ack number)
Acknowledgment number (raw): 1974122171
1000 .... = Header Length: 32 bytes (8)
Flags: 0x012 (SYN, ACK)
Window: 29200
[Calculated window size: 29200]
Checksum: 0xf3b5 [unverified]
[Checksum Status: 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: 1456 bytes
Kind: Maximum Segment Size (2)
Length: 4
MSS Value: 1456
TCP Option - No-Operation (NOP)
Kind: No-Operation (1)
TCP Option - No-Operation (NOP)
Kind: No-Operation (1)
TCP Option - SACK permitted
Kind: SACK Permitted (4)
Length: 2
TCP Option - No-Operation (NOP)
Kind: No-Operation (1)
TCP Option - Window scale: 7 (multiply by 128)
Kind: Window Scale (3)
Length: 3
Shift count: 7
[Multiplier: 128]
[Timestamps]
[SEQ/ACK analysis]
[This is an ACK to the segment in frame: 1546]
[The RTT to ACK the segment was: 0.017981000 seconds]
[iRTT: 0.018242000 seconds]
Frame 1549: 54 bytes on wire (432 bits), 54 bytes captured (432 bits) on interface \Device\NPF_{13928725-3157-4B45-93AF-47DC1FA99F14}, id 0
Ethernet II, Src: HP_b2:68:82 (c8:5a:cf:b2:68:82), Dst: RuijieNe_81:8d:13 (14:14:4b:81:8d:13)
Internet Protocol Version 4, Src: 10.180.201.67, Dst: 10.49.44.14
Transmission Control Protocol, Src Port: 52234, Dst Port: 5546, Seq: 1, Ack: 1, Len: 0
Source Port: 52234
Destination Port: 5546
[Stream index: 13]
[Conversation completeness: Complete, WITH_DATA (47)]
[TCP Segment Len: 0]
Sequence Number: 1 (relative sequence number)
Sequence Number (raw): 1974122171
[Next Sequence Number: 1 (relative sequence number)]
Acknowledgment Number: 1 (relative ack number)
Acknowledgment number (raw): 225567613
0101 .... = Header Length: 20 bytes (5)
Flags: 0x010 (ACK)
Window: 517
[Calculated window size: 132352]
[Window size scaling factor: 256]
Checksum: 0xa48f [unverified]
[Checksum Status: Unverified]
Urgent Pointer: 0
[Timestamps]
[SEQ/ACK analysis]
[This is an ACK to the segment in frame: 1548]
[The RTT to ACK the segment was: 0.000276000 seconds]
[iRTT: 0.018242000 seconds]
Frame 1551: 186 bytes on wire (1488 bits), 186 bytes captured (1488 bits) on interface \Device\NPF_{13928725-3157-4B45-93AF-47DC1FA99F14}, id 0
Ethernet II, Src: HP_b2:68:82 (c8:5a:cf:b2:68:82), Dst: RuijieNe_81:8d:13 (14:14:4b:81:8d:13)
Internet Protocol Version 4, Src: 10.180.201.67, Dst: 10.49.44.14
Transmission Control Protocol, Src Port: 52234, Dst Port: 5546, Seq: 1, Ack: 1, Len: 132
Source Port: 52234
Destination Port: 5546
[Stream index: 13]
[Conversation completeness: Complete, WITH_DATA (47)]
[TCP Segment Len: 132]
Sequence Number: 1 (relative sequence number)
Sequence Number (raw): 1974122171
[Next Sequence Number: 133 (relative sequence number)]
Acknowledgment Number: 1 (relative ack number)
Acknowledgment number (raw): 225567613
0101 .... = Header Length: 20 bytes (5)
Flags: 0x018 (PSH, ACK)
Window: 517
[Calculated window size: 132352]
[Window size scaling factor: 256]
Checksum: 0xf5f6 [unverified]
[Checksum Status: Unverified]
Urgent Pointer: 0
[Timestamps]
[SEQ/ACK analysis]
[iRTT: 0.018242000 seconds]
[Bytes in flight: 132]
[Bytes sent since last PSH flag: 132]
TCP payload (132 bytes)
Data (132 bytes)
参考:
https://zhuanlan.zhihu.com/p/626242323
http://www.023wg.com/message/message/cd_feature_tcp_message_format.html
https://www.zhihu.com/question/51074319?sort=created
https://www.cnblogs.com/cangqinglang/p/13817086.html