TCP 报文格式简介
- TCP 报文由
TCP Header
和TCP
数据组成。 - TCP Header 的最大长度为 60 字节(byte),而必须要有的固定长度也就是图一的前5层的**20字节(byte),每层占有
32bit
,也就是32/8=4
字节,5层,5*4 = 20
字节,那么第六层的可选项和填充也就是Tcp Options字段最大为60-20=40字节(byte)**。填充是为了使TCP首部为4字节(32bit)的整数倍。
TCP首部格式
Source Port:源端口,16位(bit),2个字节(byte)。
Destination Port:目的端口,16位,2个字节。
Sequence Number:序号,发送数据包中的第一个字节的序列号,32位。
Acknowledgment Number:确认序列号,32位。
Data Offset:数据偏移,4位,该字段的值是TCP首部(包括选项)长度除以4。
标志位:6位,共6个标志位URG
表示 Urgent Pointer
字段有意义ACK
表示 Acknowledgment Number
字段有意义PSH
表示 Push
功能,RST 表示复位 TCP 连接RST
表示连接复位请求SYN
表示 SYN
报文(在建立 TCP 连接的时候使用)FIN
表示没有数据需要发送了(在关闭 TCP 连接的时候使用)
Window:窗口,表示接收缓冲区的空闲空间,16位,2个字节,用来告诉TCP连接对端自己能够接收的最大数据长度。
Checksum:校验和,16位,2个字节。
Urgent Pointers:紧急指针,16位,2个字节,只有 URG 标志位被设置时该字段才有意义,表示紧急数据相对序列号(Sequence Number字段的值)的偏移。
选项和填充:最常见的可选字段是最长报文大小,又称为 MSS(Maximum Segment Size),每个连接方通常都在通信的第一个报文段(为建立连接而设置SYN标志为1的那个段)中指明这个选项,它表示本端所能接受的最大报文段的长度。选项长度不一定是32位的整数倍,所以要加填充位,即在这个字段中加入额外的零,以保证TCP头是32的整数倍。
数据:TCP 报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有 TCP 首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情况中,也会发送不带任何数据的报文段。
TCP Options字段
Tcp Options 字段的最大长度为40字节。Tcp Options 字段的一般数据结构如图所示:
- Kind(1字节)
- Length(1字节)
- Info(n字节)
选项的第一个字段 kind 说明选项的类型。有的 TCP 选项没有后面两个字段,仅包含1字节的kind字段。第二个字段length(如果有的话)指定该选项的总长度,该长度包括kind字段和length字段占据的2字节。第三个字段 info(如果有的话)是选项的具体信息。
- 第一个kind= 2,表示最大报文段长度(Max Segment Size,MSS),TCP 模块通常将 MSS 设置为(MTU-40, MTU[Maximum Transmission Unit,缩写 MTU,中文名是:最大传输单元])字节(减掉的这40字节包括20字节的TCP头部和20字节的IP头部)。这样携带 TCP 报文段的IP数据报的长度就不会超过 MTU(假设TCP头部和IP头部都不包含选项字段,并且这也是一般情况),从而避免本机发生IP分片。对以太网而言,MSS 值是1460(1500-40)字节。
- kind= 4,表示支持 SACK,SACK Block(收到乱序数据)。
- kind = 8,代表 Timestamps,即时间戳,启用 Timestamp Option 后,每个 TCP Segment 中都会带有 Timestamp Option,其中包含了两个 32bit 的 Timestamp 也就是各四个字节的 Timestamp Value(TSval)和 Timestamp Echo Reply(TSecr)。发送方在发送报文段时把当前时钟的时间值放入时间戳字段,接收方在确认该报文段时把时间戳字段值复制到时间戳回送回答字段。因此,发送方在收到确认报文后,可以准确计算出RTT。
TCP标志位
在TCP层,有个FLAGS字段,这个字段有以下几个标识:SYN, FIN, ACK, PSH, RST, URG。其中,对于我们日常的分析有用的就是前面的五个字段。
SYN表示建立连接,FIN表示关闭连接,ACK表示响应,PSH表示有 DATA数据传输,RST表示连接重置。
ACK是可能与SYN,FIN等同时使用的,比如SYN和ACK可能同时为1,它表示的就是建立连接之后的响应,如果只是单个的一个SYN,它表示的只是建立连接。
TCP的几次握手就是通过这样的ACK表现出来的。但SYN与FIN是不会同时为1的,因为SYN表示的是建立连接,而FIN表示的是断开连接。
RST一般是在FIN之后才会出现为1的情况,表示的是连接重置。
一般地,当出现FIN包或RST包时,我们便认为客户端与服务器端断开了连接;而当出现SYN和SYN+ACK包时,我们认为客户端与服务器建立了一个连接。
PSH为1的情况,一般只出现在DATA内容不为0的包中,也就是说PSH为1表示的是有真正的TCP数据包内容被传递。
SYN:同步标志
同步序列编号(Synchronize Sequence Numbers)栏有效,表示同步序号,用来建立连接。该标志仅在三次握手建立TCP连接时有效。它提示TCP连接的服务端检查序列编号,该序列编号为TCP连接初始端(一般是客户端)的初始序列编号。TCP序列编号是一个范围从 0~4294967295
(2^32-1) 的32位计数器。通过TCP连接交换的数据中每一个字节都经过序列编号。在TCP报头中的序列编号栏包括了TCP分段中第一个字节的序列编号。
SYN标志位和ACK标志位搭配使用,当连接请求的时候,SYN=1,ACK=0;连接被响应的时候,SYN=1,ACK= 1;
这个标志的数据包经常被用来进行端口扫描。扫描者发送一个只有SYN的数据包,如果对方主机响应了一个数据包回来,就表明这台主机存在这个端口;但是由于这种扫描方式只是进行TCP三次握手的第一次握手,因此这种扫描的成功表示被扫描的机器不很安全,一台安全的主机将会强制要求-连接-严格进行的TCP三次握手;
初始化序列号生成过程:
RFC1948中提出了一个较好的初始化序列号ISN随机生成算法。
ISN = M + F(localhost, localport, remotehost, remoteport).
M是一个计时器,这个计时器每隔4毫秒加1。
F是一个Hash算法,根据源IP、目的IP、源端口、目的端口生成一个随机数值。要保证hash算法不能被外部轻易推算得出,用MD5算法是一个比较好的选择。
ISN会和一个假的时钟绑在一起,这个时钟M会在每4微秒对ISN做加一操作,直到超过 2^32,又从0开始。这样,一个ISN的周期大约是4.55个小时。因为,我们假设我们的TCP Segment在网络上的存活时间不会超过Maximum Segment Lifetime(缩写为MSL),所以,只要MSL的值小于4.55小时,那么,我们就不会重用到ISN。
ACK:确认标志
确认编号(Acknowledgement Number)栏有效。此标志表示应答域有效;有两个取值:0和1,为1的时候表示应答域有效,反之为0。TCP报头内的确认编号栏内包含的确认编号(w+1,Figure-1)为下一个预期的序列编号,同时提示远端系统已经成功接收所有数据。
RST:复位标志
这个标志表示连接复位请求。用来复位那些产生错误的连接,也被用来拒绝错误和非法的数据包;
URG:紧急标志
此标志表示TCP包的紧急指针域有效,用来保证TCP连接不被中断,并且督促中间层设备要尽快处理这些数据;
PSH:推标志
这个标志位表示Push操作,该标志置位时,指在数据包到达接收端以后,接收端不将该数据进行队列处理,立即传送给应用程序,而不是在缓冲区中排队。在处理 telnet 或 rlogin 等交互模式的连接时,该标志总是置位的。
FIN:结束标志
表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据可以传送了,发送FIN标志位的TCP数据包后,连接将被断开。这个标志的数据包也经常被用于进行端口扫描。当一个FIN标志的TCP数据包发送到一台计算机的特定端口,如果这台计算机响应了这个数据,并且反馈回来一个RST标志 的TCP包,就表明这台计算机上没有打开这个端口,但是这台计算机是存在的;如果这台计算机没有反馈回来任何数据包,这就表明,这台被扫描的计算机存在这个端口。
工作方式
建立连接
TCP是因特网中的传输层协议,使用三次握手协议建立连接。当主动方发出 SYN
连接请求后,等待对方回答 SYN+ACK
,并最终对对方的 SYN
执行 ACK
确认。
TCP三次握手的过程如下:
- 客户端发送
SYN(SEQ=x)
报文给服务器端,进入SYN_SEND
状态。 - 服务器端收到
SYN
报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)
报文,进入SYN_RECV
状态。 - 客户端收到服务器端的
SYN
报文,回应一个ACK(ACK=y+1)
报文,进入Established(已获确认的)
状态。
三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。
连接终止
建立一个连接需要三次握手,而终止一个连接要经过四次握手,这是由TCP的 半关闭
(half-close
)造成的。
- (1)某个应用进程首先调用
close
,称该端执行主动关闭(active close)
。该端的TCP于是发送一个FIN
分节,表示数据发送完毕。 - (2) 接收到这个
FIN
的对端执行被动关闭(passive close)
,这个FIN
由TCP确认。
注意:FIN
的接收也作为一个文件结束符(end-of-file
)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其他数据之后,因为,FIN
的接收意味着接收端应用进程在相应连接上再无额外数据可接收。
- (3) 一段时间后,接收到这个文件结束符的应用进程将调用
close
关闭它的套接字。这导致它的TCP
也发送一个FIN
。 - (4) 接收这个最终
FIN
的原发送端TCP(即执行主动关闭的那一端)确认这个FIN
。
既然每个方向都需要一个FIN
和一个 ACK
,因此通常需要4个分节。
注意:
(1) 通常
是指,某些情况下,步骤1的 FIN
随数据一起发送,另外,步骤2和步骤3发送的分节都出自执行被动关闭那一端,有可能被合并成一个分节。
(2) 在 步骤2 与 步骤3 之间,从执行 被动关闭一端 到 执行主动关闭一端 流动数据是可能的,这称为 半关闭
(half-close)。
(3) 当一个Unix进程无论自愿地(调用exit或从main函数返回)还是非自愿地(收到一个终止本进程的信号)终止时,所有打开的描述符都被关闭,这也导致仍然打开的任何TCP连接上也发出一个FIN
。
无论是客户还是服务器,任何一端都可以执行主动关闭。通常情况是,客户执行主动关闭,但是某些协议,例如,HTTP/1.0却由服务器执行主动关闭。
TCP重连
四元组:源IP地址、目的IP地址、源端口、目的端口
五元组:源IP地址、目的IP地址、协议号、源端口、目的端口
七元组:源IP地址、目的IP地址、协议号、源端口、目的端口、服务类型、接口索引
状态详解
CLOSED:表示初始状态。
LISTEN:表示服务器端的某个SOCKET处于监听状态,可以接受连接了。
SYN_RCVD: 这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本 上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个ACK报文不予发送。因此这种状态 时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。
SYN_SENT: 这个状态与SYN_RCVD呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。
ESTABLISHED:表示连接已经建立了。
FIN_WAIT_1: FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别 是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即 进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马 上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。
FIN_WAIT_2:FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。
TIME_WAIT: 表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。
CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发送FIN报文后,按理来说是应该先收到(或同时收到)对方的 ACK 报文,再收到对方的 FIN 报文。但是 CLOSING 状态表示你发送 FIN 报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文。什 么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一个SOCKET的话,那么就出现了双方同时发送FIN报 文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。
LAST_ACK:它是被动关闭一方在发送FIN报文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。
参考: