可以先按照较为理想情况学习。学完后在看待握手和挥手的问题时,要假设每一个消息都可能丢失。
另外握手要特别注意服务端为连接分配资源的问题。
三次握手
客户端:发起连接请求
服务端:分配资源,将其加入半连接队列,响应 ACK
客户端:分配资源,响应 ACK。可附带数据。
SYN 攻击(DDoS 攻击的一种)所用的客户端只执行第一步,不执行第三步。使得流程只执行到第二步。
如果半连接队列满了,新来的 SYN 请求会被丢弃。
第三步的另一个作用:
由于服务端很晚才收到客户端的连接请求,所以客户端发现第一步的请求超时,会再次发送。如果没有第三步,先前发送的连接请求有可能在连接关闭后才到达服务端,此时服务端分配资源。但由于服务端发送 ACK 后客户端没有执行第三步(因为客户端确实不想要再连接了),服务端在等待超时后释放资源。
四次挥手
客户端:发送关闭请求,表示无更多请求。
服务端:收到客户端关闭请求。服务端继续返回数据。
服务端:发送关闭请求,表示无更多数据可以返回。
客户端:收到服务端关闭请求,发送确认。等待 2MSL 后关闭。
这个表示无更多请求的行为,可以联想到 Golang 中关闭 Channel。关闭 Channel 只表示不会继续往 Channel 里面存放数据,但仍然可以被消费,直到 Channel 为空。
为什么等 2MSL(Maximum Segment Lifetime)
超过 MSL 的报文会被丢弃。
因为正常情况下服务端要等待客户端的 LAST-ACK 才会关闭连接。如果客户端太早关闭连接,就会使得服务端收不到 LAST-ACK。
客户端如何尽可能地确保服务端能收到 LAST-ACK?那就是等待一段时间(2MSL)。因为服务端在发送 FIN 后,会等待接收 LAST ACK,同时设置一个超时重传等待时间(RTO)。如果时间到了服务端没有收到客户端的 LAST-ACK,就会重传 FIN。
客户端会在 2MSL 之内收到服务端的重传。如果超过了 2MSL 客户端还没有收到服务端的 FIN 重传,则客户端默认服务端已收到了 LAST-ACK。
客户端每次收到服务端重传并发送 LAST-ACK 后,会重制计时。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
| +---------+ ---------\ 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 |
+---------+ +---------+
|
https://tools.ietf.org/html/rfc793
如果服务端一直重传,在客户端 2MSL 后也没有收到 LAST-ACK ,会怎么样?
服务端会在发送 FIN 后,碰到以下三种情况的任意一种时关闭连接:
- 收到客户端的 LAST-ACK
- 收到客户端的 RST
- 客户端发送 LAST-ACK 没有被服务端收到,并且 2MSL 内没有收到服务端的 FIN,关闭了连接。 2MSL 后收到服务端的 FIN 时,发送 RST。
- 重传超时
参考
https://zhuanlan.zhihu.com/p/86426969
https://www.zhihu.com/question/27564314/answer/162476313
https://www.jianshu.com/p/ff26312e67a9