序号、确认号
-
客户端与服务器从建立连接到发送数据的完整流程如下图
-
第一步建立连接,客户端向服务器发送
SYN,数据部分为0,只有头部信息。 -
发送方:
seq = 0,len = 0 -
第二步,接收方:
ack = 0 + 1 -
第三步,发送方为了响应上一次的
ack,客户端给回服务器的响应中,seq为1。发送方期望下一次的响应从1开始,所以ack = 1 -
第三步和第四步都是客户端向服务器发送信息,所以
seq和ack相同,不同的是第四步数据部分不为0 -
第五步到第八步皆为服务端向客户端发送信息,所以
ack皆为上一次接收到的数据长度k + 1。 -
不同之处在于每次请求的
seq为上一次的长度加上本次数据长度。 -
第九步客户端的
ack为前几次服务器数据之和+ 1,而seq为上一次发送数据长度+ 1 -
总结如下
建立连接
三次握手
CLOSED:client处于关闭状态LISTEN:server处于监听状态,等待client连接SYN-RCVD:表示server接受到了SYN报文,当收到client的ACK报文后,它会进入到ESTABLISHED状态SYN-SENT:表示client已发送SYN报文,等待server的第2次握手ESTABLISHED:表示连接已经建立
前2次握手的特点
SYN都设置为1- 数据部分的长度都为
0 TCP头部的长度一般是32字节- 固定头部:
20字节 - 选项部分:
12字节
- 固定头部:
- 双方会交换确认一些信息
- 比如
MSS,是否支持SACK,Window scale(窗口缩放系数)等 - 这些数据都放在了
TCP头部的选项部分中(12字节)
- 比如
疑问
- 为什么建立连接的时候,要进行
3次握手,2次不行吗?- 主要目的: 防止
server端一直等待,浪费资源
- 主要目的: 防止
- 如果建立连接只需要
2次握手,可能会出现的情况- 假设
client发出的第一个连接请求报文段,因为网络延迟,在连接释放以后的某个时间才到达server - 本来这是一个早已失效的请求,但
server收到此失效的请求后,误认为是client再次发出的一个新的连接请求 - 于是
server就向client发出确认报文段,同意建立连接 - 如果不采用"
3次握手",那么只要server发出确认,新的连接就建立了 - 由于现在
client并没有真正想连接服务器的意愿,因此不会理睬server的确认,也不会向server发送数据 - 但在
server却以为新的连接已经建立,并一直等待client发来数据,这样,server的很多资源就白白浪费了
- 假设
- 采用"三次握手"的办法可以防止上述现象发生
- 例如上述情况,
client没有向server的确认发出确认,server由于收不到确认,就知道client并没有要求建立连接
- 例如上述情况,
- 第3次握手失败了,会怎么处理?
- 此时
server的状态为SYN-RCVD,若等不到client的ACK,server会重新发送SYN+ACK包 - 如果
server多次重发SYN+ACK都等不到client的ACK,就会发送RST包,强制关闭连接
- 此时
释放连接
状态解读
FIN-WAIT-1:表示想主动关闭连接- 向对方发送了
FIN报文,此时进入到FIN-WAIT-1状态
- 向对方发送了
CLOSE-WAIT:表示在等待关闭- 当对方发送
FIN给自己,自己会回应一个ACK报文给对方,此时则进入到CLOSE-WAIT状态 - 在此状态下,需要考虑自己是否还有数据要发送给对方,如果没有,发送
FIN报文给对方
- 当对方发送
FIN-WAIT-2:只要对方发送ACK确认后,主动方就会处于FIN-WAIT-2状态,然后等待对方发送FIN报文CLOSING:一种比较罕见的例外状态- 表示你发送
FIN报文后,并没有收到对方的ACK报文,反而却也收到了对方的FIN报文 - 如果双方几乎在同时准备关闭连接的话,那么就出现了双方同时发送
FIN报文的情况,也即会出现CLOSING状态 - 表示双方都正在关闭连接
- 表示你发送
LAST-ACK:被动关闭的一方在发送FIN报文之后,最后等待对方的ACK报文- 当收到
ACK报文后,即可进入CLOSED状态了
- 当收到
TIME-WAIT:表示收到了对方的FIN报文, 并发送出了ACK报文,就等2MSL后即可进入CLOSED状态了- 如果
FIN-WAIT-1状态下,收到了对方同时带FIN标志和ACK标志的报文时- 可以直接进入到
TIME-WAIT状态,而无须经过FIN-WAIT-2状态
- 可以直接进入到
- 如果
CLOSED: 关闭状态- 由于有些状态的时间比较短暂, 所以很难用
netstat命令看到, 比如SYN-RCVD、FIN-WAIT-1等
细节
TCP/IP协议栈在设计上,允许任何一方先发起断开请求。这里演示的是client主动要求断开client发送ACK后,需要有个TIME-WAIT阶段,等待一段时间后,再真正关闭连接- 一般是等待
2倍的MSL(Maximum Segment Lifetime)`最大分段生存期MSL是TCP报文在Internet上的最长生存时间- 每个具体的
TCP实现都必须选择一个确定的MSL值,RFC 1122建议是2分钟 - 可以防止本次连接中产生的数据包误传到下一次连接中(因为本次连接中的数据包都会在
2MSL时间内消失了)
- 一般是等待
- 如果
client发送ACK后马上释放了,然后又因为网络原因,server没有收到client的ACK,server就会重发FIN- 这时可能出现的情况是:
client没有任何响应,服务器那边会干等,甚至多次重发FIN,浪费资源client有个新的应用程序刚好分配了同一个端口号,新的应用程序收到FIN后马上执行断开连接的操作,本来它可能是想跟server建立连接的
- 这时可能出现的情况是:
疑问
- 为什么释放连接的时候, 要进行
4次挥手?TCP是全双工模式- 第
1次挥手:当主机1发出FIN报文段时- 表示主机
1告诉主机2,主机1已经没有数据要发送了,但是,此时主机1还是可以接受来自主机2的数据
- 表示主机
- 第
2次挥手:当主机2返回ACK报文段时- 表示主机
2已经知道主机1没有数据发送了,但是主机2还是可以发送数据到主机1的
- 表示主机
- 第
3次挥手:当主机2也发送了FIN报文段时- 表示主机
2告诉主机1,主机2已经没有数据要发送了
- 表示主机
- 第
4次挥手:当主机1返回ACK报文段时- 表示主机
1已经知道主机2没有数据发送了。随后正式断开整个TCP连接
- 表示主机