TCP为什么是三次握手,而不是两次或四次?

三旺通信


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

TCP是传输控制协议。

  • syn是该协议中的一个标志位。如果该位被置为1,则表示这个报文是一个请求建立连接的报文。
  • ack也是该协议的一个标志位。如果该位被置为1,则表示这个报文是一个用于确认的报文。

seq是序列号,这是为了连接以后传送数据用的,ack是对收到的数据包的确认,值是等待接收的数据包的序列号。在第一次消息发送中,A随机选取一个序列号x作为自己的初始序号发送给B;第二次消息B使用ack对A的数据包进行确认,因为已经收到了序列号为x的数据包,准备接收A序列号为x+1的包,所以ack=x+1,同时B告诉A自己的初始序列号,就是seq=y;第三条消息A告诉B收到了B的确认消息并准备建立连接,A自己此条消息的序列号是x+1,所以seq=x+1,而ack=y+1是表示A正准备接收B序列号为y+1的数据包。seq是数据包本身的序列号;ack是期望对方继续发送的那个数据包的序列号。

(1)第一次握手:Client将标志位SYN((同步序列编号(Synchronize Sequence Numbers))置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。

(2)第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。

(3)第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。 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


石墨烯大傅


你听到了吗?

听到了,你呢?

我也听到了

三次之后确保双方能通信,少哪句都是不通的


O0o0o0o0O


TCP的三次握手,基本算上是BAT面试过程的高频考点了。基本是程序员面试必备知识点了,无论你是前端还是后台开发,下面是一张简化版的TCP状态图,如果需要高清版,可关注私信或留言即可。


下面言归正传,对题主的问题进行回答。首先我们需要明白到底什么是三次握手!

什么是三次握手?

下面的过程来自维基百科的解释:

  1. 客户端通过向服务器端发送一个SYN来创建一个主动打开,作为三次握手的一部分。客户端把这段连接的序号设定为随机数A。

  2. 服务器端应当为一个合法的SYN回送一个SYN/ACK。ACK的确认码应为A+1,SYN/ACK包本身又有一个随机产生的序号B。

  3. 最后,客户端再发送一个ACK。此时包的序号被设定为A+1,而ACK的确认码则为B+1。当服务端收到这个ACK的时就完成了三次握手,并进入了连接创建状态。


为什么是三次,而不是二次或者四次?

还记得我们初中数学常用的反证法吗?在这里同样适用。

0x01. 假设TCP连接过程采用「四次握手」,我们来模拟下其过程:

  1. A 发送同步信号SYN + A'sInitial sequence number

  2. B 确认收到A的同步信号,并记录A's ISN 到本地,命名 B's ACK sequence number

  3. B发送同步信号SYN + B's Initial sequence number

  4. A确认收到B的同步信号,并记录B's ISN 到本地,命名 A's ACK sequence number


很显然,步骤2和步骤3重复了,可以合并,只需三次握手即可完成,否则会造成网络资源的浪费。


0x02. 假设TCP连接过程采用「二次握手」,模拟过程如下:

  1. A 发送同步信号SYN + A'sInitial sequence number

  2. B发送同步信号SYN + B'sInitial sequence number + B's ACK sequence number


看出问题了吧,A的初始序列号在A、B之间达成了一致,但是B无法知道A是否已经接收到自己的同步信号,如果这个同步信号丢失了,A和B就B的初始序列号将无法达成一致。


所以,TCP的设计者才选用了三次握手机制。


一个程序员的奋斗史


一、三次握手

三次握手:在通信之前,会先通过三次握手的机制来确认两端口之间的连接是否可用。而UDP不需要确认是否可用,直接传。

三次握手机制

一开始客户端和服务端都是关闭状态,但是在某个时刻,客户端需要和服务端进行通信,此时双方都会各自准备好端口,服务器段的端口会处于监听状态,等待客户端的连接。

客户端可会知道自己的端口号,和目的进程的端口号,这样才能发起请求。

第一次握手:客户端想与服务器进行连接了,所以状态变为主动打开,同时发送一个连接请求报文给服务器段SYN=1,并且会携带x个字节过去。

发送完请求连接报文后,客户端的状态就变为了SYN_SENT,可以说这个状态是等待发送确认(为了发送第三次握手时的确认包)

第二次握手:服务端接收到连接请求报文后,从LSTTEN状态变为被动打开状态,然后给客户端返回一个报文。这个报文有两层意思,一是确认报文,而可以达到告诉客户端,我也打开连接了。

发完后,变为SYN_RCVD状态(也可以说是等待接受确认状态,接受客户端发过来的确认包)

第三次握手:客户端得到服务器端的确认和知道服务器端也已经准备好了连接后,还会发一个确认报文到服务器端,告诉服务器端,我接到了你发送的报文,接下来就让我们两个进行连接了。

客户端发送完确认报文后,进入ESTABLISHED,而服务器接到了,也变为ESTABLISHED

进入到ESTABLISHED状态后,连接就已经完成了,可以进行通信了。

问题:为什么需要第三次握手,有前面两次不就已经可以了吗?

假设没有第三次握手,客户端发送一个连接请求报文过去,但是因为网络延迟,在等待了一个超时时间后,客户端就会再重新发一个请求连接报文过去,然后正常的进行;

服务器端发回一个确认连接报文,然后就开始通讯,通讯结束后,那个第一次因为网络延迟的请求连接报文到了服务器端,服务器端不知道这个报文已经失效,也发回了一个确认连接报文。

客户端接收后,发现自己并没有发送连接请求(因为超时了,所以就认为自己没有发),所以对这个确认连接请求就什么也不做,但是此时客户端不这么认为,他认为连接已经建立了,就一直打开着等待客户端传数据过来,这就造成了极大的浪费。

如果有了第三次握手,那么客户端就可以通知服务器了,所以第三次握手也很重要!

二、四次挥手

四次挥手是用来断开服务器和客户端之间的通信的,之所以要断开连接,是因为TCP/IP 协议是要占用端口号的,而计算机的端口却是有限的,不进行断开的话,势必会造成计算机资源的浪费。

1、在整个通信的过程中,谁先发起请求,谁就是客户端。

当客户端的数据传输到尾部时,客户端向服务器发送带有FIN标志的数据包,使其明白自己准备断开通信了。

2、因为TCP的通信是使用全双工通信的WebSocket,所以在断开连接的时候也应该是双向的;当服务器收到带有FIN标志的数据包时,其必不会直接发送FIN标志断开通信的请求,而是先发送一个带有ACK标志的应答信息,使客户端明白服务器还有数据要进行发送。

3、当 服务器的数据发送完成后,向客户端发送带有FIN标志的数据包,通知客户端断开连接。

4、这一次挥手是我觉得四次挥手中设计的最巧妙的一次。

当客户端收到FIN后,担心网络上某些不可控制的因素导致服务器不知道他要断开连接,会发送ACK进行确认,同时把自己设置成TIME_WAIT状态并启动定时器,**在TCP的定时器到达后客户端并没有接收到请求,会重新发送;当服务器收到请求后就断开连接;当客户端等待2MLS(两倍报文最大生存时间)后,没有收到请求重传的请求后,客户端这边就断开连接,**整个TCP通信就结束了。

四次挥手的图如下所示:

问题:关闭的时候为什么会是四次挥手?

四次挥手不能像三次握手一样,三次握手可以将ACK+SYN 一起发送,ACK用于确认信息,SYN却是用来建立联机的;

四次挥手中ACK是不能和FIN一起发送,ACK只是告诉客户端确认我收到了,等我将数据发送完毕之后会向其发送FIN的标志,所以四次挥手是不能够改变的。


三旺通信


网上有很多文章讲TCP为什么建立连接时需要三次握手,关闭连接时需要四次握手,讲了很多原理,反而让很多人难以理解。

其实只有一句话:TCP连接是两个端点之间的事,由于TCP连接是可靠连接,所以不管是建立连接还是关闭连接,需要两个端点都要发送请求和收到确认

其次要理解TCP的通道是全双工的,是可以读和写数据的,理解这个之后就明白了为什么关闭连接时需要四次握手。

首先讲三次握手,建立连接时不涉及到读写通道,只是两个端点的请求和确认。要记住是两个端点之间的,两个端点是平等的关系。

从客户端的角度来说,它只需要发送一个请求syn,然后收到ack就能知道连接是可以建立的

从服务器端的角度,它也只需要发送一个请求syn,然后收到ack就能知道连接是可以建立的

所以建立连接时最少只需要三次握手即可,即服务器端收到客户端syn,之后向客户端发送ack的同时携带上自己的syn,这样就只需要三次握手就能使双方都发生syn和都收到ack.

当然你自己设计的时候也可以使用4次或者更多的握手,但是最少3次即可

关闭连接时也还是两个端点都要发送请求和收到确认。但是关闭连接时涉及到两个端点的读写通道的事。

从客户端的角度来说,它发送一个关闭请求FIn,然后收到ack就知道可以关闭连接的

从服务器的角度来说,它发送一个关闭请求Fin,然后收到ack就知道可以关闭连接的

但是由于是读和写通道,所以服务器端在收到客户端的关闭Fin时,可能还在往客户端发送消息,所以只能先发送ack确认客户端客户关闭写通道,这时候服务端不能使用通过一消息发送它的关闭Fin,只能过会等它自己写完了,才能重新发送一个关闭请求的Fin。

所以比建立连接时多了一次握手,关闭连接时最少需要4次握手。理解了为什么需要4次握手之后,就不难理解TCP半关闭的概念。


程序猿之奇异世界


分析

一个虚拟连接的建立是通过三次握手来实现的

1.(Client)[SYN](Server)

假如Client和Server通讯. 当Client要和Server通信时,Client首先向Server发一个SYN (Synchronize) 标记的包,告诉Server请求建立连接.

注意: 一个 SYN包就是仅SYN标记设为1的TCP包(参见TCP包头Resources). 认识到这点很重要,只有当Server收到Client发来的SYN包,才可建立连接,除此之外别无他法。因此,如果你的防火墙丢弃所有的发往外网接口的SYN包,那么你将不 能让外部任何主机主动建立连接。

2. (Client) [SYN/ACK] (Server)

接着,Server收到来自Client发来的SYN包后,会发一个对SYN包的确认包(SYN/ACK)给Client,表示对第一个SYN包的确认,并继续握手操作.

注意: SYN/ACK包是仅SYN 和 ACK 标记为1的包.

3.(Client) [ACK] (Server)

Client收到来自Server的SYN/ACK 包,Client会再向Server发一个确认包(ACK),通知Server连接已建立。至此,三次握手完成,一个TCP连接完成。

Note: ACK包就是仅ACK 标记设为1的TCP包. 需要注意的是当三此握手完成、连接建立以后,TCP连接的每个包都会设置ACK位。

四次挥手用来关闭已建立的TCP连接

· (Client) ACK/FIN (Server)

· (Client) ACK (Server)

· (Client) ACK/FIN (Server)

· (Client) ACK (Server)


小福的故事


为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤。

如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认

以下图1是正确握手,图2、3是如果是2次示例





子阙创客


第一次握手:客户端发送TCP包,置SYN标志位为1,将初始序号X,保存在包头的序列号(Seq)里。

第二次握手:服务端回应确认包,置SYN标志位为1,置ACK为X+1,将初始序列号Y,保存在包头的序列号里。

第三次握手:客户端对服务端的确认包进行确认,置SYN标志位为0,置ACK为Y+1,置序列号为Z。

为什么不是两次

重新看一遍图(S-服务端,C-客户端)

第一次握手后,S可以确认自己收报文与C发报文的功能都正常,而C呢,它什么都不能确认。

第二次握手后,C可以确认自己的收发报文与S的收发报文功能都正常,也就是认为连接已建立。

那么第三次呢,S也可以确认双方能够正常通信。

假想一下,如果我们去掉了第三次呢?

如果只是第二次建立的话,服务端和客户端就已经建立,但是如果客户端没有收到服务端的回应?这个时候,客户端认为没有建立,服务端却为认为建立成功,并保存了必要的资源,如果出现大量的这样的情况。那么服务器会奔溃。

因此第三次握手是必要的。

为什么不是四次

因为三次握手后,C和S至少可以确认之前的通信情况,但无法确认之后的情况。 所以如果四次还是五次或是更多次都是徒劳的



分享到:


相關文章: