TCP三次握手和四次挥手
参考
- http://www.imooc.com/article/17411
- https://blog.csdn.net/whuslei/article/details/6667471
- https://blog.csdn.net/smileiam/article/details/78226816
- https://blog.csdn.net/scuzoutao/article/details/81774100
各种标志解释
SYN(synchronous建立联机)
ACK(acknowledgement 确认)
PSH(push传送)
FIN(finish结束)
RST(reset重置)
URG(urgent紧急)
Sequence number(顺序号码)
Acknowledge number(确认号码)
状态迁移
客户端TCP状态迁移:
CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED
服务器TCP状态迁移:
CLOSED->LISTEN->SYN收到->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED
各个状态的意义如下:
LISTEN - 侦听来自远方TCP端口的连接请求;
SYN-SENT -在发送连接请求后等待匹配的连接请求;
SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认;
ESTABLISHED- 代表一个打开的连接,数据可以传送给用户;
FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;
FIN-WAIT-2 - 从远程TCP等待连接中断请求;
CLOSE-WAIT - 等待从本地用户发来的连接中断请求;
CLOSING -等待远程TCP对连接中断的确认;
LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认;
TIME-WAIT -等待足够的时间以确保远程TCP接收到连接中断请求的确认;
CLOSED - 没有任何连接状态
三次握手
- 最初两端的TCP都处在CLOSED(关闭)状态。
- B的TCP服务器进程创建传输控制块TCB,服务器进程进入LISTEN(收听)状态,等待客户的连接请求。
传输控制块:Transmission Control Block,TCB,存储连接中的信息。如:TCP连接表,到发送和接收缓存的指针,到重传队列的指针,当前发送和接收序号 - A的TCP客户进程创建传输控制块TCB,向B发出连接请求报文段。这时,首部中同步位SYN=1,初始序号seq=x。SYN报文段不携带数据,但要消耗一个序号。TCP客户进程进入SYN-SENT(同步已发送)状态。
- B收到连接请求报文段,如同意建立连接,则向A发送确认。确认报文段中,SYN和ACK都为1,确认号ack=x+1,并选择自己的初始序号seq=y。此报文段同样不携带数据,但要消耗一个序号。TCP服务器进程进入SYN-RCVD(同步接收)状态。
- TCP客户进程收到B的确认后,向B发出确认。确认报文段的ACK=1,确认号ack=y+1,自己的seq=x+1。ACK报文段可携带数据,不携带数据则不消耗序号。TCP连接已建立,A进入ESTABLISHED(已连接)状态。
- B收到A的确认,也进入ESTABLISHED状态。
A收到B的确认,为什么还要再次向B发送确认?
答:A向B发出连接请求报文段,如果此报文段在网络中长时间滞留,A误以为报文段丢失,会再次向B发送连接请求报文段(假设第二次请求连接建立成功)。当数据传输完成并释放连接后,B又收到A第一次发送的连接请求,B误以为A发起了一次新的连接请求,便会回复ACK包并且分配资源。并且一直等待client发送数据,这就会造成server的资源浪费。所以,A有必要让B知道是否发起了新的连接请求。
第三次握手失败了怎么办?
答:当client与server的第三次握手失败了之后,即client发送至server的确认建立连接报文段未能到达server,server在等待client回复ACK的过程中超时了,那么server会向client发送一个RTS报文段并进入关闭状态,即:并不等待client第三次握手的ACK包重传,直接关闭连接请求,这主要是为了防止泛洪攻击,即坏人伪造许多IP向server发送连接请求,从而将server的未连接队列塞满,浪费server的资源。
三次握手有什么缺陷可以被黑客利用,用来对服务器进行攻击?
答:黑客仿造IP大量的向server发送TCP连接请求报文包,从而将server的半连接队列(上文所说的未连接队列,即server收到连接请求SYN之后将client加入半连接队列中)占满,从而使得server拒绝其他正常的连接请求。即拒绝服务攻击。
怎么防范这种攻击?
答:1.缩短服务器接收客户端SYN报文之后的等待连接时间,即SYN timeout时间,也就是server接收到SYN报文段,到最后放弃此连接请求的超时时间,将SYN timeout设置的更低,便可以成倍的减少server的负荷,但是过低的SYN timeout可能会影响正常的TCP连接的建立,一旦网络不通畅便可能导致client连接请求失败。
2. SYN cookie + SYN proxy 无缝集成(较好的解决方案)
- SYN cookie:当server接收到client的SYN之后,不立即分配资源,而是根据client发送过来的SYN包计算出一个cookie值,这个cookie值用来存储server返回给client的SYN+ACK数据包中的初始序列号,当client返回第三次握手的ACK包之后进行校验,如果校验成功则server分配资源,建立连接。
- SYN proxy代理,作为server与client连接的代理,代替server与client建立三次握手的连接,同时SYN proxy与client建立好了三次握手连接之后,确保是正常的TCP连接,而不是TCP泛洪攻击,那么SYN proxy就与server建立三次握手连接,作为代理(网关?)来连通client与server。(类似VPN了解一下。)
四次挥手
- 开始时,A和B都处在ESTABLISHED状态。
- A的应用进程向TCP发出连接释放报文段,停止发送数据,关闭TCP连接。A把报文段首部中FIN置为1,序号seq=u,u是已传送的数据的最后一个字节序号加1。A进入FIN-WAIT-1(终止等待1)状态,等待B的确认。FIN报文段即使不携带数据,也要消耗一个序号。
- B收到连接释放报文段向A发出确认,确认号ack=u+1,B自己的序号是v,v是已传送过的数据的最后一个字节加1。B进入CLOSE-WAIT(关闭等待)状态。TCP服务器进程通知高层应用进程,从A到B的连接被释放。TCP进入半关闭(half-close)状态。
- A收到B的确认,进入FIN-WAIT-2(终止等待2)状态,等待B发出连接释放报文段。
- B应用进程通知TCP释放连接,报文段首部FIN=1,序号为w(半关闭状态时B可能又发送了一些数据),B重复上次发送的确认号ack=u+1。B进入LAST-ACK(最后确认)状态,等待A的确认。
- A收到B的连接释放报文段,向B发出确认。确认报文段中ACK=1,确认号ack=w+1,A自己的序号seq=u+1(发送的FIN报文段使用一个序号)。A进入TIME-WAIT(时间等待)状态。
- 经过时间等待计时器(TIME-WAIT timer)设置的时间2MSL后,A进入CLOSED状态。
最长报文段寿命:Maximum Segment Lifetime,MSL,TCP允许根据不同情况调整此值,RFC 793建议时间2分钟。 - B收到A的确认,进入CLOSED状态,撤销传输控制块TCB,TCP连接释放成功。
A为什么在TIME-WAIT状态等待2MSL时间?
- 保证A发送的最后一个ACK报文段能够到达B。若此报文段丢失,处在LAST-ACK状态的B收不到FIN+ACK报文段的确认,B将超时重传FIN+ACK报文段,A可在2MSL时间内收到重传的FIN+ACK报文段。A重传确认,重新启动2MSL计时器。保证A和B顺利进入CLOSED状态。
- 防止“已失效连接请求报文段”。A等待2MSL,可使本连接持续时间内所产生的所有报文段全部从网络中消失。
为什么连接的时候是三次握手,关闭的时候却是四次握手?
- 因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。