图解TCP通信三次握手和四次挥手

1.TCP报文格式
TCP报文格式如下图

图中有几个字段说明如下:
  (1)序号:seq,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。
  (2)确认号:ack,占32位,只有ACK标志位为1时,确认号字段才有效,ack=seq+1。
  (3)标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:
  (A)URG:紧急指针(urgent pointer)有效。
  (B)ACK:确认序号有效A。仅当ACK=1的时候,确认字号(期望收到对方下一个报文段的第一个数据字节的编号)才有效。因此,TCP规定,当链接建立之后,所有往来的报文里面的ACK都应该是1(事实上,也只有客户端发起的链接请求报文的ACK没有置1)。
  (C)PSH:接收方应该尽快将这个报文交给应用层。
  (D)RST:重置连接。
  (E)SYN:发起一个新连接。在链接的时候创建一个同步序号,当SYN=1同时ACK=0的时候,表明这是一个连接请求的报文段。如果对方有意链接,返回的报文里面SYN=1,ACK=1,。从这个意义上来说,SYN=1的时候,就表明这是一个‘请求’或者‘接受请求’的报文。
  (F)FIN:释放一个连接。当FIN=1的时候,表明此报文的发送方已经完成了数据的发送,没有新的数据要传送,并要求释放链接。
注意:
(1)不要将确认序号ack与标志位中的ACK搞混了。
(2)确认方ack=发起方seq+1,两端配对。

2.三次握手详解
所谓三次握手(Three-Way Handshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。在socket编程中,这一过程由客户端执行connect来触发,整个流程如下图所示:


(1)第一次握手
客户端发起,向服务器发送报文 SYN=1,ACK=0,初始序号seq=x。
客户端进入SYN_SENT状态。

(2)第二次握手
服务器收到SYN=1的请求,知道客户端请求建立连接,服务器向客户端返回SYN=1,ACK=1,同时确认号ack=x+1,同时也为自己选择一个初始序号seq=y。
服务器进入SYN_RCVD状态。

(3)第三次握手
客户端接收到了服务器的返回信息之后,检查ack是否为x+1,ACK是否为1,还要给服务器返回最后一条确认,ACK=1,确认号ack=y+1。
客户端进入ESTABLISHED状态。
服务器收到数据包后,检查ack是否为y+1,ACK是否为1,如果正确则连接建立成功,服务器进入ESTABLISHED状态,完成三次握手,随后客户端与服务器之间可以开始传输数据了。

为什么两次握手不行,非得三次?
首先说明一种正常的情况,就是客户端发送了一条请求链接的报文,但是由于网络原因丢失了,所以,不可能接收到服务器端的确认。这个时候,客户端就就只有再一次发送原来的请求报文,这次服务器收到之后返回确认,客户端再确认一次,链接确立。

然后考虑一种不正常的情况,客户端发了两次请求链接的报文,第二条被服务器捕捉到,返回数据,完成了两次握手。数据传送完成之后,链接关闭。但是这时候,第一条拥塞的请求报文现在到达了服务器端,服务器还以为客户端要又一次建立连接,于是发送确认,然后把自己敞开,等着客户端发送过来数据。于是,很多的网络资源就是这样浪费掉了。

要是实行三次握手,服务器收到了一条过期的请求报文,返回确认信息,客户端接收到了服务器的信息之后感到莫名其妙,心想:我他妈又没要链接,你返回这个是不是疯了。于是不置一词。服务器过一段时间还没有收到第三次握手的数据,知道客户端并没有要求建立链接的请求,含泪离开。

SYN攻击
在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了,使用如下命令可以让之现行:

#netstat -nap | grep SYN_RECV
3.四次挥手详解
所谓四次挥手(Four-Way Wavehand)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务器总共发送4个包以确认连接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发,整个流程如下图所示:


由于TCP连接时全双工的,因此,每个方向都必须要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。
(1)第一次挥手
客户端发起请求,请求断开链接。FIN=1,seq=u。u是之前传送过来的最后一个字节的序号+1。
客户端进入FIN_WAIT_1状态 ,等着服务器返回确认。

(2)第二次挥手
服务器收到客户端的请求断开链接的报文之后,返回确认信息。ACK=1,seq=v,ack=u+1。
服务器进入CLOSE_WAIT状态。
这个时候,客户端不能给服务器发送信息报文,只能接收。但是服务器要是还有信息要传给服务器,仍然能传送。

(3)第三次挥手
当服务器也没有了可以传送的信息之后,给客户端发送请求关闭连接的报文。FIN=1,ACK=1,ack=u+1,seq=w。
服务器进入LAST_ACK状态。

(4)第四次挥手
客户端收到FIN后,接着发送一个ACK给服务器,确认序号为收到序号+1,
客户端收到FIN=1的报文之后,进入TIME_WAIT状态。向服务器返回确认报文,ACK=1,seq=u+1,ack=w+1。发送完毕之后,客户端进入等待状态,等待两个时间周期,关闭。服务器收到确认报文后,进入CLOSED状态。完成四次挥手。


为什么最后还要等待两个时间周期呢?

1、客户端的最后一个ACK报文在传输的时候丢失,服务器并没有接收到这个报文。这个时候服务器就会超时重传这个FIN消息,然后客户端就会重新返回最后一个ACK报文,等待两个时间周期,完成关闭。如果不等待这两个时间周期,服务器重传的那条消息就不会收到。服务器就因为接收不到客户端的信息而无法正常关闭。


2、预防上一次在三次握手中提到的失效的报文干扰。两个时间周期过去之后,所有的报文都会在网络中消失,保证下一次重新连接的时候有乱七八糟的报文影响。

为什么建立连接是三次握手,而关闭连接却是四次挥手呢?
这是因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值