TCP报文结构
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port | Destination Port |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum | Urgent Pointer |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Options | Padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| data |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
这些字段是所有TCP特性的基石,很难在这里把每一个字段使用的场景说清楚,下面只是对部分字段做一些说明
字段 | 解释 |
---|---|
Sequence Number | 序列号,TCP协议的一个基础概念就是在TCP中对每一个data端的每一个8位字节都有一个编号。例如,在SYN为0的情况下,如果此时SEQ为x,则代表data段中的第一个字节编号为x。如果该data段具有100个字节,则data段的编号范围为xx+100。而如果在SYN为1时,即在链接建立阶段,此时SEQ为ISN(Initial Sequence Number,初始序列号为一个有效范围内的随机数),第一个字节的序号为x+1,即ISN+1。 需要明确的是,序列号是有范围的,在协议中规定,序列号的有效范围是02^32 -1。当序列号到达边界时将重置为0 |
Acknowledgment Number | 确认号,当ACK为1时,确认号代表下一个期待收到的序列号。例如上一个收到的数据段的序列号为x,则此时回复的数据段的确认号就是x+1。而当发送端收到该确认号时,即可确认上一个数据段已成功送达。 ACK只有在第一个SYN数据段发送时被置位0,在链接建立后将一直为1 |
URG Urgent | 紧急数据标识位。当为1时代表接收端接收到这个数据段后应该立即交给应用程序处理而不是缓冲起来等到缓冲区满再通知应用程序 |
ACK | 确认标识,除了在第一个SYN数据段为0,其他时候都为1 |
PSH | PUSH标识位。为1时发送端应该立即把该数据段发出,而不是缓冲起来等到缓冲区满再发送 |
RST | 重置(RESET)。通常在服务端积极拒绝或者双方关闭链接时RST为1 |
SYN | 同步(Synchronize )。用于链接建立时的数据段标志 |
FIN | 终止(FINAL )。示意链接关闭 |
TCP状态转移
+---------+ ---------\ active OPEN
| CLOSED | \ -----------
+---------+<---------\ \ create TCB
| ^ \ \ snd SYN
passive OPEN | | CLOSE \ \
------------ | | ---------- \ \
create TCB | | delete TCB \ \
V | \ \
+---------+ CLOSE | \
| LISTEN | ---------- | |
+---------+ delete TCB | |
rcv SYN | | SEND | |
----------- | | ------- | V
+---------+ snd SYN,ACK / \ snd SYN +---------+
| |<----------------- ------------------>| |
| SYN | rcv SYN | SYN |
| RCVD |<-----------------------------------------------| SENT |
| | snd ACK | |
| |------------------ -------------------| |
+---------+ rcv ACK of SYN \ / rcv SYN,ACK +---------+
| -------------- | | -----------
| x | | snd ACK
| V V
| CLOSE +---------+
| ------- | ESTAB |
| snd FIN +---------+
| CLOSE | | rcv FIN
V ------- | | -------
+---------+ snd FIN / \ snd ACK +---------+
| FIN |<----------------- ------------------>| CLOSE |
| WAIT-1 |------------------ | WAIT |
+---------+ rcv FIN \ +---------+
| rcv ACK of FIN ------- | CLOSE |
| -------------- snd ACK | ------- |
V x V snd FIN V
+---------+ +---------+ +---------+
|FINWAIT-2| | CLOSING | | LAST-ACK|
+---------+ +---------+ +---------+
| rcv ACK of FIN | rcv ACK of FIN |
| rcv FIN -------------- | Timeout=2MSL -------------- |
| ------- x V ------------ x V
\ snd ACK +---------+delete TCB +---------+
------------------------>|TIME WAIT|------------------>| CLOSED |
+---------+ +---------+
TCP链接建立
TCP链接建立使用'three-way handshake':
TCP A TCP B
1. CLOSED LISTEN
2. SYN-SENT --> <SEQ=100><CTL=SYN> --> SYN-RECEIVED
3. ESTABLISHED <-- <SEQ=300><ACK=101><CTL=SYN,ACK> <-- SYN-RECEIVED
4. ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK> --> ESTABLISHED
5. ESTABLISHED --> <SEQ=101><ACK=301><CTL=ACK><DATA> --> ESTABLISHED
如上图,TCPA(发起方)得初始状态总是CLOSED,SEQ得起始值ISN是随机的。 TCP双方通过SEQ和ACK来确认自己发出的消息切实被对方收到了,同时确认双方之间的TCP通信链路是通的。
状态 | 描述 |
---|---|
CLOSED | 默认状态,代表链接不存在或已经被销毁了 |
SYN-SENT | 客户端发送了SYN并等待另一方确认 |
SYN-RECEIVED | 服务端收到了SYN消息,并发送了ACK和己方的SYN,且在等待另一方的ACK确认消息 |
ESTABLISHED | 链接建立,现在可以自由交换数据了 |
TCP A TCP B
1. CLOSED CLOSED
2. SYN-SENT --> <SEQ=100><CTL=SYN> ...
3. SYN-RECEIVED <-- <SEQ=300><CTL=SYN> <-- SYN-SENT
4. ... <SEQ=100><CTL=SYN> --> SYN-RECEIVED
5. SYN-RECEIVED --> <SEQ=100><ACK=301><CTL=SYN,ACK> ...
6. ESTABLISHED <-- <SEQ=300><ACK=101><CTL=SYN,ACK> <-- SYN-RECEIVED
7. ... <SEQ=101><ACK=301><CTL=ACK> --> ESTABLISHED
同步创建,指双方同时发送SYN,由于发生的前提条件比较苛刻,这种情况比较少见,但TCP设计时就支持这种场景,最终只会成功建立起一条链接。例如A使用本地端口P1向B的P2端口发送SYN包的同时,B也恰好使用本地端口P2向A的P1端口发送SYN包。由于本地端口时随机分配的,且双方的SYN包都必须在对方的SYN包发出之后到达,通常这种场景十分少见。
RST在TCP异常处理中的作用
1.当一方试图向一个不存在的链接(CLOSED状态)写入数据时,RST将作为回复
2.假设链接在任何一个非同步的状态(LISTEN,SYN-SENT,SYN-RECEIVED),且收到的TCP数据ACK了一个还没收到的数据,或者数据包含的安全级别或comparement与要求的不符,RST将作为回复,链接状态保持不变
3.假设链接在任何一个同步状态(ESTABLISHED,FIN-WAIT-1, FIN-WAIT-2, CLOSE-WAIT, CLOSING, LAST-ACK, TIME-WAIT),数据包含的安全级别或comparement与要求的不符,RST将作为回复,链接状态转为CLOSED
RST执行流程:
执行方为LISTEN状态 -> 忽略RST
执行方为SYN-RECEIVED状态且之前已经过LISTEN状态 -> 重置为LISTEN
其他 -> 丢弃链接,回到CLOSED状态
TCP链接释放
CLOSE操作意味着后续没有数据要发送了,由于TCP的全双工特性,因此发送CLOSE操作的一方可以继续执行RECEIVE操作,直到对方已经关闭链接,以此来保障TCP链接的优雅关闭。链接关闭主要有以下3种场景:
1.TCP链接的一方用户主动通过CLOSE操作通知链接关闭
TCP A TCP B
1. ESTABLISHED ESTABLISHED
2. (Close)
FIN-WAIT-1 --> <SEQ=100><ACK=300><CTL=FIN,ACK> --> CLOSE-WAIT
3. FIN-WAIT-2 <-- <SEQ=300><ACK=101><CTL=ACK> <-- CLOSE-WAIT
4. (Close)
TIME-WAIT <-- <SEQ=300><ACK=101><CTL=FIN,ACK> <-- LAST-ACK
5. TIME-WAIT --> <SEQ=101><ACK=301><CTL=ACK> --> CLOSED
6. (2 MSL)
CLOSED
2.TCP从网络中收到FIN消息
TCP收到一个不是通过CLOSE主动发起的FIN消息,这时收到FIN的一方可以ACK这个FIN并告知用户链接正在关闭。场景与第一种类似
3.双方同时通过CLOSE通知链接关闭
双方均在确认FIN后关闭链接
TCP A TCP B
1. ESTABLISHED ESTABLISHED
2. (Close) (Close)
FIN-WAIT-1 --> <SEQ=100><ACK=300><CTL=FIN,ACK> ... FIN-WAIT-1
<-- <SEQ=300><ACK=100><CTL=FIN,ACK> <--
... <SEQ=100><ACK=300><CTL=FIN,ACK> -->
3. CLOSING --> <SEQ=101><ACK=301><CTL=ACK> ... CLOSING
<-- <SEQ=301><ACK=101><CTL=ACK> <--
... <SEQ=101><ACK=301><CTL=ACK> -->
4. TIME-WAIT TIME-WAIT
(2 MSL) (2 MSL)
CLOSED CLOSED