[计算机网络]-TCP-连接管理

net.ipv4.tcp_syn_retries参数的含义 net.ipv4.tcp_syn_retries的设置,表示应用程序进行connect()系统调用时,在对方不返回SYN + ACK的情况下(也就是超时的情况下),第一次发送之后,内核最多重试几次发送SYN包;并且决定了等待时间. Linux上的默认值是 net.ipv4.tcp_syn_retries = 6 ,也就是说如果是本机主动发起连接,(即主动开启TCP三次握手中的第一个SYN包),如果一直收不到对方返回SYN + ACK ,那么应用程序最大的超时时间就是127秒 iptables -A INPU 阅读详情

TCP 连接的建立与终止

一个 TCP 连接由一对 端点 或 套接字 构成,其中通信的每一端都由一对 (IP 地址,端口号) 的二元组所唯一标识。一个 TCP 连接通常分为 启动,数据传输(连接已建立),退出 三个时期

TCP 连接建立及终止的示意图如下:
连接建立及终止的示意图
连接状态示意图

连接建立

一开始,双方都处于 CLOSED 状态,先是服务端主动监听某个端口,处于 LISTEN 状态

  1. 主动开启方 (我们称为客户端) 发送一个 SYN 报文段 (SYN 位启用的报文段,用于初始化连接),指明 自己想要连接的端口号 和它的客户端 初始序列号(记为 ISNc)。之后客户端处于 SYN_SENT 状态
  2. 被动开启方(服务器)接收到客户端的 SYN 报文段后,发送自己的 SYN 报文段作为响应,并包含了它的 初始序列号 (记为 ISNS),此外,为了确认客户端的 SYN(报文段),他还需要发送对应的 ACK,其数值为 ISNc + 1 (SYN 报文段不能携带数据,但要消耗掉一个序列号),SYN 跟 ACK 一起发送。因此,每发送一个 SYN,序列号就会加一,这样如果出现丢失的情况,该 SYN 就会被重发。之后服务端处于 SYN_RCVD 状态
  3. 客户端为了确认服务器的 SYN 同样需要发送一个 ACK,数值为 ISNS + 1。ACK 报文段可以携带数据,如果不携带数据就不消耗序列号 (不携带数据称为 “纯 ACK”)。之后客户端处于 ESTABLISHED 状态。服务端接收到 ACK 后也进入 ESTABLISHED 状态

通过上述三个报文段就能完成一个连接的建立,通常称为 三次握手

为什么同样不携带数据,SYN 报文需要消耗一个序列号而 ACK 不需要

只要记住:需要对方确认的报文段都需要消耗序列号。SYN 报文需要对方确认,所以需要消耗序列号;而发送 (不携带数据的) ACK不需要对方确认,所以不需要消耗序列号

初始序列号

在一个连接中,TCP 报文段在经过网络路由后可能会存在延迟抵达与排序混乱的情况,为了解决这一问题,需要仔细选择 初始序列号,然后序列号从初始序列号开始有规律地变化,对应着每个报文段原本的顺序,从而尽管会有报文段到达接收方时排序混乱的情况,也能根据序列号进行调整

初始序列号会随时间而改变 (注意,说的是初始序列号而不是序列号),因此 每一个连接都拥有不同的初始序列号。初始序列号可被视为一个 32 位的计数器,该计数器的数值每 4 微秒加一,其目的在于 为一个连接的报文段安排序列号时防止出现与其它连接的序列号重叠

初始序列号除了需要 防止重叠 以外,还有一个是 安全性问题。试想一下,如果选择合适的序列号,IP 地址以及端口号,那么任何人都能伪造出一个 TCP 报文段。抵御这种行为的方法是使初始序列号变得相对难以猜出,另一种方法是加密

现代系统通常采用半随机的方法选择初始序列号
在 Linux 系统中,采用基于 时钟 的方案,并且针对每一个连接为时钟设置 随机的偏移量,随机偏移量是在 连接标识(即 四元组) 的基础上利用 加密散列函数 得到的,而 散列函数的输入 每隔 5 分钟就会改变一次,也就是说对于同一个连接的不同实例得到的随机偏移量也是不同的。在 32 位的初始序列号中,最高的 8 位是一个保密的序列号,而剩余的各位则由散列函数生成

为什么是三次握手不是两次/四次

>>> 为什么不能是两次?

主要原因是 为了防止已失效的连接请求报文突然又传送到了服务端导致服务端进入连接状态

假设只有两次握手。考虑这样一种情况,客户端发送的第一个连接请求报文段 并没有丢失,但是在某些网络节点长时间滞留,或者由于网络拥塞长时间未到达服务端。那么客户端会重发 SYN,然后重新建立起连接

假设第一个连接请求报文段延误,直到客户端跟服务端连接释放后的某个时间才到达服务端,那么本来这个请求报文已经 失效 了。但服务端收到后误以为是客户端的又一次新的连接请求,于是就向客户端返回 ACK 同意建立连接,并进入 ESTABLISHED 状态等待客户端发送数据,对服务端来说,新的连接就已经建立了,这样服务端就浪费了许多资源

服务端不能判断请求报文段是否有效,但客户端可以 (根据 ACK 的序列号/时间戳,如果要服务端来判断,那么服务端需要存储每个客户端上次连接的时间,明显不合理) 判断本次连接是否有效,所以需要让客户端判断 ACK 是否有效,有效才进行第三次握手,告知服务端可以建立连接

换句话说,只有两次握手的话,服务端在接收到 SYN 后就会进入 ESTABLISHED 状态,中间没有一个缓冲状态,不论 SYN 是否有效,这就可能会导致服务端浪费资源。解决办法就是在服务端建立连接之前阻止掉无效的历史连接,这就需要第三次握手来完成

其次,可以确保双方的序列号同步。客户端发送 SYN 到服务端,然后服务端响应了 ACK,就说明从客户端到服务端这个方向上的序列号成功同步了;同理,服务端发送 ACK 时也会发送自己的 SYN,然后客户端响应 ACK,就说明从服务端到客户端的这个方向上的序列号同步了,一共三步,才能确保了双方的序列号都被成功接收了

>>> 为什么不是四次?

其实在服务端发给客户端 ACK 以及 SYN 这里可以看成两步,即发送响应客户端 的 SYN 的 ACK,以及发送自己的 SYN,但可以合并为一步所以就合并了

像这样的三步已经理论上足够一个可靠连接的建立,所以不需要使用更多的通信次数了

第一次握手丢失会发生什么

第一次握手丢失,即客户端发出的连接建立超时,那么会触发 超时重传 机制,重传 SYN 报文,如果一直没有收到 ACK 应答,就会一直重传。重传的相隔时间是上一次重传相隔时间的两倍,这一行为被称为 指数回退,每次回退数值都是前一次数值的两倍

在 Linux 中,系统配置变量 net.ipv4.tcp_syn_retries 表示在一次主动打开连接的申请中尝试重新发送 SYN 报文段的最大次数;相应地,变量 net.ipv4.tcp_synack_retries 表示在响应对方的主动打开连接请求时尝试重新发送 SYN + ACK 的最大次数,它们默认数值为 5

第二次握手丢失会发生什么

第二次握手,包含了服务端对第一次握手,即客户端的 SYN 报文的确认 ACK;还包含了服务端发送给客户端的 SYN。所以需要从这两个方面分析

对客户端的 SYN 报文的 ACK 丢失了,就会触发客户端超时重传 SYN。其实对于客户端来说,第二次握手丢失触发的事件跟第一次握手丢失触发的事件是一样的

服务端的 SYN 丢失了,对服务端来说就相当于自己发出了SYN却没有得到对方的A CK,那么也会触发超时重传,重传 SYN + ACK 报文。SYN + ACK 报文最大重传次数由参数net.ipv4.tcp_synack_retries 决定

可以思考一下,第三次握手丢失会发生什么,其实第三次握手丢失,对于服务端来说,跟第二次握手是一样的,不管是第二次握手丢失,还是第三次丢失,对于服务端来说,他只知道自己没有收到对方的ACK,所以会重传 SYN + ACK (ACK 值与上次的 SYN + ACK 报文中的 ACK 值相等)

注意,虽然说的是重传 SYN + ACK 报文,但是ACK是不会重传的,当ACK丢失了,就由发送方重传对应的报文

连接终止

连接的任何一方都能够发起一个关闭操作。通常发起关闭连接的是客户端,然后一些服务器如Web服务器在对请求做出响应之后也会发起一个关闭操作。通常一个关闭操作是由应用程序提出关闭连接的请求而引发的 (如使用系统调用 close())

TCP 一端通过发送一个 FIN 报文段 (即 FIN 控制位置位的报文段) 来发起关闭操作,而且只有当连接双方都完成关闭操作后才构成一个完整的关闭

  1. 连接的主动关闭者,这里我们假设为客户端,发送一个 FIN 段指明接收端希望收到的序列,还包含了一个 ACK 由于确认对方最近一次发来的数据。之后客户端进入 FIN_WAIT_1 状态

  2. 被动关闭者 (服务端) 接收到对方的 FIN 报文段后需要发送一个 ACK 表明自己已经接收到它的FIN 报文,之后服务端进入 CLOSE_WAIT 状态

    此时上层的应用程序会被告知连接的另一端已经提出了关闭操作,通常 (TCP支持半关闭,见下文),这将导致应用程序发起自己的关闭操作。客户端接收到服务端的 ACK 应答报文后,进入 FIN_WAIT_2 状态

  3. 接着,服务端就会成为主动关闭者,然后向对方发出自己的 FIN 报文段,其序列号跟刚刚响应第一个 FIN 的 ACK 报文段的序列号相等,因为那个 ACK 报文段不消耗序列号。同时还要携带 与那个ACK报文段中的ACK字段值相等的ACK值,之后服务端进入 LAST_ACK 状态

  4. 客户端接收到服务端的关闭请求 FIN 报文段后,同样需要发送一个 ACK 报文段用于确认,之后进入 TIME_WAIT 状态。如果出现 FIN 报文丢失的情况,发送方将重新传输直到接收到一个ACK确认为止。服务端接收到 ACK 应答报文后,就进入 CLOSED 状态,至此,服务端已经完成连接的关闭。客户端在 TIME_WAIT 状态结束后自动进入 CLOSED 状态,至此客户端的关闭操作也完成了

综上所述,连接的关闭需要四个报文段,又称为 四次挥手。每一方关闭其到对方的连接都需要两个报文段,加起来一共四个报文。两次关闭操作在分析时可以对称地分析

第一次挥手丢失了会发生什么

当主动关闭方发送 FIN 报文段后,长时间没有收到对方的 ACK应答,也会触发超时重传机制;最大重发次数由 tcp_orphan_retries 参数控制。当重发次数达到 tcp_orphan_retries 后,且最后一次重发后在等待时间内也没有收到 ACK,就不再发送 FIN 报文,直接进入 CLOSED 状态

TCP 半关闭

可能会出现有的应用程序在自己的数据已经发送完成了,发送 FIN 关闭从自己到对方的连接,但它仍然希望能接收来自对方的数据,直至对方发送了 FIN,即只关闭自己到对方的连接而不关闭对方到自己的连接,这就是 半关闭

应用程序可以通过调用 shutdown() 来代替 close() 函数,就能实现上述操作

关于 FIN_WAIT_2 状态

FIN_WAIT_2 状态表示某通信端已主动发送一个 FIN 并已得到另一端的确认。从示意图中可以看出,在这种状态下,只有等对方发送了 FIN 并且这一端接收到了 FIN,这一端才会进入 TIME_WAIT 状态,这意味着这一端能够一直保持这种状态,对方也能一直保持 CLOSE_WAIT 状态,直到对方的应用程序决定关闭连接

为了防止连接处于这种可能无限等待的状态,如果主动关闭的应用程序执行的是一个 完全关闭 操作,而不是一个 半关闭,那么就会设置一个 计时器,如果当计时器超时时连接是空闲的,主动关闭的这一端就会从 FIN_WAIT_2 进入到 CLOSED 状态

在 Linux 中通过变量 net.ipv4.tcp_fin_timeout 来设置计时器的秒数,默认为 60s。这里说的半关闭操作其实就是 shutdown() 函数,net.ipv4.tcp_fin_timeout 参数无法控制调用 shutdown() 函数进行关闭连接的通信端,所以主动关闭的通信端调用的是 shutdown() 函数的话,在接收到对方的 FIN 之前,都会处于FIN_WAIT_2状态

关于 TIME_WAIT 状态

连接的主动关闭者在发出自己的 FIN,并接收到对方的 ACK 后,就会进入 FIN_WAIT_2 状态,等待对方的 FIN 报文。在接收到对方的 FIN 报文后,就会发送 ACK 给对方,并进入 TIME_WAIT 状态。这个状态下有一个 时间等待计时器,其设置的时间为 2MSL ,有时也称为 加倍等待,TCP需要等待这个时间,才会进入 CLOSED 状态

MSL, M a x i m u m   S e g m e n t   L i f e t i m e Maximum\ Segment\ Lifetime Maximum Segment Lifetime,最大段生命,代表任何报文段在被丢弃前在网络中被允许存在的最长时间。2MSL 在 Linux 中是通过一个宏定义 TCP_TIMEWAIT_LEN 设置的,默认是 60s,想要修改的话需要修改这个参数并重新编译内核。2MSL 的时间是从客户端收到 FIN 后发送 ACK 才开始计时的,也就是说,假设在 TIME_WAIT 时间内,由于客户端发送的 ACK 超时,导致了服务端重传 FIN,那么客户端再次接收到 FIN,并发送 ACK,计时器将重新计时

为什么需要 TIME_WAIT 状态

  1. 保证最后一个 ACK 能够到达对方,使其能正常关闭。假设客户端发给服务端的最后一个 ACK 丢失了,处于 LAST_ACK 状态的服务端就收不到客户端对自己发出的 FIN 的确认报文,那么它就会超时重传这个 FIN 报文,而客户端就能在 2MSL 时间内收到这个重传的 FIN,然后再次发送确认报文,重新启动 2MSL 计时器。假如客户端不在 TIME_WAIT 状态等待一段时间,而是发完最后一个 ACK 后立即释放连接,那么就无法收到服务端重传的 FIN 报文,也就不会再发送一次 ACK,服务端也就不能正常地进入 CLOSED 状态
    也正是考虑到这种 ACK 丢失然后服务端重传 FIN 的情况,客户端需要等待重传的 FIN 到达,一个 ACK 加上一个重传 FIN,两个报文,所以需要 2MSL 作为 TIME_WAIT 的时间
  2. 防止历史连接中的数据,被后面的连接接收。正如上面在连接建立部分讲到的为什么三次握手不能是两次握手的问题中的”已失效的连接请求报文“。客户端在发送最后一个ACK后再经过时间2MSL,就可以使本连接持续时间内所产生的所有报文段都从网络上消失,确保下一次连接中不会出现旧连接产生的报文段

TIME_WAIT 状态过多有什么危害

  1. 占用内存资源
  2. 占用本地端口资源。一个通信端的端口资源是有限的,一般可以开启的端口为 32768~61000,可以通过参数 net.ipv4.ip_local_port_range 来修改。如果发起连接方的 TIME_WAIT 状态过多,端口被占满,就无法创建新的连接;而对于服务端,虽然它可能只需要监听一个端口,然后不同的客户端访问这一个端口产生不同连接,也就是说它不太会因为 TCP 连接过多而导致端口资源受限,但连接过多也会占用系统资源,如 文件描述符,内存资源,CPU,线程 等等

如何优化 TIME_WAIT

  1. 设置 net.ipv4.tcp_tw_reuse 参数为1,表示可以复用处于 TIME_WAIT 的 socket 供新的连接使用。前提是需要打开时间戳选项的支持
  2. 修改 net.ipv4.tcp_max_tw_buckets 参数的值,该值表示当系统中处于 TIME_WAIT 状态的连接一旦超过这个数值时系统就会将后面的 TIME_WAIT 连接状态重置
  3. 设置 net.ipv4.tcp_tw_recycle 参数为 1,表示快速回收 TIME_WAIT 状态的连接。该参数不能轻易开启,参考资料
  4. 有一个 so_linger 选项,其为 linger 结构体类型,其中含有两个 int 型变量:
struct linger {
     int l_onoff;
     int l_linger;
};

当 l_onoff 为 0 时,表示整个选项关闭,l_linger 的值也会被忽略;
当 l_onoff 为 1,且 l_linger 为 0,那么主动关闭方调用 close() 函数后会立刻发送一个 RST 报文,所以连接会跳过四次挥手,自然也就跳过了 TIME_WAIT 状态;
当 l_linger 为非 0,连接会设置一个 超时 时间,在这段时间内可以发送缓冲区内残留的数据,如果时间到了之前所有数据都被发送且收到确认应答,那么内核就会用正常的四次挥手来关闭连接,否则就用 RST 报文的方式来关闭

TCP的状态转换图

TCP有限状态机

TCP的选项

部分选项
每一个选项的头一个字节为 “种类”,指明该选项的类型。种类值为 0 或 1 的选项仅占用 1 个字节。每种选项根据自己的种类确定自身的字节数 len,选项字段的总长度就包括种类与 len 个字节。上图中的长度指的是选项字段的总长度 (包括种类)

NOP ( N o   O p e r a t i o n No\ Operation No Operation) 选项的目的是允许发送者在必要的时候填充某个字段,使整个 TCP 头部长度达到 32bit 的倍数

EOL ( E n d   O f   O p t i o n   L i s t End\ Of\ Option\ List End Of Option List) 选项指出了选项列表的结尾,说明无需对选项列表再处理

最大段大小选项

最大段大小 (MSS, M a x i m u m   S e g m e n t   S i z e Maximum\ Segment\ Size Maximum Segment Size)是指 TCP 协议所允许的从对方接收到的最大报文段,也即通信双方发送数据时能够使用的最大报文段,最大段大小只记录 TCP 数据的字节数,不包括其它相关的 TCP 及 IP 头部。其默认数值为 536 字节

选择确认选项

由于接收的数据是无序的,所以接收到的数据的序列号也是不连续的,那么 TCP 接收方的数据队列中就会出现空洞的情况,如收到了序列号 1-3,6-7 的数据,那么就有一个空洞 4-5

而由于 TCP 提供的是字节流的服务,它要保证交付给应用程序的数据是有序的,需要防止应用程序使用超出空洞的数据
如果 TCP 发送方能够知道接收方当前的空洞情况,他就能在报文段丢失或者被接收方遗漏时更好地进行重传工作。这就是 选择确认 的功能

通过接收 SYN (或者 SYN + ACK) 中的 “允许选择确认” 选项,TCP 通信方就知道自己能发布 SACK 的信息。当接收到乱序的数据时,它就能提供一个 SACK 选项来描述这些乱序的数据。SACK 选项中包含了 接收方已经成功接收的数据块 的序列号范围,每个范围被称为一个 SACK 块,由 一对 32 位的序列号 表示。因此,一个 SACK 选项包含 n 个 SACK 块的话,其长度就为 8n + 2 字节,2 个字节用于保存 种类 和 长度。通过序列号范围就能得到空洞的范围

由于头部长度的限制,一个报文段中发送的最大 SACK 块数目为 3 (假设使用了时间戳选项,这是现代 TCP 实现中的典型情况)

窗口缩放选项

窗口缩放选项 (WSCALE 或 WSOPT, W i n d o w   S c a l e   O p t i o n Window\ Scale\ Option Window Scale Option) 能够有效地 将 TCP 窗口广告字段的范围从 16 位增加至 30 位。选项总长度为 3 字节,其中一个字节表示种类,一个字节表示长度,剩下一个字节就是窗口缩放的 比例因子

假设比例因子设值为 s,那么窗口数值就会扩大到原先的 2s 倍。原本的窗口大小最大值为 65535 (216 - 1),比例因子的最大值允许是 14,也就是说窗口的实际最大值是 65535 * 214,这个数值接近 1 GB。RFC 7323 中提到,如果一个通信端接收到了一个大于 14 的字段值,他也只能使用 14

该选项只能出现于一个 SYN 报文段中,因此当连接建立后比例因子是与方向绑定的,要调整窗口大小的话就通过修改窗口大小字段的值来调整

为了保证窗口调整,通信双方都需要在 SYN 报文段中包含该选项,当然,这还是由通信端自己选择包不包含,如果双方有一方没有包含该选项,那么双方都不会启用这个选项。主动打开连接的一方发送了 SYN,而被动打开的一方只有在收到的 SYN 报文中指出了该选项自己才能发送该选项;而如果主动打开连接的一方发送了 SYN,且包含了该选项,但是没有接收到来自对方的窗口缩放选项,它会将自己发送与接收的比例因子数值都设为 0

时间戳选项与防回绕序列号

时间戳选项 (TSOPT 或 TSopt)的示意图如下:

时间戳选项格式
时间戳选项要求发送方在每一个报文段中添加 2 个 4 字节的时间戳数值,其总长度为 10 字节 (还有 1 个字节为种类,1 个字节为长度),接收方将会在确认 ACK 报文中反映 (回显) 这些数值,允许发送方针对每一个接收到的 ACK (注意不是每一个报文,因为 TCP 采用的是累积确认) 估算 TCP 连接的往返时间

发送方将一个 32 位的数值填充到时间戳数值字段 (TSV / TSval) 作为时间戳选项的第一个部分;接收方则将收到的时间戳数值原封不动地填充到第二部分的 时间戳回显重试 字段 (TSER / TSecr),然后在第一部分处填充自己的时间戳数值

估算一条 TCP 连接的往返时间 主要是为了设置重传时间,时间戳选项使我们获得了更多的往返时间样本,从而提升了精确估算往返时间的能力

除此之外,时间戳选项 也为接收者提供了避免接收旧报文段与判断报文段正确性的方法,即 防回绕序列号 ( P r o t e c t i o n   A g a i n s t   W r a p p e d   S e q u e n c e   n u m b e r s Protection\ Against\ Wrapped\ Sequence\ numbers Protection Against Wrapped Sequence numbers,PAWS)
试想一下,一个 相对高速的连接 中,序列号即使有 232 个,也会很快就循环完一轮,假设当前这一轮中有某些序列号对应的数据丢失了,然后重传,但是在序列号循环的第二轮才出现,接收方只根据序列号的话是很难区分这些数据是最近发送的还是以前发送的

而有了时间戳选项。就可以判断报文段的时间戳是否小于最近接收到的报文段的时间戳,是的话防回绕序列号算法就将其丢弃

防回绕序列号算法并不要求在发送者跟接收者之间有任何形式的时钟同步,接收者所需要的是保证时间戳数值单调递增,并且每一个窗口的数据至少增加 1

可以看出来,时间戳选项对于前面我们提到的 “来自历史连接的报文段” 也可以进行处理,判断出其是来自历史连接还是本次连接
将参数 net.ipv4.tcp_timestamps 的值设为1就可以开启时间戳选项的支持。默认即为 1

用户超时选项

用户超时选项数值 ( U S E R _ T I M E O U T USER\_TIMEOUT USER_TIMEOUT) 指明了TCP发送者在确认对方未能成功接收数据之前愿意等待该数据ACK确认的时间

认证选项

TCP认证选项 ( T C P   A u t h e n t i c a t i o n   O p t i o n TCP\ Authentication\ Option TCP Authentication Option,TCP-AO) 是用于增强连接的安全性的。它使用了一种加密散列算法以及TCP连接双方共同维护的一个秘密值来认证每一个报文段。当发送数据时,TCP会根据共享密钥生成一个通信密钥,并根据一个特殊的算法计算散列值,接收者装配有相同的密钥,同样也能够生成通信密钥,借助通信密钥接收者就可以确认到达的报文段是否在传输过程中被篡改过。由于需要创建并分发一个共享密钥,该选项并没有得到广泛使用
RFC 5925中提到,每个连接的通信密钥跟连接本身一样唯一,即一个连接 (实例) 只会有一个通信密钥

重置报文段

一个将 RST 控制位置位的报文段即称为 重置报文段。当发现一个到达的报文段对于相关连接而言是不正确的时,TCP 就会发送一个重置报文段

产生报文段的场景有以下几种:

  1. 当一个连接请求 SYN 到达本地却没有相关进程在目的端口监听时就会产生一个重置报文段。重置报文段中的 ACK 位必须被置位,对于响应一个连接请求的 SYN 报文段,由于 SYN 报文段不携带数据,但其本身会占用一个字节的序列号,所以返回的重置报文段中的 ACK 数值应为 SYN 报文中的初始序列号加上数据长度 0 以及 SYN 占用的 1 字节

    ACK 字段的数值必须在正确窗口的范围内,这样有助于防止一种简单的 攻击,在这种攻击中任何人都能生成一个与相应连接匹配的重置报文段从而扰乱这个连接,但伪造的重置报文段很难做到 ACK 数值在正确窗口的范围内

  2. 终止一条连接。我们称通过通信一方发送 FIN 来终止连接的方法为 有序释放;而在任何时刻,都可以通过发送一个重置报文段替代 FIN 来终止一条连接,这种方式又称为 终止释放

    终止一条连接可以为应用程序带来两大特性:任何排队的数据都将被丢弃,一个重置报文段会被立即发送出去;重置报文段的接收方会说明通信另一端采用了终止的方式而不是一次正常关闭。API 必须提供一种实现上述终止行为的方式来取代正常的关闭操作
    套接字API通过将 逗留于关闭 套接字选项 SO_LINGER 数值设为 0 实现上述功能,这意味着 不会在终止之前为了确定数据是否到达另一端而逗留任何时间,那么四次挥手将会被跳过,TIME_WAIT 状态也会被跳过,这也是优化 TIME_WAIT 状态的一种手段

    还需要注意的是重置报文段不会被确认

  3. 半开连接。如果在未告知另一端的情况下通信的一端关闭或终止连接,那么就认为该条连接处于 半开 状态。例如,当一个连接中服务端崩溃,连接被切断,留给客户端一个半开连接,当服务端重启后,客户端尝试发送数据到服务端,服务端会对这条连接一无所知,此时,TCP 规定接收者将回复一个重置报文段作为响应

  4. 时间等待错误。处于 TIME_WAIT 状态的通信端通常不需要做任何操作,只需要维持着当前状态直到 2MSL 计时结束,而如果它在这段时间内接收到来自这条连接的一些报文段,或是更加特殊的重置报文段,它就会被破坏,这种情况称为 时间等待错误 ( T I M E − W A I T   A s s a s s i n a t i o n TIME-WAIT\ Assassination TIME−WAIT Assassination,TWA)

    当客户端主动发起连接并且四次挥手完成后,客户端将处于 TIME_WAIT 状态,而服务端已处于 CLOSED 状态,此时有可能客户端会接收到本次连接产生的旧的报文段 (到达时间比较晚),那么他就会发送一个 ACK 作为响应。然而,当服务端接收到这个报文段之后,它却没有关于这条连接的任何信息,因此它发送一个重置报文段作为响应,这不是服务端的问题,但却会使客户端过早地从 TIME_WAIT 状态转移到 CLOSED 状态

    许多系统规定,当处于 TIME_WAIT 状态时不对重置报文段作出反应,从而避免了上述问题

半连接队列和全连接队列

服务端接收到客户端的 SYN 请求后,内核会把连接放到 半连接队列 (也称 SYN 队列) 中,然后向客户端响应 SYN + ACK 报文,等待客户端返回 ACK,即第三次握手后,再把连接从半连接队列中移出,创建完整的连接添加到 全连接队列 (也称 accept 队列) 中,等待进程调用 accept() 时再把连接取出来

全连接队列中连接数目的最大值 为内核参数 net.core.somaxconn (默认为 128) 与 listen() 函数中 backlog 参数两者的较小值
当 全连接队列已满,服务端自然就无法接受一个新的连接。系统控制变量 net.ipv4.tcp_abort_on_overflow 可以设置服务端的行为:
当其值为 0,服务端只会忽略客户端的 ACK;
当其值为 1,服务端还会发送一个重置报文段给客户端,默认情况下这个功能是不开启的。因为此时客户端处于的是 ESTABLISHED 状态,它会尝试与服务器联系,发出请求。如果它收到了重置报文段,那它可能会认为没有服务器存在,但实际上服务器是因为繁忙而不是不存在,繁忙跟不存在完全不一样。假设服务端繁忙的状况能够在短时间内得到改善,那么它是有机会去接受刚才繁忙时没能接受的连接的 (只要客户端有在继续尝试联系,因为请求报文会携带 ACK,能触发服务端继续完成连接) ;而如果直接发送了重置报文,那么客户端也会放弃主动打开的操作,就算服务端短时间内不繁忙了,也不能再来接受这个连接了
所以只有当肯定全连接队列会长期溢出时,才应该将 net.ipv4.tcp_abort_on_overflow 参数设为 1 以尽快通知客户端不再尝试连接,节省其资源

如果 半连接队列已满,不一定只能丢弃连接,在开启 syncookies 功能的情况下就可以不使用 SYN 半连接队列而完成连接的建立。半连接队列 的最大数目不止与参数 net.ipv4.tcp_max_syn_backlog (默认为 1000) 有关,还跟全连接队列的最大数目有关

与 TCP 连接管理相关的攻击

SYN 泛洪

SYN 泛洪 是一种 TCP 拒绝服务攻击,指一个或多个恶意的客户端产生一系列 TCP 连接尝试 ,即 SYN 报文段),并将它们发送给一台服务器,服务器会为每一条连接分配一定数量的连接资源,由于连接尚未完全建立 (此时 服务端响应了这些 SYN 并发送自己的 SYN + ACK 报文段,处于 SYN_RCVD 状态并等待对方的 ACK,但 恶意的客户端并不会继续响应 ACK,即第三次握手),服务端为了维护大量的半打开连接会在耗尽自身内存后拒绝为后续的合法连接请求服务

一种针对这种问题的机制为 SYN cookies。其主要思想是,当一个 SYN 到达时,这条连接存储的大部分信息都会被编码并保存在 SYN + ACK 报文段的序列号字段。采用 SYN cookies 的目标主机并不需要为进入的连接请求分配任何存储资源,只有当 SYN + ACK 本身被确认后 (并且已返回初始序列号) 才会分配真正的内存,在这种情况下,所有重要的连接参数都能重新获得,同时连接也能够被设置为 ESTABLISHED 状态

该功能通过 net.ipv4.tcp_syncookies 参数设定,当其值为 0 时表示不开启,为 1 时表示仅当 SYN 队列已满时再开启,为 2 时表示无条件开启该功能

Linux 中,服务端在接收到一个 SYN 后会采用下面的方法设置初始序列号然后保存在 SYN + ACK 报文段中):首 5 位是 t 模 32 的结果,其中 t 是一个 32 位的计数器,每隔 64 秒增 1;接着 3 位是对服务器最大段大小的编码值;剩余的 24 位保存了四元组 (即连接标识) 与 t 值的散列值,该值是根据服务器选定的散列加密算法计算得到的

在采用该方法时,服务端总是以一个 SYN + ACK 报文段作为响应,在接收到第三次握手的 ACK 后,如果根据其中的 t 值可以计算出与加密的散列值相同的结果,那么服务器才会为该 SYN 重新构建队列

除此之外还可以通过 减少 SYN + ACK 的重传次数 来抵御 SYN 泛洪,即减小参数 net.ipv4.tcp_synack_retries 的值,这样 SYN + 重传次数减少了,处于SYN_RCVD状态的连接也就可以更快被断开

一文了解Linux上TCP的几个内核参数调优 在tcp_tw_recycle=1同时tcp_timestamps(默认开启的情况下),对同一个IP的连接会做这样的限制,也即之前后建立的连接的时间戳必须要大于之前建立连接的最后时间戳,但是经过NAT的一个IP后面是不同的机器,时间戳相差极大,就会导致内核直接丢弃时间戳较低的连接的现象。但是,有些代码可能会有忘了设置timeout的情况,例如某个版本的Kafka就是,所以它在我们一些混沌测试的情况下,容灾恢复的时间会达到一分多钟,主要时间就是卡在connect上面-_-!内核TCP连接缓冲队列的设置。 阅读详情

相关推荐

本地大模型部署实战:LM Studio与Ollama的易用性与性能深度评测

本文对两款主流本地大模型部署工具LM Studio和Ollama进行了深度评测。LM Studio凭借图形化界面和内置模型库,为新手提供了开箱即用的便捷体验,并支持OpenAI兼容API,易于生态集成。Ollama则以命令行驱动,轻量高效,凭借其原生API和Modelfile自定义能力,在自动化集成和性能方面表现突出。文章从易用性、模型生态、性能及扩展性多维度对比,帮助用户根据自身技术背景和使用场景做出最佳选择。

weixin_29039773的博客 528

TCP的连接和断开详解

IP 层是「不可靠」的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责。因为 TCP 是一个工作在传输层的可靠数据传输的服务,它能确保接收端接收的网络包是。

红军不怕远征难,万水千山只等闲 1万+

ctf——writeup

ctf线上赛——第三关地址:http://106.75.86.18:2222提示:ip spoofing——ip伪造首先访问目标地址,一个正常的网页,挺文艺的。上面有些提示。根据上面的提示,我们浏览网页。找了好久,终于找到你。这个跳转链接。(在这个页面下方)我们接着点击这个链接,就跳转到administrator.php这个页面了。看到这个登录页面,就要知道用户名,密码,可是没有怎么办。一开始也试

Child_by_dream的博客 1320

计算机网络(6) TCP协议

序列号是一个32位的字段,用于标识TCP段中第一个字节的数据的编号。

qq_42761215的博客 3051

Qt开发实战:QWebEngineView缓存路径详解与自定义配置(Windows/Linux)

本文详细解析了Qt框架中QWebEngineView组件的缓存机制,提供了Windows和Linux系统下的默认缓存路径及自定义配置方案。通过实战代码示例,展示了如何实现跨平台缓存路径管理、权限设置以及企业级应用中的多用户隔离策略,帮助开发者有效管理Web内容缓存,提升应用性能和数据安全性。

weixin_42519826的博客 403

LAMMPS实操系列(四): 高熵合金FCC-CoCrCuFeNi的退火模拟

我们对FCC-CoCrCuFeNi的退火模拟思路主要是,首先将材料以均匀的速率用100 ps从300 K升温至1000 K,然后在1000 K的温度下平衡100 ps,随后以均匀的速率用100 ps从1000 K降温至300 K,随后通过共轭梯度下降法做静力学结构优化得到退火后的结构。由于FCC-CoCrCuFeNi高熵合金被认为是潜在的工程结构材料[1],接下来我们将对退火前后的结构进行压缩模拟,对比退火前后FCC-CoCrCuFeNi高熵合金的力学性能变化,敬请各位读者关注我们的更新。

Ceshigo_CD的博客 4837

TCP 三次握手与四次挥手深入探究(大图解)

TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。面向连接:一定是「一对一」才能连接,不能像 UDP 协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;可靠的:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端;字节流:用户消息通过 TCP 协议传输时,消息可能会被操作系统「分组」成多个的 TCP 报文,如果接收方的程序如果不知道「消息的边界」,是无法读出一个有效的用户消息的。

流楚丶格念的博客 2904

从零构建工业级SPI-CAN网关:CH347与CH9431的软硬件协同设计实战

本文详细介绍了基于CH347和CH9431芯片构建工业级SPI-CAN网关的完整方案。通过硬件架构设计、Linux驱动定制、并发处理优化和EMC抗干扰测试,实现高可靠性通信网关,适用于工业自动化和汽车电子等严苛环境。

spark7igniter的博客 632

计算机网络 (39)TCP的运输连接管理

计算机网络——TCP的运输连接管理篇

m0_73399576的博客 1290

电子结构设计(EMD)十年知识总结

客官,你的面相是做结构设计的奇才~1. 材料选择【一出手的0.1s就败了】2. 钣金件设计【折来折去有啥难】3. 塑胶件设计【看着好复杂】4. 型材件设计【凭啥不让我设计】5. 电子结构(PCBA)设计【静电肿么要我背锅】6. 包装设计【要的就是金玉其外,败絮其中】7. 外观(工业)设计【好不好看,谁说了算】8. 标签设计【】9. 标准件选择【一个螺丝难倒英雄汉/提起螺丝,英雄气短】10. 计算仿...

wfeng86的博客 1569

PCL 直通滤波器【2026最新版】

PCL直通滤波器函数详解以及用于多字段滤波、强度滤波、获取某一时间范围内的点等高阶用法案例分享。博客长期更新,本文最近一次更新时间为:2026年3月11日。

点云侠的博客 40万+

计算机网络_TCP的传输连接管理(三次握手,四次挥手)

计算机网络 传输层 传输层是主机才有的层次,其既为应用层提供通信服务,也使用网络层的服务。 传输层的功能:第一是提供进程与进程之间的通信(网络层提供主机之间的通信);第二是复用和分用;第三是对收到的报文进行差错检测。 传输层的两个协议:TCP协议和UDP协议。 TCP协议: 面向连接的传输控制协议 需要建立连接和释放连接 可靠的面向连接,时延大,适用大文件传输 TCP协议的特点: 面向连接 每一个TCP连接只能有两个端点,点对点通信 可靠交付,无差错,不丢失,不重复,按序到达(可靠有序,不丢不重) T

Weiami的博客 376

微信小程序 - 解决支付报错 requestPayment:fail access denied (微信支付提示错误requestPayment:拒绝访问失败,真机运行测试或打包编译上线后报错问题)

微信小程序,微信支付,wx.requestPayment,微信小程序支付报错requestPayment:fail access denied,调不起微信,无法唤起支付弹窗,支付失败,支付权限没问题,小程序上线后支付报错,小程序真机测试支付报错,微信小程序苹果ios系统手机无法支付,小程序发布上线后支付报错,本地测试没问题,拒绝访问失败,小程序调起支付API requestPayment:fail:access denied微信开发者工具正常支付,权限没问题,微信公众平台,依旧报错怎么办,检查了appid都

🏆 前端领域优质创作者 1万+

计算机网络:(十六)TCP 的运输连接管理

TCP连接管理详解:从建立到释放的全过程 TCP通过"三报文握手"建立连接,确保双方在线并协商参数: 客户端发送SYN同步请求 服务器回复SYN+ACK确认 客户端发送最终ACK确认 这一过程解决资源分配和参数协商问题,避免历史连接干扰。 数据传输阶段严格按协商规则进行,保证数据有序可靠传输。 连接释放采用"四报文握手": 客户端发送FIN终止请求 服务器确认后进入半关闭状态 服务器发送FIN终止请求 客户端确认后等待2MSL时间 关键的TIME-WAIT状态(2MS

2402_83322742的博客 2412

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

计算机网络之传输层(tcp协议连接管理)

此外,确认号ack被设置为客户端序号seq加1,表示服务器已经收到了客户端的连接请求,并且期望收到客户端下一个报文段(该报文段序号seq为客户端初始序号加1)的数据。这个报文段的ACK标志位被设置为1,序号seq被设置为服务器已经传送过的数据的最后一个字节的序号加1,确认号ack被设置为客户端序号seq加1。这个报文段的FIN和ACK标志位都被设置为1,序号seq被设置为服务器在半关闭状态下可能又发送了一些数据的最后一个字节的序号加1,确认号ack保持不变,仍然是对客户端连接释放报文段的确认。

DKPT的博客 566

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

计算机网络——TCP连接管理

本文将会介绍如何建立和拆除一条TCP连接,TCP的连接会显著的增加人们感受到的时延(尤其是在Web冲浪时)。同时一些常见的网络攻击SYN洪范攻击利用了TCP连接管理的弱点。假定运行一台主机(客户)上的一个进程想和另一台主机(服务器上)的一个进程建立一条连接;客户端进程会首先通知客户端TCP,建立一个与服务器上层某个进程之间的连接。

qq_42544728的博客 1410

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

《计算机网络微课堂》5-8 TCP的运输连接管理

介绍 TCP 的运输连接管理

PeterJXL的博客 924

计算机网络中TCP连接管理,计算机考研——计算机网络(TCP传输连接管理)

计算机考研中目前部分院校部分专业是统考,计算机统考是考408计算机基础综合科目,考试内容自己参照考试大纲,考试科目是数据结构,操作系统,组成原理,计算机网络。大部分学校还是采用自主命题,自主命题的科目和考试内容因学校不同而不同,但是基本上是要考数据结构的,部分要考操作系统,计算机网络,计算机组成原理。今天给大家带来的是计算机网络中一块非常重要的内容,也是我们的高频考点,TCP传输连接管理中关于连接...

weixin_36329071的博客 262

《计算机网络微课堂》实验24 TCP的运输连接管理

本仿真实验的目的在于验证 TCP 使用三报文握手来建立连接,使用四报文挥手来释放连接。

PeterJXL的博客 760

tcp释放连接过程计算机网络,计算机网络——TCP传输连接管理

原标题:计算机网络——TCP传输连接管理计算机考研中目前部分院校部分专业是统考,计算机统考是考408计算机基础综合科目,考试内容自己参照考试大纲,考试科目是数据结构,操作系统,组成原理,计算机网络。大部分学校还是采用自主命题,自主命题的科目和考试内容因学校不同而不同,但是基本上是要考数据结构的,部分要考操作系统,计算机网络,计算机组成原理。今天给大家带来的是计算机网络中一块非常重要的内容,也是我们...

weixin_42356844的博客 517

计算机网络基础:TCP 的传输连接管理

TCP传输连接管理是指,TCP协议通过一系列报文交互(控制位、序列号协同),实现两台主机之间逻辑连接的建立、维护与优雅释放,并通过状态机跟踪连接的当前状态,确保连接的双向性、可靠性,同时避免连接泄露、资源浪费的一套核心机制。很多人会疑惑:“两次握手不行吗?”答案是不行,核心原因有两个,本质都是“避免旧连接残留报文干扰新连接,确保连接的可靠性”:第一次握手:客户端→服务器,仅能确认“客户端能发送,服务器能接收”;第二次握手:服务器→客户端,仅能确认“服务器能发送,客户端能接收”;

梁辰兴的博客 1016

【计算机网络】传输层 : TCP 连接管理 ( TCP 连接建立 | 三次握手 | TCP 连接释放 | 四次挥手 )

一、TCP 连接管理、 二、TCP 连接建立、 三、TCP 连接建立 相关报文段 字段、 四、SYN 洪泛攻击、 五、TCP 连接释放、

让 学习 成为一种 习惯 ( 韩曙亮 の 技术博客 ) 1926

计算机网络 TCP 的运输连接管理——详记

TCP 的连接建立 运输连接的三个阶段 TCP 是面向连接的协议。 TCP 连接有三个阶段: 连接建立 数据传送 连接释放 TCP 连接的管理就是使 TCP 连接的建立和释放都能正常地进行。 TCP 连接建立过程中要解决的三个问题 要使每一方能够确知对方的存在。 要允许双方协商一些参数(如最大窗口值、是否使用窗口扩大选项和时间戳选项以及服务质量等)。 能够对运输实体资源(如缓存大小、连接表中的项目等)进行分配。 客户—服务器方式 TCP 连接的建立采用客户服务器方式。 主动发起连接建立的应用进程叫

qq_44647223的博客 683

【计算机网络】实验17:TCP的运输连接管理

本文主要是介绍了计算机网络实验17,即TCP的运输连接管理,属于计算机网络实验专栏

superiony的博客 1649

郑州大学计算机网络实验04 TCP协议探索和连接管理分析

分析连接建立后,第一个TCP客户发给TCP服务器的报文段,以及第一个TCP服务器发给TCP客户的报文段的首部,截图并填写表4.1和表4.2。3.在主机ns56A的模拟终端中,输入如下命令,将该命令被发送到主机ns57C的shell中,读取文件3500.0,命令执行结果通过TCP连接发送回主机ns56A。执行上述命令后,Linux将在网络命名空间ns56A中开启bash交互,之后输入的Linux命令均在ns56A中执行,相当于模拟了主机ns56A的终端。4.在你的实验结果中,通信双方使用窗口扩大选项了么?

unbug1的博客 2613
上一篇: [Nginx]-入门知识
下一篇: [计算机网络]-TCP-概述
Pacifica_
博客等级 码龄5年 60粉丝 88原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值