掌握TCP三次握手和四次挥手

引语

最近在重温计算机网络TCP/IP协议簇,TCP的三次握手四次挥手能够说是面试中有关计算机网络部分常常被拿出来问的部分,因此这里就作一个整理。javascript

TCP概念与特色

传输控制协议(TCP,Transmission Control Protocol)是一种面向链接的、可靠的、基于字节流的传输层通讯协议,由IETF的RFC 793定义。

TCP 是面向链接的传输层协议,面向链接是指发送数据以前必须在两端创建链接。创建链接的方法是“三次握手”。前端

TCP 提供可靠传输服务,实行“顺序控制”或“重发控制”机制。java

TCP提供全双工通讯,容许通讯双方的应用程序在任什么时候候都能发送数据。TCP链接的两端都设有缓存,用来临时存放双向通讯的数据。面试

TCP还具有“流控制(流量控制)”、“拥塞控制”、提升网络利用率等众多功能。当网络出现拥塞的时候,TCP可以减少向网络注入数据的速率和数量,缓解拥塞。算法

TCP仅支持单播传输,每条TCP传输链接只能有两个端点,只能进行点对点的数据传输,不支持多播和广播传输方式。编程

许多应用层协议都是基于TCP协议:segmentfault

  • FTP:文件传输协议;
  • SSH:安全登陆、文件传送(SCP)和端口重定向;
  • Telnet:不安全的文本传送;
  • SMTP:简单邮件传输协议Simple Mail Transfer Protocol (E-mail);
  • HTTP:超文本传送协议 (WWW);
  • 其余...

TCP报文段首部

TCP/IP协议的每一个分层中,都会对所发送的数据附加一个首部,在这个首部中包含了该层必要的信息,如发送的目标地址以及协议相关信息。一般,为协议提供的信息为包首部,所要发送的内容为数据。在下一层的角度看,从上一层收到的包所有都被认为是本层的数据。缓存

d1d5720c658a4479a7894bdaa072a05b.jpg

虽然TCP以字节流方式进行传输,但TCP传输的数据单元倒是报文段。TCP报文段分为TCP首部和数据部分,TCP报文段首部的前20个字节是固定的,后面有4*n字节根据须要动态添加的选项,最大长度为40字节。安全

因此要理解TCP协议,首先要了解TCP报文段的首部。服务器

page76image58697056.jpg

TCP报文段首部中各部分的含义:

  • 源端口和目的端口: 各占两个字节,TCP的分用功能也是经过端口实现的。
  • 序号 占4个字节,范围是[0,2^32],TCP是面向字节流的,每一个字节都是按顺序编号。例如一个报文段,序号字段是201,携带数据长度是100,那么第一个数据的序号就是201,最后一个就是300。当达到最大范围,又从0开始。
  • 确认号: 占4个字节,是指望收到对方下一个报文段的第一个字节的序号。若确认号=N,则表示序号N前全部的数据已经正确收到了。
  • 数据偏移: 占4位,表示报文段的数据部分的起始位置,距离整个报文段的起始位置的距离。间接的指出首部的长度。
  • 保留: 占6位,保留使用,目前为0.
  • 标志位:共6个,即URG、ACK、PSH、RST、SYNFIN等,具体含义

    • URG(紧急): 当URG=1,代表紧急指针字段有效,该报文段有紧急数据,应尽快发送。
    • ACK(确认): 仅当ACK=1时,确认号才有效,链接创建后,全部的报文段ACK都为1。
    • PSH(推送): 接收方接收到PSH=1的报文段,会尽快交付接收应用常常,再也不等待整个缓存填满再交付。实际较少使用。
    • RST(复位): RST=1时,代表TCP链接中出现严重差错,必须是否链接,再重连。
    • SYN(同步): 在创建链接时用来同步序号。当SYN=1,ACK=0,则代表是一个链接请求报文段。SYN=1,ACK=1则表示对方赞成链接。TCP创建链接用到。
    • FIN(终止): 用来释放一个链接窗口。当FIN=1时,代表此报文段的发送方再也不发送数据,请求释放单向链接。TCP断开链接用到。
  • 窗口: 占2个字节,表示发送方本身的接收窗口,窗口值用来告诉对方容许发送的数据量。
  • 校验和: 占2字节,检验和字段查验范围包括首部和数据部分。
  • 紧急指针: 占2字节,URG=1时,紧急指针指出本报文段中的紧急数据的字节数(紧急字节数结束后为普通字节)。
  • 选项: 长度可变,最长可达40字节。例如最大报文段长度MSS。MSS指的是数据部分的长度而不是整个TCP报文段长度,MSS默认为536字节长。窗口扩大,时间戳选项等。

须要注意的是

  1. 不要将确认序号Ack与标志位中的ACK搞混了。
  2. 确认方Ack=发起方Req+1,两端配对。

TCP的三次握手与四次挥手

开始以前,咱们先来说两个故事吧。

故事一:三次牵手
男:我喜欢你。
女:我知道了,我也喜欢你。
男:我知道了,那咱们在一块儿吧。

故事二:四次分手
男:我不喜欢你了,我要和你分手。
女:我知道到了,等我先打完这把王者荣耀再说。
女:我打完了,我也不喜欢你了,我要和你分手。
男:我知道了,赞成分手。

这真是两个有意思的故事,那么接下去转入正题。

序列号与确认号的含义

ACK=1:确认序号有效;
SYN=1:发起一个新链接;
FIN=1:释放一个链接;
seq = x:本报文段发送的数据的第一个字节的序号
ACK=1, ack = y:期待收到对方下一个报文段的第一个字节的序号。

举个例子:

  1. 序列号为2000且长度为100的数据包,在此链接上包含第2000-2099个字节,发送一个seq = 2000;
  2. 当接收器接收到包括第2099字节在内的全部字节时,它发送一个确认ack = 2100,表示它已接收到序号2000-2099的全部字节,并期待接下去接收到起始序号为2100的字节。

三次握手

注意,不是三次牵手 : )

所谓三次握手是指创建一个 TCP 链接时须要客户端和服务器端总共发送三个包以确认链接的创建。在socket编程中,这一过程由客户端执行connect来触发。
page77image65112304.jpg

第一次握手:

客户端将标志位SYN置为1,随机产生一个值seq=x,并将该数据包发送给服务器端,客户端进入SYN-SENT状态,等待服务器端确认。

第二次握手:

服务器端收到数据包后由标志位SYN=1知道客户端请求创建链接,服务器端将标志位SYNACK都置为1,ack=x+1,随机产生一个值seq=y,并将该数据包发送给客户端以确认链接请求,服务器端进入SYN-RCVD状态。

第三次握手:

客户端收到确认后,检查ack是否为x+1ACK是否为1,若是正确则将标志位ACK置为1ack=y+1,并将该数据包发送给服务器端,服务器端检查ack是否为y+1ACK是否为1,若是正确则链接创建成功,客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端与服务器端之间能够开始传输数据了。

链接创建总结:

SYN = 1,  seq = x;

SYN = 1,  ACK = 1, seq = y; ack = x + 1;

ACK = 1, seq = x + 1, ack = y + 1;

四次挥手

注意,不是四次分手 : )

四次挥手即终止TCP链接,就是指断开一个TCP链接时,须要客户端和服务端总共发送4个包以确认链接的断开。在socket编程中,这一过程由客户端或服务端任一方执行close来触发。

page77image65112096.jpg

中断链接端能够是客户端,也能够是服务器端。

第一次挥手:

客户端进程发出链接释放报文,而且中止发送数据。释放数据报文首部,FIN=1,其序列号为seq=u等于前面已经传送过来的数据的最后一个字节的序号加1,并将该数据包发送给服务器端,用来关闭客户端到服务器端的数据传送,客户端进入FIN-WAIT-1状态。意思是说:“我客户端没有数据要发给你了,可是若是你服务器端还有数据没有发送完成,则没必要急着关闭链接,能够继续发送数据”

第二次挥手:

服务器端收到FIN=1后,发出确认报文,ACK=1ack=u+1,而且带上本身的序列号seq=v,告诉客户端:“你的请求我收到了,可是我还没准备好,请继续你等个人消息”。这个时候客户端就进入FIN-WAIT-2 状态,继续等待服务器端的FIN报文。客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2状态,等待服务器发送链接释放报文(在这以前还须要接受服务器发送的最后的数据)。

第三次挥手:

当服务器端肯定数据已发送完成,就向客户端发送链接释放报文。置FIN=1ACK=1ack=u+1,因为在半关闭状态,服务器极可能又发送了一些数据,假定此时的序列号为seq=w,告诉客户端:“好了,我这边数据发完了,准备好关闭链接了,请求关闭链接”。服务器端进入LAST-ACK状态。

第四次挥手:

客户端收到服务器端的链接释放报文后,必须发出确认ACK=1ack=w+1,而本身的序列号是seq=u+1,此时,客户端就进入TIME-WAIT状态,若是Server端没有收到ACK则能够重传。注意此时TCP链接尚未释放,必须通过2MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。服务器只要收到了客户端发出的确认,当即进入CLOSED状态。一样,撤销TCB后,就结束了此次的TCP链接。能够看到,服务器结束TCP链接的时间要比客户端早一些。

链接断开总结:

FIN = 1, seq = u;

ACK = 1, seq = v; ack = u + 1;

FIN = 1, ACK = 1,    seq = w, ack = u + 1;

ACK = 1, seq = u + 1, ack = w + 1

这里列出每一个状态所包含的含义以供参考:

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三次握手四次挥手相关面试问题

为何要进行三次握手?

为了防止已失效的链接请求报文段忽然又传送到了服务端,于是产生错误,同时确认双方的接收与发送能力是否正常。

为何要 TCP 四次分手?

因为TCP链接是全双工的,所以,每一个方向都必需要单独进行关闭,这一原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的链接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,可是在这个TCP链接上仍然可以发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另外一方则执行被动关闭。

为什要有 2MSL 等待延迟?
MSL是Maximum Segment Lifetime的英文缩写,可译为“最长报文段寿命”,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。
为了保证客户端发送的最后一个ACK报文段可以到达服务器。由于这个ACK有可能丢失,从而致使处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,从新启动时间等待计时器。最后客户端和服务器都能正常的关闭。假设客户端不等待2MSL,而是在发送完ACK以后直接释放关闭,一但这个ACK丢失的话,服务器就没法正常的进入关闭链接状态。

两个理由:

  1. 保证客户端发送的最后一个ACK报文段可以到达服务端。 这个ACK报文段有可能丢失,使得处于LAST-ACK状态的B收不到对已发送的FIN+ACK报文段的确认,服务端超时重传FIN+ACK报文段,而客户端能在2MSL时间内收到这个重传的FIN+ACK报文段,接着客户端重传一次确认,从新启动2MSL计时器,最后客户端和服务端都进入到CLOSED状态,若客户端在TIME-WAIT状态不等待一段时间,而是发送完ACK报文段后当即释放链接,则没法收到服务端重传的FIN+ACK报文段,因此不会再发送一次确认报文段,则服务端没法正常进入到CLOSED状态。
  2. 防止“已失效的链接请求报文段”出如今本链接中。 客户端在发送完最后一个ACK报文段后,再通过2MSL,就可使本链接持续的时间内所产生的全部报文段都从网络中消失,使下一个新的链接中不会出现这种旧的链接请求报文段。
什么是SYN攻击?

服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,因此服务器容易受到SYN洪泛攻击。SYN攻击就是Client在短期内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,因为源地址不存在,所以Server须要不断重发直至超时,这些伪造的SYN包将长时间占用未链接队列,致使正常的SYN请求由于队列满而被丢弃,从而引发网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。

检测 SYN 攻击很是的方便,当你在服务器上看到大量的半链接状态时,特别是源IP地址是随机的,基本上能够判定这是一次SYN攻击。在 Linux/Unix 上可使用系统自带的 netstats 命令来检测 SYN 攻击。

netstat -n -p TCP | grep SYN_RECV

常见的防护 SYN 攻击的方法有以下几种:

  • 缩短超时(SYN Timeout)时间
  • 增长最大半链接数
  • 过滤网关防御
  • SYN cookies技术
(ISN)是固定的吗?

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

什么是三次握手的半链接队列和全链接队列?

服务器第一次收到客户端的 SYN 以后,就会处于 SYN-RCVD 状态,此时双方尚未彻底创建其链接,服务器会把此种状态下请求链接放在一个队列里,咱们把这种队列称之为半链接队列

全链接队列指已经完成三次握手,创建起链接的就会放在全链接队列中。若是队列满了就有可能会出现丢包现象。

三次握手中SYN-ACK 重传次数

服务器发送完SYN-ACK包,若是未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传,若是重传次数超 过系统规定的最大重传次数,系统将该链接信息从半链接队列中删除。注意,每次重传等待的时间不必定相同,通常会是指数增加,例如间隔时间为 1s, 2s, 4s, 8s, ….

三次握手过程当中能够携带数据吗?

第三次握手的时候能够携带数据的。第一次、第二次握手不能够携带数据。

对于第三次握手来讲,此时客户端已经处于 established 状态,也就是说,对于客户端来讲,他已经创建起链接了,而且也已经知道服务器的接收、发送能力是正常的了,因此能携带数据页没啥毛病。

结语

最近一段时间应该会比较系统性地从新整理一些TCP/IP协议簇有关的知识,目前正在打算开一个计算机基础相关的专题,可能主要会涉及操做系统计算机网络算法设计数据结构有关的内容。

俗话说的好,基础不牢,地动山摇,咱们励志要成一只有梦想的优质前端攻城狮!

参考
《计算机网络 第7版 谢希仁》
终于把TCP/IP 协议讲的明明白白了,不再怕被问三次握手了
「真香警告」重学 TCP/IP 协议 与三次握手
https://blog.csdn.net/freekin...
TCP和UDP比较
关于三次握手与四次挥手面试官想考咱们什么?— 不看后悔系列
若是你还看不懂这篇TCP/IP协议的话,就能够来打我了


推荐阅读:
【专题:JavaScript进阶之路】
ES6 Promise
JavaScript之深刻理解闭包
ES6 尾调用和尾递归
JavaScript之函数柯理化
浅谈 MVC 和 MVVM 模型


我是Cloudy,年轻的前端攻城狮一枚,爱专研,爱技术,爱分享。
我的笔记,整理不易,感谢关注、阅读、点赞和收藏。
文章有任何问题欢迎你们指出,也欢迎你们一块儿交流前端各类问题!
相关文章
相关标签/搜索