基础介绍

先介绍几个TCP协议首部的标志位:

SYN:同步序号。
FIN:发端完成发送任务。
ACK:确认序号有效。
PSH:接收方应该尽快将这个报文段交给应用层。
RST:重建链接。

再介绍几个状态:

CLOSED:关闭状态,没有连接活动正在进行。
LISTEN:监听状态,服务器正在等待连接进入。
SYN_RCVD:收到一个连接请求,尚未确认。
SYN_SENT:已经发出连接请求,等待确认。
ESTABLISHED:连接建立,正常数据传输状态。
FIN_WAIT_1:(主动关闭)已经发送关闭请求,等待确认。
FIN_WAIT_2:(主动关闭)收到对方关闭确认,等待对方关闭请求。
TIME_WAIT:完成双向关闭,等待所有分组消失。
CLOSING:双方同时尝试关闭,等待对方确认。
CLOSE_WAIT:(被动关闭)收到对方关闭请求,已经确认。
LAST_ACK:(被动关闭)等待最后一个关闭确认,并等待所有分组消失。

最后介绍几个本文出现的英文缩写:

ack:它和大写的ACK不同,它不是标志位,只是一个确认序号,它的序号值表示接收端期待接受的下一个报文的序号值。例如:ack = x+1 表示接收端现在已经确认收到前x个报文,接下来该接收第x+1个报文。
seq:表示当前发送的报文序号,后面依次加一,初始序号是动态随机生成的(后面有详细解释为什么要随机)。
MSL:Maximum Segment Lifetime,最长报文寿命,假设具有最大跳数限制的IP分组在网络中存在的时间不可能超过MSL秒(每个IP分组都有一个TTL,没经过一跳路由器,TTL-1)。RFC1122建议MSL = 2min,现在的网络常设为30s。

三次握手

建立连接的过程共需要三个报文段,故称“三次握手”,建立连接的过程如下图所示。

具体过程:
1. 服务启动后一直处于监听状态,等待客户端的连接请求。客户端发起连接请求,首部标志位SYN置为1,同时选择一个序列号seq = x,这时客户端进入SYN_SENT状态。(SYN报文段不能携带数据)。
2. 服务器收到连接请求后,结束监听状态,给客户端回复确认报文,SYN = 1,ACK = 1,seq = y,确认号ack = x+1,进入SYN_RCVD状态。
3. 客户端收到服务器的确认报文后,结束监听状态,给客户端回复确认报文,SYN = 1,ACK = 1,seq = x+1,ack = y+1。随后进入ESTABLISHED状态,此后双方可以进行全双工通信,此后报文中标志位SYN均等于0。

四次挥手

拆除连接的过程共需要四个报文段,故称“三次挥手”,拆除连接的过程如下图所示。

具体过程:
1. 客户端首先发出拆除连接报文,FIN = 1,进入FIN_WAIT_1状态。
2. 服务器收到拆除连接请求,回复确认报文,ACK = 1。
3. 客户端收到回复进入FIN_WAIT_2 状态,等待服务器发送拆除连接报文,此时服务器还可以给客户端发送数据。服务器若没有要发送的数据,则发送一个拆除连接报文,FIN = 1。
4. 客户端对此报文回复确认,之后并不会马上关闭连接,而是进入一个TIME_WAIT状态,等待2MSL后,才进入CLOSED状态。而服务器在收到回复报文后就立即进入CLOSED状态。

一些问题

1. 为什么客户端在TIME_WAIT状态必须为2MSL的时间?

①可靠地实现TCP全双工连接的终止。
客户端最后一个确认报文可能丢失,这样服务器收不到确认报文,会超时重传一个拆除连接的报文,而客户端在2MSL时间内收到这个重传的拆除连接报文,客户端发送确认,并重启2MSL计时器,最后正常关闭连接。如果客户端发送最后一个确认报文后立即关闭连接,就收不到上述服务器重传的拆除连接报文,这样服务器就无法正常进入关闭状态。
②防止“已失效的连接请求报文”出现在本连接中。
客户端在发送完最后一个确认报文后,再经过2MSL,就可以使本连接持续的时间内所产生的所有报文在网络中消失。

2. 为什么ISN(初始序号seq)要动态随机生成?

①动态随机生成可以增加安全性,为了避免被第三方猜到,从而伪造RST报文。
②ISN动态随机使得每个TCP session的字节序列号没有重叠,如果出现TCP五元组冲突这种极小概率情况的发生,一个session数据也不会被误认为是另一个session的。
补充:
ISN:Initial Sequence Number,发送方的字节数据编号的源点,让对方生成一个合法的窗口,就是seq的初始化。
RFC1948中提出了一个较好的初始化序列号ISN随机生成算法:
ISN = M + F(localhost, localport, remotehost, remoteport)
M是一个计时器,这个计时器每隔4毫秒加1。
F是一个Hash算法,根据源IP、目的IP、源端口、目的端口生成一个随机数值。想要保证hash算法不被第三方猜出,用MD5算法较好。

3. 第三方伪造RST报文需要满足什么条件?

需要seq位于对方的合法接收窗口内,而ISN是动态随机的,猜出对方合法窗口难度加大。

4. 三次握手过程中的报文可以携带数据吗?

RFC793文档里带有SYN标志的过程包是不可以携带数据的,也就是说三次握手的前两次是不可以携带数据的,因为连接还没又建立,而第三次握手时是可以携带数据的。
能够发出第三次握手报文的主机肯定能接收到第二次握手的报文。伪造IP的主机是不会接收到第二次报文的,所以能发第三次报文的一定是合法用户。第三次握手的瞬间,双方状态就会切换为established,里面的数据可以正常被解析。

5. 为什么不可以把第一次握手中的数据缓存下来,等连接成功建立后再递交?

这样可能会引起SYNFLOOD攻击,如果攻击者伪造了大量连接请求报文,携带了大量数据,而接收方开辟大量缓存来存放携带的数据,就会造成资源耗尽而拒绝服务。

6. 只看到过TCP状态位为“FIN+ACK",但从来没看到过状态只有”FIN“,这时为什么?

REC793明确规定,除了第一次握手报文SYN除外,其他报文必须将ACK置为一。
TCP作为一个可靠传输协议,其可靠性就依赖于收到对方的数据,给对方发送ACK确认,这样对方就可以释放缓存的数据,因为对方确信数据已经被接收到了,但tcp在ip网络上传输,丢包是常有的事,接收方要抓住一切机会,把消息告诉发送方,最方便的就是任何我发送的报文都带ACK标志位。

7. ACK标志位能单独承担这个消息传递的任务吗?

不能。需要有个ack(acknowledge number)配合才行,如果我方发出的ack=10001,那意味着序号10000及之前的已经成功接收,如果对方占据序号10000是应用层数据,那么就是确认应用层数据,如果对方占据自己序列号10000是FIN状态位,那么就是确认收到对方的FIN。

(部分资源来自网络,侵删)