[网络通信与协议]三次握手与四次挥手

三次握手与四次挥手

  • TCP报文结构
  • TCP使用窗口机制进行流量控制
    • 窗口
    • TCP的流控过程(滑动窗口)
  • TCP连接全过程
    • 11个状态
      • SYN_RECV
      • CLOSE_WAIT
      • TIME_WAIT
    • 客户端状态流转
    • 服务器状态流转
    • 两个序号
    • 标志位
  • 三次握手
    • 作用
    • 初始状态
    • 图解
    • 具体步骤
      • 第一次握手
      • 第二次握手
      • 第三次握手
  • 四次挥手
    • 图解
    • 具体步骤
      • 第一次挥手
      • 第二次挥手
      • 第三次挥手
      • 第四次挥手
    • 另一种情况
  • 为什么需要四次挥手
  • 为什么需要三次握手
  • 半连接队列
  • SYN-ACK 重传次数
  • SYN攻击
  • 动态生成ISN(Initial Sequence Number)
  • 三次握手过程中携带数据
  • 第三次握手丢失了,客户端服务端处理策略
    • 服务端
    • 客户端
  • 已经建立了连接,客户端突然出现故障了怎么办?
  • 2MSL等待状态
    • 保证TCP协议的全双工连接能够可靠关闭
    • 保证这次连接的重复数据段从网络中消失

声明:文章部分内容源自网络摘抄,本文仅作备忘笔记使用

TCP报文结构

[网络通信与协议]三次握手与四次挥手_第1张图片

  • 源端口和目的端口,各占2个字节,分别写入源端口和目的端口;
  • 序号(报文段序号),占4个字节,TCP面向字节流,每个字节都按顺序编号。序号使用mod运算。
  • 确认号,占4个字节,是期望收到对方下一个报文的第一个数据字节的序号。若确认序号=N,则表明:到序号N-1为止的所有数据都已正确收到。
  • 数据偏移,占4位,表示TCP报文段的首部长度。TCP首部的最大长度为60字节。
  • 保留,占6位,保留今后使用,但目前应都位0;
  • 紧急URG,当URG=1,表明紧急指针字段有效。紧急数据+普通数据
  • 确认ACK, TCP规定,在连接建立后所有报文的传输都必须把ACK置1;当ACK=1时,确认字段才有效。当ACK=0时,确认号无效。
  • 推送PSH,PSH=1时表示这一端的应用进程希望在键入一个命令后立即就能收到对方的响应,响应方不用等到缓存填满了后在向上交付。
  • 复位RST,RST=1时表明TCP连接中出现严重差错,必须释放连接,然后再重新建立连接。
  • 同步SYN,在连接建立时用来同步序号。当SYN=1,ACK=0,表明是连接请求报文。当SYN=1,ACK=1,表明同意连接。
  • 终止FIN,用来释放连接。当FIN=1,表明此报文的发送方的数据已经发送完毕,并且要求释放;
  • 窗口,占2字节,窗口值作为接收方让发送方设置其发送窗口的依据
  • 检验和,占2字节,校验首部和数据这两部分;在计算检验和时,要在TCP报文段的前面加上12字节的伪首部。
  • 紧急指针,占2字节。紧急指针仅在URG=1时才有意义,指出本报文段中的紧急数据的字节数。
  • 选项,长度可变,定义一些其他的可选的参数。

注意:

(A)不要将确认序号Ack与标志位中的ACK搞混了。

(B)确认方Ack=发起方Req+1,两端配对。

TCP使用窗口机制进行流量控制

窗口

连接建立时,各端分配一块缓冲区用来存储接收的数据,并将缓冲区的尺寸发送给另一端接收方发送的确认信息中包含了自己剩余的缓冲区尺寸,剩余缓冲区空间的数量叫做窗口

TCP的流控过程(滑动窗口)

[网络通信与协议]三次握手与四次挥手_第2张图片

TCP连接全过程

[网络通信与协议]三次握手与四次挥手_第3张图片

[网络通信与协议]三次握手与四次挥手_第4张图片

[网络通信与协议]三次握手与四次挥手_第5张图片

粗的实线箭头表示正常的客户端状态变迁,粗的虚线箭头表示正常的服务器状态变迁。

[网络通信与协议]三次握手与四次挥手_第6张图片

11个状态

  1. LISTEN :服务端-等待连接。处于三次握手的准备态
  2. SYN_SENT : 客户端-主动发起连接请求(发送 SYN n)。第一次握手
  3. SYN_RECV : 服务端-接到连接请求后,响应 (发送 ACK n+1,SYN j)。第二次握手
  4. ESTABLISHED :服务端+客户端-连接建立成功(发送 ACK j+1)。第三次握手
  5. FIN_WAIT1 : 客户端-主动关闭连接,等待对方确认是否可以关闭(发送 FIN x)。第一次挥手
  6. FIN_WAIT2 : 客户端-收到对方确定可以关闭回应(接收到ACK x+1)。第三次挥手
  7. CLOSE_WAIT : 服务端-准备关闭连接时的状态,此时连接还未关闭。第二次挥手
  8. LAST_ACK : 服务端-发送可以关闭的消息(发送FIN y)。第三次挥手
  9. CLOSING :客户端-接到服务器关闭连接的信息,发送连接正式关闭消息(接收到 FIN y,发送ACK y+1)。第四次挥手
  10. TIME_WAIT : 客户端进入time wait 时间,等2倍MSL 时间后,关闭连接。第四次挥手之后,连接关闭之前
  11. CLOSED :服务端接到客户端正式关闭消息后,关闭连接。(接收到 ACK y+1)。第四次挥手

SYN_RECV

服务端收到建立连接的SYN没有收到ACK包的时候处在SYN_RECV状态。有两个相关系统配置:

1,net.ipv4.tcp_synack_retries :INTEGER

决定服务端在放弃连接之前所送出的 SYN+ACK 数目。不应该大于255,默认值是5,对应于180秒左右时间。

通常我们不对这个值进行修改,因为我们希望TCP连接不要因为偶尔的丢包而无法建立。

2,net.ipv4.tcp_syncookies

一般服务器都会设置net.ipv4.tcp_syncookies=1来防止SYN Flood攻击。

CLOSE_WAIT

被动关闭的服务端收到FIN后,但未发出ACK的TCP状态是CLOSE_WAIT。出现这种状况一般都是由于服务端代码的问题,如果你的服务器上出现大量CLOSE_WAIT,应该要考虑检查代码。

TIME_WAIT

TIME_WAIT状态将持续2个MSL(Max Segment Lifetime),在Windows下默认为4分钟,即240秒。

TIME_WAIT状态下的socket不能被回收使用. 具体现象是对于一个处理大量短连接的服务器,如果是由服务器主动关闭客户端的连接,将导致服务器端存在大量的处于TIME_WAIT状态的socket, 甚至比处于Established状态下的socket多的多,严重影响服务器的处理能力,甚至耗尽可用的socket,停止服务。

和TIME_WAIT状态有关的系统参数有一般有3个,本厂设置如下:

net.ipv4.tcp_fin_timeout,默认60s,减小fin_timeout,减少TIME_WAIT连接数量。

net.ipv4.tcp_tw_reuse = 1表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;

net.ipv4.tcp_tw_recycle = 1表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。

客户端状态流转

  • 三次握手建立连接

启动 —> SYN_SENT —> ESTABLISHED

  • 四次握手关闭连接

ESTABLISHED —> FIN_WAIT1 —> FIN_WAIT2 —> CLOSING —> TIME_WAIT —> CLOSED

服务器状态流转

  • 三次握手建立连接

LISTEN —> SYN_RECV —> ESTABLISHED

  • 四次握手关闭连接

ESTABLISHED —> CLOSE_WAIT —> LAST_ACK —> CLOSED

两个序号

Seq(sequence number)顺序号码,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。

Ack(acknowledge number)确认号码,占32位,只有ACK标志位为1时,确认序号字段才有效,ack=seq+1。

标志位

共6个,即URG、ACK、PSH、RST、SYN、FIN

(A)URG(urgent):紧急指针(urgent pointer)有效。

(B)ACK(acknowledgement):确认序号有效。

(C)PSH(push):接收方应该尽快将这个报文交给应用层。

(D)RST(reset):重置连接。

(E)SYN(synchronous):发起一个新连接。

(F)FIN:释放一个连接。

三次握手

三次握手(Three-way Handshake)其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。

作用

作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。

实质上其实就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。

HTTPS在这个过程进行数字证书的加密以及密钥的生成

初始状态

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

TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态;

TCP客户进程也是先创建传输控制块TCB,然后向服务器发出连接请求报文

图解

[网络通信与协议]三次握手与四次挥手_第7张图片

[网络通信与协议]三次握手与四次挥手_第8张图片

具体步骤

第一次握手

  1. 客户端发送syn包

  2. 进入SYN_SENT状态

  3. 等待服务器确认(第二次握手)

首部的同步位SYN=1,初始序号seq=x,SYN=1的报文段不能携带数据,但要消耗掉一个序号。

服务端就知道客户端发送正常,服务端接收正常

第二次握手

  1. 服务器收到syn包

  2. 确认客户的SYN

  3. 发送自己的SYN包

  4. 进去SYN_RECV状态

ACK=1,SYN=1,确认号是ack=x+1,初始化序列号 seq=y。这个报文也不能携带数据,但是同样要消耗一个序号。

客户端知道其服务器端收发正常,客户端收发正常,但是服务端不知道自己发送正常与否,客户端接收与否

第三次握手

  1. 客户端收到服务端的SYN+ACK包

  2. 向服务器发送确认包ACK

  3. 服务端和客户端进入ESTABLISHEN状态

确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1

ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。

服务端与客户端都知道自己和对方收发正常

四次挥手

当被动方收到主动方的FIN报文通知时,它仅仅表示主动方没有数据再发送给被动方了。

但未必被动方所有的数据都完整的发送给了主动方,所以被动方不会马上关闭SOCKET

它可能还需要发送一些数据给主动方后,再发送FIN报文给主动方,告诉主动方同意关闭连接,所以这里的ACK报文和FIN报文多数情况下都是分开发送的。

由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。

这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。

收到一个 FIN只意味着这一方向上没有数据流动

一个TCP连接在收到一个FIN后仍能发送数据。

图解

[网络通信与协议]三次握手与四次挥手_第9张图片

[网络通信与协议]三次握手与四次挥手_第10张图片

具体步骤

第一次挥手

  1. 客户端发送连接释放报文–一个FIN=1

  2. 停止发送数据

  3. 释放数据报文首部

  4. 客户端进入FIN_WAIT_1状态

FIN报文段即使不携带数据,也要消耗一个序号。

第二次挥手

  1. 服务端收到FIN

  2. 给客户端发送ACK=1,ack=u+1,seq=v

  3. 进入CLOAE_WAIT状态

TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。

第三次挥手

  1. 服务端发送FIN

  2. 关闭服务端到客户端的数据传送

  3. 服务端进入LAST_ACK状态

  4. 客户端收到服务端请求进入FIN_WAIT_2状态,等待服务器发送连接释放报文(此时客户端还需要接受服务器发送的最后的数据)

第四次挥手

  1. 客户端收到FIN

  2. 客户端进去TIME_WAIE状态

  3. 发送ACK给服务端ACK=1,ack=w+1

  4. 服务端进入CLOASED状态

注意此时TCP连接还没有释放,必须经过2MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。

服务器只要收到了客户端发出的确认,立即进入CLOSED状态。

同样,撤销TCB后,就结束了这次的TCP连接。

另一种情况

同时发起主动关闭的情况,具体流程如下图:

[网络通信与协议]三次握手与四次挥手_第11张图片

为什么需要四次挥手

TCP全双工需要两方都关闭通信

FIN和ACK分开发送

发送FIN报文仅表示不在发送数据但还能接收数据

TCP的半关闭(half-close)造成的。

半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。

第二次握手的时候服务端将ACK+FIN一同发送给客户端

第二次挥手和第三次挥手将ACK,FIN分开两步发送

ACK报文是用来应答的,SYN报文是用来同步的

为什么需要三次握手

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

允许双方就初始序列号进行协商。初始化Sequence Number 的初始值。

通信的双方要互相通知对方自己的初始化的Sequence Number(缩写为ISN:Inital Sequence Number),所以叫SYN,Synchronize Sequence Numbers。这个号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输的问题而乱序(TCP会用这个序号来拼接数据)。

防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。

​ 客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费

如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。

半连接队列

第二次握手并未完全建立链接,服务器会把处于完成第二次握手状态的请求放在一个队列中。

服务器会把完成三次握手的请求放在全连接队列中,如果队列满了可能会出现丢包现象。

SYN-ACK 重传次数

服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。

注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s…

SYN攻击

服务器端的资源分配是在二次握手时分配的

客户端的资源是在完成三次握手时分配的

服务器容易受到SYN洪泛攻击。

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

SYN 攻击是一种典型的 DoS/DDoS 攻击。

检测 SYN 攻击:服务器有大量的半连接状态时,特别是源IP地址是随机的,基本上可以断定这是一次SYN攻击。

在 Linux/Unix 上可以使用系统自带的 netstats 命令来检测 SYN 攻击。

netstat -n -p TCP | grep SYN_RECV

常见的防御 SYN 攻击的方法有如下几种:

缩短超时(SYN Timeout)时间

服务器端重试(再次发送SYN+ACK给客户端)并等待一段时间后丢弃这个未完成的连接,这段时间的长度我们称为SYN Timeout,一般来说这个时间是分钟的数量级(大约为30秒-2分钟)。

增加最大半连接数

过滤网关防护

SYN cookies技术

SYN Cookie是对TCP服务器端的三次握手协议作一些修改,专门用来防范SYN Flood攻击的一种手段。

它的原理是,在TCP服务器收到TCP SYN包并返回TCP SYN+ACK包时,不分配一个专门的数据区,而是根据这个SYN包计算出一个cookie值。在收到TCP ACK包时,TCP服务器在根据那个cookie值检查这个TCP ACK包的合法性。如果合法,再分配专门的数据区进行处理未来的TCP连接。

动态生成ISN(Initial Sequence Number)

当一端为建立连接而发送它的SYN时,它为连接选择一个初始序号。

ISN随时间而变化,因此每个连接都将具有不同的ISN。

ISN可以看作是一个32比特的计数器,每4ms加1 。

这样选择序号的目的在于防止在网络中被延迟的分组在以后又被传送,而导致某个连接的一方对它做错误的解释。

三次握手的其中一个重要功能是客户端和服务端交换 ISN(Initial Sequence Number),以便让对方知道接下来接收数据的时候如何按序列号组装数据。

如果 ISN 是固定的,攻击者很容易猜出后续的确认号,因此 ISN 是动态生成的。

三次握手过程中携带数据

第三次握手可以携带数据,第一二次不可以

第一次握手不可以携带数据,会让服务器更加容易被攻击

第三次已经建立好连接所以可以携带数据

第三次握手丢失了,客户端服务端处理策略

服务端

TCP连接的状态为SYN_RECV

SYN-ACK重传

重传一定次数后仍未收到客户端的ACK应答,一段时间后服务端自动关闭链接

客户端

客户端在接收到SYN+ACK包,它的TCP连接状态就为ESTABLISHED(已连接),表示该连接已经建立。

第三次握手中的ACK包丢失的情况下,客户端向服务端发送数据,服务端将以RST包(reset重置)响应,才能感知到服务端的错误。

已经建立了连接,客户端突然出现故障了怎么办?

TCP还设有一个保活计时器,客户端如果出现故障,服务器不能一直等下去。

服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75分钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

2MSL等待状态

TIME_WAIT状态也称为2MSL等待状态。

MSL(Maximum Segment Lifetime)最长报文段寿命

MSL是任何报文段被丢弃前在网络内的最长时间。

这个时间是有限的,因为TCP报文段以IP数据报在网络内传输,而IP数据报则有限制其生存时间的TTL字段。

TIME-WAIT状态停留时间为2倍MSL

在此期间,定义这个连接的插口(客户端的IP地址和端口号,服务端的IP地址和端口号)不能被使用。

保证TCP协议的全双工连接能够可靠关闭

客户端发送的最后一个ACK报文段能够到达服务端,最后客户端和服务端都能正常关闭

因为这个ACK有可能丢失,从而导致处在LAST-ACK状态的服务器收不到对FIN-ACK的确认报文。服务器会超时重传这个FIN-ACK,接着客户端再重传一次确认,重新启动时间等待计时器。

保证这次连接的重复数据段从网络中消失

如果Client直接CLOSED,然后又再向Server发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。

特殊情况:假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达Server,由于新连接和老连接的端口号是一样的,又因为TCP协议判断不同连接的依据是socket pair,于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。

你可能感兴趣的:(网络通信协议)