不要再让面试官问三次握手和四次挥手了

记住一句话:面试时越简单的问题,一般就是隐藏着比较大的坑,一般都是需要将问题扩展的。

三次握手

三次握手 (Three-way Handshake)其实就是指建立一个TCP连接时,需要客户端和服务端总共发送 3 个包。进行三次握手的主要作用是为了确认双方的接受能力和发送能力是否正常,并指定自己的初始化序列号、为后面的可靠性传送做准备。
实质上就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。

刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态;

进行三次握手

  • 第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN(c)。此时客户端处于 SYN_SEND 状态;
    首部的同步位SYN=1,初始序号seq=xSYN=1的报文段不能携带数据,但要消耗掉一个序号。
  • 第二次握手:服务端收到客户端的 SYN 报文之后,会以自己的SYN报文作为应答,并且也指定了自己的初始化序列号 ISN(s)。同时会把客户端的ISN+1作为ACK的值,表示自己已经收到了客户端的SYN。此时服务器处于SYN_REVD的状态。
    在确认报文段中SYN=1,ACK=1,确认号ack=x+1,初始序号seq=y
  • 第三次握手:客户端收到SYN报文之后,会发送一个ACK报文,当然也是一样把服务器的ISN+1作为ACK的值,表示已经收到了服务器的SYN报文。此时客户端处于ESTABLISHED状态。
    服务器收到ACK报文之后,也处于ESTABLISHED状态。此时双方已建立起了连接。

确认报文段ACK=1,确认号ack=y+1,序号seq=x+1(初始为seq=x,所以第二个报文段要+1),ACK报文段可以携带数据,不携带数据则不消耗序号。

发送第一个SYN的一端将执行主动打开,接收这个SYN并发回下一个SYN的另一端执行被动打开。

socket编程中,客户端执行connect()时,将触发三次握手。

三次握手.png
  1. 为什么需要三次握手,两次不行吗?
    弄清楚这个问题,首先需要搞明白三次握手的目的是什么,能不能只用两次握手来达到同样的目的。

    • 第一次握手:客户端发送网络包,服务端收到了。
      这样服务器就能得出结论:客户端的发送能力、服务器端的接收能力是正常的。
    • 第二次握手:服务端发包,客户端收到了。
      这样客户的就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。
      不过,此时的服务器并不能确认客户端的接收能力是否正常
    • 第三次握手:客户端发包,服务器收到了。
      这样服务端就能得到结论:客户端的接收、发送能力正常,自己的发送、接收能力也正常。

    故:需要三次握手才能确认双方的接收与发送能力是否正常!

    试想一下,如果是用两次握手,则会出现以下情况:

客户端发送连接请求,但因连接请求报文丢失而未收到确认,于是客户端再次发送一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文,其中第一个丢失,第二个到达了服务端,但第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端,此时服务端误认为客户端又发来了一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接,不采用三次握手,只要服务端发出确认,新的连接就已经建立了。而此时客户端忽略了服务端发来的确认包,也不发送数据,则服务端一直等待客户端发送数据,浪费服务器资源。

  1. 什么是半连接队列?
    服务器第一次收到客户端的SYN之后,就会处于SYN_RCVD状态,此时双方还没有完全建立起连接,服务器会把此种状态下的请求连接放入一个队列中,此队列称之为半连接队列。

    当然还有一个全连接队列,就是已经完成了三次握手,建立起的连接被放入的队列。如果队列满了,就可能会出现丢包现象。

    关于SYN_ACK重传次数的问题:

服务器发送完SYN_ACK后,如果未收到客户端确认包,服务端进行首次重传;等待一段时间仍未收到确认包,则进行第二次重传;如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
注意:每次重传所等待的时间不一定相同,一般会是指数增长,如1s、2s、4s、8s ...

  1. ISN是固定的吗?
    当一端为建立连接而发送它的SYN时,它为连接选择一个初始序号。ISN随时间而变化,因此每个连接都将具有不同的ISN

    ISN可以看作是一个32bit的计数器,每4ms1。这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,而导致某个连接的一方对它做错误的解释。

    三次握手中的一个重要功能是客户端和服务端交换ISN(Initial Sequence Number),以便让对方知道接下来接收数据时如何按序列号组装数据。如果ISN是固定的,攻击者很容易猜出后续的确认号,因此ISN是动态生成的。

  2. 三次握手过程中可以携带数据吗?
    第三次握手可以携带数据。但前两次是不可以的。

试想一下,假如第一次可以携带数据,如果有人要恶意攻击服务器,那他每次都在第一次握手中的SYN报文中放入大量的数据。因为攻击者根本不关心服务器的接收、发送能力是否正常,然后疯狂重发SYN报文,这会让服务器花费大量时间和内容空间来接收报文。
也就是说,第一次握手不可以放数据,其中一个最简单的原因就是会让服务器更容易收到攻击。
而第三次握手时,客户端已经处于ESTABLISHED状态,对客户端来说,连接已经建立了,也确认服务端的接收、发送能力正常,所以能携带数据。

  1. SYN攻击是什么?
    服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易收到SYN泛洪攻击。

SYN攻击就是客户端在短时间内伪造大量不存在的IP地址,并向服务器不断地发送SYN包,服务端则回复确认包,并等待客户端确认。由于源地址不存在,因此服务端需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。
SYN攻击是一种典型 DoS/DDoS 的攻击。

检测SYN攻击非常方便,当你在服务器上看到大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。在Linux/Unix上可以使用系统自带的 netstats 命令来检测SYN攻击:
netstats -n -p TCP | grep SYN_RECV

常见的防御措施:

  • 缩短超时时间(SYN Timeout)
  • 增加最大半连接数
  • 过滤网关防护
  • SYN cookies技术

四次挥手

建立一个连接需要三次握手,而终止一个连接要经过四次挥手(也有人叫四次握手)。这是由TCP的半关闭(half-close)造成的。
所谓半关闭,其实就是TCP提供了连接的一端在它结束自己的发送后,还能接受来自另一端数据的能力。

TCP连接的拆除需要发送四个包,因此称为四次挥手 (Four-way handshake),客户端或服务器均可主动发起挥手动作。

刚开始双方都处于ESTABLISHED状态,加入是客户端先发起关闭请求,四次挥手的过程如下:

  • 第一次挥手:客户端发送一个FIN报文,报文中会指定一个序列号。此时客户端处于FIN_WAIT1状态;

    即发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。

  • 第二次挥手:服务端收到FIN包之后,会发送ACK报文,且把客户端的序列号值+1 作为ACK报文序列号值,表明已经收到客户端的报文了,此时服务端处于CLOSE_WAIT状态;

    即服务端收到连接释放的报文段后,立即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段;

  • 第三次挥手:如果服务端也想断开连接,和客户端的第一次挥手一样,发送FIN报文,且指定一个序列号。此时服务端处于LAST_ACK状态。

    即服务端没有要向客户端发送的数据,服务端发出连接释放的报文段(FIN=1ACK=1,序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端确认。

  • 第四次挥手:客户端收到FIN之后,一样发送一个ACK报文作为应答,且把服务端的序列号值+1 作为自己的ACK报文的序列号值,此时客户端处于TIME_WAIT状态,需要过一阵子才会进入CLOSED状态,为了确保服务端收到自己的ACK报文。服务端收到ACK报文之后,就会关闭连接,也进入CLOSED状态。

    即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计数器设置的时间2MSL后,客户端才会进入CLOSED状态。

收到一个FIN只意味着这一方向上没有数据流动,客户端执行主动关闭并进入TIME_WAIT状态是正常,服务端通常执行被动关闭,不会进入TIME_WAIT状态。

socket编程中,任何一方执行 close() 操作即可产生回收操作。

四次挥手.png
  1. 挥手为什么需要四次?
    当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文,其中ACK报文是用来应答的,SYN报文是用来同步的。
    但关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端:“我收到了你发的FIN报文”。只有等待服务端所有的报文都发送完了,才能发送FIN报文,因此不能一起发送ACK+FIN
    故需要四次挥手。

  2. 2MSL等待状态
    TIME_WAIT状态也称为2MSL等待状态。每个具体TCP实现必须选择一个报文段的最大生存时间--MSL(Maximum Segment Lifetime),它是任何报文段被被丢弃前在网络中的最长时间。这个时间是有限的,因为TCP报文段以IP数据包在网络中传输,而IP数据包有限制其生存时间的TTL字段。

    对一个具体实现所给定的MSL值,处理原则是:当TCP执行一个主动关闭,并发回最后一个ACK时,该连接必须在TIME_WAIT状态停留的时间为2倍的MSL。这样可让TCP再次发送最后的ACK,以防这个ACK丢失(另一端超时并重发最后的FIN)。

    这种2MSL等待的另一个结果是,这个TCP连接在2MSL等待期间,定义这个连接的插口(客户端的IP地址和端口号,服务器的IP地址和端口号)不能再被使用。这个连接只能在2MSL结束后才能再被使用。

  3. 四次挥手释放连接时,等待2MSL的意义?
    2MSL:Maximum Segment Lifetime,最长报文段寿命,它是任何报文在网络上存在的最长时间,超过这个时间的报文将被丢弃。

    为了保证客户端发送的最后一个ACK报文段能到达服务器。因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK报文,接着客户端再重传一次确认,重新启动时间等待计时器。最后客户端和服务端都能正常的关闭。
    假设客户端不等待2MSL,而是在发送完ACK之后直接释放关闭,一旦这个ACK丢失,那么服务器就无法正常的进入关闭连接状态。

    两个理由:

    • 保证客户端发送的最后一个ACK报文段能够到达服务端。
      这个ACK报文段有可能丢失,使处于LAST_ACK状态的服务器收不到对已发送的FIN-ACK报文段的确认。那么,服务端将会超时重传FIN-ACK报文段。而客户端能在2MSL时间内收到这个重传的FIN-ACK报文段,接着客户端也会重传一次确认ACK报文,重新启动2MSL计时器,最后客户端和服务端都进入CLOSED状态。
      如果客户端在TIME_WAIT状态不等待一段时间,而是在发完ACK报文段后立即释放连接,则无法收到服务端重传的FIN-ACK报文段,所以不会再发送一次ACK报文端,导致服务端无法正常进入CLOSED状态。
    • 防止“已失效的连接请求报文段”出现在本地连接中。
      客户端在发送完最后一个ACK报文段后,再经过2MSL,就可以使本次连接持续的时间内所产生的所有报文段都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文段。
  4. 为什么TIME_WAIT状态需要经过2MSL才能返回到CLOSED状态?
    理论上,四个报文都发送完毕就可以直接进入CLOSED状态了,但可能网络是不可靠的,有可能最后一个ACK丢失,所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。

总结

《TCP/IP详解 卷1:协议》有一张TCP状态变迁图,很具有代表性,有助于理解三次握手和四次挥手的状态变化。
如下图所示,粗的实线箭头表示正常的客户端状态变迁,粗的虚线箭头表示正常的服务器状态变迁。

TCP状态变迁图.jpg
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容