java IO 工作机制(2)(补充1)(连接建立和断开的状态变化)

对英文字母缩写的总结:

Seq 是 Sequence 的缩写,表示序列;
Ack(ACK) 是 Acknowledge 的缩写,表示确认;
SYN 是 Synchronous 的缩写,是“同步的”,这里表示建立同步连接;
FIN 是 Finish 的缩写,表示完成。

带阴影的几个字段需要重点说明一下:

1) 序号:Seq(Sequence Number)序号占32位,用来标识从计算机A发送到计算机B的数据包的序号,计算机发送数据时对此进行标记。

2) 确认号:Ack(Acknowledge Number)确认号占32位,客户端和服务器端都可以发送,Ack = Seq + 13) 标志位:每个标志位占用1Bit,共有6个,分别为 URG、ACK、PSH、RST、SYN、FIN,具体含义如下:
URG:紧急指针(urgent pointer)有效。
ACK:确认序号有效。
PSH:接收方应该尽快将这个报文交给应用层。
RST:重置连接。
SYN:建立一个新连接。
FIN:断开一个连接。

注意:有个Ack字段和ACK标志位是不同。

连接的建立(三次握手)

建立连接时,客户端和服务器端会相互发送三个数据包

客户端调用 socket() 函数创建套接字后,因为没有建立连接,所以套接字处于CLOSED状态;

服务器端调用 listen() 函数后,套接字进入LISTEN状态,开始监听客户端请求。

这个时候,客户端开始发起请求:

1) 当客户端调用 connect() 函数后,TCP协议会组建一个数据包,并设置 SYN 标志位,表示该数据包是用来建立同步连接的。
同时生成一个随机数字 1000填充“序号(Seq)”字段,表示该数据包的序号。
完成这些工作,开始向服务器端发送数据包客户端进入了SYN-SEND状态2) 服务器端收到数据包检测到已经设置了 SYN 标志位,就知道这是客户端发来的建立连接的“请求包”
服务器端也会组建一个数据包,并设置 SYN 和 ACK 标志位,SYN 表示该数据包用来建立连接,ACK 用来确认收到了刚才客户端发送的数据包。 服务器生成一个随机数
2000填充“序号(Seq)”字段2000 和客户端数据包没有关系。 服务器将客户端数据包序号(1000)加1,得到1001,并用这个数字填充“确认号(Ack)”字段。 服务器将数据包发出,进入SYN-RECV状态3) 客户端收到数据包检测到已经设置了 SYN 和 ACK 标志位,就知道这是服务器发来的“确认包”
客户端会检测“确认号(Ack)”字段,看它的值是否为 1000+1,如果是就说明连接建立成功。 接下来,客户端会继续组建数据包,并设置 ACK 标志位,表示客户端正确接收了服务器发来的“确认包”。
同时,将刚才服务器发来的数据包序号(
2000)加1,得到 2001并用这个数字来填充“确认号(Ack)”字段。 客户端将数据包发出,进入ESTABLISED状态,表示连接已经成功建立。 4) 服务器端收到数据包,检测到已经设置了 ACK 标志位,就知道这是客户端发来的“确认包”。
服务器会检测“确认号(Ack)”字段,看它的值是否为 2000+1,如果是就说明连接建立成功,服务器进入ESTABLISED状态。 至此,客户端和服务器都进入了ESTABLISED状态,连接建立成功,接下来就可以收发数据了。
最后的说明 三次握手的关键是要确认对方收到了自己的数据包,这个目标就是通过“确认号(Ack)”字段实现的。
计算机会记录下自己发送的数据包序号 Seq,待收到对方的数据包后,检测“确认号(Ack)”字段,看Ack
= Seq + 1是否成立,
如果成立说明对方正确收到了自己的数据包。

 

TCP四次握手断开连接

建立连接需要三次握手,断开连接需要四次握手,可以形象的比喻为下面的对话:

[Shake 1] 套接字A:“任务处理完毕,我希望断开连接。”
[Shake 2] 套接字B:“哦,是吗?请稍等,我准备一下。”
等待片刻后……
[Shake 3] 套接字B:“我准备好了,可以断开连接了。”
[Shake 4] 套接字A:“好的,谢谢合作。”

建立连接后,客户端和服务器都处于ESTABLISED状态。

这时,客户端发起断开连接的请求:

1) 客户端调用 close() 函数后,向服务器发送 FIN 数据包,进入FIN_WAIT_1状态
FIN 是 Finish 的缩写,表示完成任务需要断开连接。
2) 服务器收到数据包后,检测到设置了 FIN 标志位,知道要断开连接,于是向客户端发送“确认包”,进入CLOSE_WAIT状态。 注意:服务器收到请求后并不是立即断开连接,而是先向客户端发送“确认包”,告诉它我知道了,我需要准备一下才能断开连接。 3) 客户端收到“确认包”后进入FIN_WAIT_2状态等待服务器准备完毕后再次发送数据包。 4) 等待片刻后,服务器准备完毕,可以断开连接,于是再主动向客户端发送 FIN 包,告诉它我准备好了,断开连接吧。然后进入LAST_ACK状态5) 客户端收到服务器的 FIN 包后,再向服务器发送 ACK 包,告诉它你断开连接吧。然后进入TIME_WAIT状态6) 服务器收到客户端的 ACK 包后,就断开连接,关闭套接字,进入CLOSED状态

关于 TIME_WAIT 状态的说明

客户端最后一次发送 ACK包后进入 TIME_WAIT 状态,而不是直接进入 CLOSED 状态关闭连接,这是为什么呢?

TCP 是面向连接的传输方式,必须保证数据能够正确到达目标机器,不能丢失或出错,而网络是不稳定的,随时可能会毁坏数据,
所以机器A每次向机器B发送数据包后,都要求机器B”确认“,回传ACK包,告诉机器A我收到了,这样机器A才能知道数据传送成功了。
如果机器B没有回传ACK包,机器A会重新发送,直到机器B回传ACK包。 客户端最后一次向服务器回传ACK包时,有可能会因为网络问题导致服务器收不到,服务器会再次发送 FIN 包,
如果这时客户端完全关闭了连接,那么服务器无论如何也收不到ACK包了,
所以客户端需要等待片刻、确认对方收到ACK包后才能进入CLOSED状态。
那么,要等待多久呢? 数据包在网络中是有生存时间的,超过这个时间还未到达目标主机就会被丢弃,并通知源主机。
这称为报文最大生存时间(MSL,Maximum Segment Lifetime)。
TIME_WAIT 要等待 2MSL 才会进入 CLOSED 状态。
ACK 包到达服务器需要 MSL 时间,服务器重传 FIN 包也需要 MSL 时间,2MSL 是数据包往返的最大时间,
如果 2MSL 后还未收到服务器重传的 FIN 包,就说明服务器已经收到了 ACK 包。

 

posted @ 2018-08-14 02:26  假程序猿  阅读(433)  评论(0)    收藏  举报