[TCP] TCP/IP 基础知识词典(2)

我想统计一下,TCP/IP 尤其是TCP协议,能搜到的常见的问题,整理起来,关键词添加在目录中,便于以后查阅。

目前预计整理共3篇:

[TCP] TCP/IP 基础知识问答 :基础知识
[TCP] TCP/IP 基础知识问答(2) :TCP协议相关知识
[TCP] TCP/IP 基础知识问答(3) :UDP协议相关知识

文章目录

  • TCP协议相关知识
    • 什么是TCP
    • MTU和MSS分别是什么?
    • 沾包和拆包
      • 现象
      • 对策
    • TCP头包含哪些信息
    • 常见TCP的连接状态有哪些?
    • TIMEWAIT状态存在的意义
    • TIMEWAIT过多的危害
    • 服务器出现大量 CLOSE_WAIT 状态的原因
    • 如何优化TIMEWAIT
    • 为什么需要三次握手,两次不行吗?
    • 三次握手的过程可以携带数据吗
    • 挥手为什么需要四次
    • 挥手可以是3次吗
      • 延迟应答
    • 在四次挥手中,为什么发起端进入TIME_WAIT状态要有2MSL等待
      • 什么是MSL
      • 为什么等待2MSL
    • 什么是半连接队列
    • listen的第二个参数
    • 没有 listen,能建立 TCP 连接吗?
    • 服务端如果只 bind 了 IP 地址和端口,而没有调用 listen 的话,客户端可以和服务端通信吗?
    • SYN攻击是什么
    • TCP如何确保可靠性的
    • 序列号
      • ISN(Initial Sequence Number)是固定的吗
      • 序列号回绕了怎么办
      • TCP时间戳
    • TCP 重传机制
      • 超时重传
        • RTT
        • RTO
      • 快速重传
        • 为何快速重传是选择3次ACK
      • SACK
      • D-SACK
    • TCP 流量控制
      • 滑动窗口
        • 窗口的大小
        • 累计应答
        • 滑动窗口缩放因子
        • 发送窗口的控制
        • 接收窗口的控制
        • 滑动窗口的流量控制
          • 死锁
          • 死锁的解决方法
      • 糊涂窗口综合征
        • 对策
          • 让接收方不通告小窗口
          • 延迟确认
          • Nagle算法
    • TCP 拥塞控制
      • 拥塞窗口
        • 慢启动
          • 慢启动门限
        • 拥塞避免
        • 拥塞发生
          • 超时重传- 拥塞发生算法
            • 门启动门限ssthresh初始值
          • 快速重传- 拥塞发生算法
        • 快速恢复
        • 超时重传的拥塞算法图像
        • 快速重传的拥塞算法图像

TCP协议相关知识

什么是TCP

TCP是面向连接的,可靠的,基于字节流的通信协议。

MTU和MSS分别是什么?

MTU是最大传输单元(maximum transmission unit)。
由硬件规定,比如以太网是1500字节.

MSS是最大分节大小(maximum segment size)。
在TCP传输中,MTU - IP头长度 - TCP头长度 == MSS, 在通信时,发送端会在TCP头中包含MSS的大小

沾包和拆包

现象

沾包现象,不是TCP的bug,而是TCP传输带来的特点。

在程序执行的过程中,并不是调用send()函数后,就会直接发送出去,而是把应用层缓冲区中的

数据拷贝到内核缓冲区中,再拷贝到TCP协议栈的缓冲区中,然后由TCP协议栈来控制发送。

协议栈可能会把多个比较短的数据,合并成一个包来发送。也可能汇报超过MSS大小的数据,拆分成多个包来发送。

TCP协议是基于字节流的,协议栈理解的数据是没有边界的,没有包的概念。
沾包和拆包中的包,是用户态理解的概念,例如我多次调用Send(),就是用户态发出了多个包到内核态,如果这多个包都缓存在协议栈缓冲区等待发送,协议栈不会区分这是由几个Send()传输过来的数据,可能直接把所有数据都发出去了。

对策

  • 发送固定长度的数据
  • 用特殊结束符,比如\r\n,标记本条数据结束
  • 给数据包添加头和尾
    另外,如果程序设计式样允许,可以再Send()一次后,Sleep()一下,避免数据被沾包合并发送。

TCP头包含哪些信息

在这里插入图片描述
TCP头包含以下内容:
源端口和目的端口(IP地址在IP头中),各16位,共4字节;
序列号和确认号,用于TCP包的顺序确认,各4字节,共8字节。
头部长度,TCP存在选择字段,所以TCP头是可以变长的(20-60字节),长度存储在此字段,4位,和保留字段g共占8位,1个字节。
标志字段标志这个TCP包的作用,最常见的是ACK标志。
常用的标志位是:
ACK:确认序号有效标识
PSH:告诉协议栈,应尽快讲此报文交付给应用层
RST:重建连接标识。即TCP连接出现错误,连接断开,需要重新建立连接
SYN:发起连接时的标志
FIN:释放连接
窗口大小这个值是接收端期望接收的字节数。窗口最大为65535字节。(如果有缩放因子选项,还要另外计算)。因为滑动窗口机制,需要告诉发送端,接收端还有多少窗口大小能接收数据。如果发送端,收到回复包中窗口大小的值小于MSS或者等于0,则说明接收端接收窗口空间不足,发送端协议栈会暂停发送数据。
校验和发送端协议栈计算和写入,接收端进行校验数据
紧急指针只有当URG标志置1时紧急指针才有效。TCP的紧急方式是发送端向另一端发送紧急数据的一种方式。紧急指针指出在本报文段中紧急数据共有多少个字节(紧急数据放在本报文段数据的最前面)。不常用。
以上为固定字段,共20字节
选项 会存储一些特殊选项,例如:窗口缩放因子,选择性ACK等


参考:TCP头部格式和封装

常见TCP的连接状态有哪些?

共11个状态。
在这里插入图片描述
LISTEN:socket在执行listen()后,会进入LISTEN状态,监听套接字

SYN_SEND:主动端在发出第一次握手的SYN包后会进入这个状态

SYN_RECV:被动端在收到第一次握手包,发出第二次握手的SYN ACK包后,会进入这个状态

ESTABLISHED:在双方完成三次握手后,都会进入这个状态,表示建立连接

(四次挥手)

FIN_WAIT1:主动断开端发送完第一次挥手的FIN ACK包后进入这个状态

CLOSE_WAIT:被动端在发出第二次挥手的ACK 包后,进入这个状态

FIN_WAIT2:主动端在收到第二次挥手的包以后,等待收到第三次握手的时候的状态

LAST_ACK:被动端在发出第三次挥手后,进入这个状态

TIME_WAIT:主动端在发出第四次挥手后,进入这个状态,会保持2MSL再进入CLOSED

CLOSED:断开连接的状态

CLOSING:是应对四次挥手的意外情况,主动方发出FIN ACK包,同时收到了FIN ACK包,就会进入CLOSING状态,等到收到了ACK,就会进入TIMEWAIT状态。

TIMEWAIT状态存在的意义

  1. 确保被动段开方能顺利断开连接
  2. 防止收到上一个相同连接的历史报文

TIMEWAIT过多的危害

  1. 如果主动断开连接的是客户端,TIMEWAIT在客户端出现过多,可能会导致端口耗尽:
    每个TCP连接都是一个四元组,通过一个四元组就可以确定一个连接。
    当存在TIMEWAIT时,这个连接就被占用了。客户端想要再连接服务器就需要建立新的连接,使用新的端口,但是客户端的端口是有限的,所以TIMEWAIT过多,可能导致端口耗尽。

  2. 如果出现在服务端,说明服务器主动断开了大量连接。服务器出现大量的TIMEWAIT可能会占用系统资源。
    原因可能如下:

  • 出现了大量的HTTP短连接。不论客户端还是服务端的HTTP头出现Connection:Close 都会使HTTP变成短连接,服务器会主动断开连接。这要排查服务器或者客户端时候哪里设置了Connection:Close ,要改成长连接
  • HTTP长连接超时。这要排查,是不是什么问题导致客户端长时间无法向服务端发包
  • HTTP 长连接超出允许的数量。

服务器出现大量 CLOSE_WAIT 状态的原因

原因是服务器作为被动断开方,因为某些原因没能调用close()结束连接。
需要调查为什么没有执行close()。

如何优化TIMEWAIT

reuseaddr
reuseport

为什么需要三次握手,两次不行吗?

不可以。
三次握手的目的:

  • 确认两端手法能力正常
    第一个握手包,证明发起端,发送数据能力正常。
    第二个握手包,证明接收端,接受能力和发送能力正常。
    第三个握手包,证明发起端,接收能力正常。
    如果只有两次我收就建立连接,而发起端是无法发出第三次握手包的,那就会导致这个连接一直占用连接队列。
  • 避免历史报文的影响
    如果两次握手就建立连接。
    客户端发出一个SYN包,然后宕机,立即重启后,又发出一个新SYN包。
    如果服务器先收到旧SYN包,服务器返回一个SYN+ACK包
    ,此时服务器就认为连接已经建立了。
    但是客户端收到这个SYN+ACK包后,通过ack num 发现不是自己刚刚发出的,不会进入ESTABLISHED状态,没有建立连接,返回一个RST报文。
    在收到RST报文前,服务器以为建立了连接,有可能给客户端发送出数据了,浪费服务器资源。

而三次握手就可以避免这个问题。
服务器先收到旧SYN包,回复一个SYN+ACK包。
客户端收到以后,通过ack num 发现不是自己刚刚发出的,不会进入ESTABLISHED状态,没有建立连接,返回一个RST报文,服务器收到以后也不会进入ESTABLISHED状态。等收到信的SYN包后,再进行三次握手建立连接。

  • 同步序列号
    客户端发出SYN包, 以及初始化的syn num。
    服务器收到以后,发出SYN+ACK包,发出初始化的syn num 和 ack num,ack num是SYN包的shn num + 1,代表服务器已经同步客户端的序列号。
    客户端收到服务器的SYN+ACK包后,获取了服务器的syn num ,同步了服务器的序列号。
    服务器收到客户端发出的ACK包,知晓客户端已经同步了服务器的序列号。

参考:TCP 三次握手与四次挥手面试题

三次握手的过程可以携带数据吗

第三次握手可以。


参考:TCP第三次握手能携带数据吗?做个实验就知道!

挥手为什么需要四次

第一次挥手包,是发起端告诉接收端,我数据处理完了,要断开连接。
第二次挥手包,是接收端告诉发起端,我知道了,等我处理数据。
第三次挥手包,是接收端告诉发起端,我数据处理完了,要断开连接。
第四次挥手包,是发起端告诉接收端,我知道了,连接断开。

此时才可以确认双方都同意要断开连接了,没有数据发送了。

挥手可以是3次吗

可以。
被动挥手端 在收到第一次FIN包后,会进入CLOSE_WAIT状态,返回ACK包。等待数据处理完成,然后再发送FIN包。

如果满足以下条件,可以实现ACK包和FIN包一起发送。
1.没有应用层数据需要处理
2.TCP启动了延迟确认

延迟应答

延迟应答是默认开启的。
可以通过setsockopt 的 TCP_QUICKACK 选项关闭延迟确认。

接收方如果每次收到来自发送发的数据包后都立刻回复确认应答的话,可能会返回一个较小的窗口,这个窗口是用来通知发送方下次发送数据的大小。主要是因为接收方会先将数据放到缓冲区,待上层应用层将这些数据取走。而由于刚收到数据,应用层还没来得及取,此时缓冲区的可用空间变小,就会通知发送方要减少下次发送的数据长度。
当接收端收到这个小窗口的通知以后,会以它为上限发送数据,从而又降低了网络的利用率。

除此之外,如果只是单纯的发送一个确认应答,代价又会很高。因为IP头部有20字节,TCP头部也有20字节(此处不计选项)。
————————————————
原文链接:https://blog.csdn.net/LOOKTOMMER/article/details/121522110

所以TCP的延迟确认,就是:

  • 当没有响应数据要发送时,ACK 将会延迟一段时间,以等待是否有响应数据可以一起发送
  • 当有响应数据要发送时,ACK 会随着响应数据一起立刻发送给对方(捎带应答
  • 如果在延迟等待发送 ACK 期间,对方的第二个数据报文又到达了,这时就会立刻发送 ACK
    延迟确认等待时间大概100-200ms。

在数据包通信频繁的时候,延迟确认是有好处的。但是如果数据包通信不频繁,延迟确认可能会导致通信效率降低,可以配置系统参数缩短延迟确认时间,或者setsockopt 关闭延迟确认。

在四次挥手中,为什么发起端进入TIME_WAIT状态要有2MSL等待

什么是MSL

MSL是报文最大生存时间(maximum segment lifetime),即一个数据包在网络中最大存在的时间。

为什么等待2MSL

发起挥手方在发出第四次挥手包后,进入TIME_WAIT状态,但是有可能这个包丢失了。发出第三个挥手包的接收方,迟迟等不到第四个挥手包,他会等Min(1MSL,超时时间)的时间,重发第三个挥手包。
重新发出的第三个挥手包,最长会经过1 * MSL 时间,到达发起方。
这样就出现了2
MSL时间。

等待2 MSL时间,可以避免,因为丢包而导致的对端无法正常断开连接。

另外,也是为了防止网络中还有发给发起方的数据包没有收到,如果没有2MSL的等待,发起方断开连接后,迅速重启了连接,可能会收到上一个连接的包。

什么是半连接队列

如果服务器接收到了客户端发来的第一次握手包,会把这个连接放入半连接队列。
当三次握手完成后,会把这个连接放入全连接队列,等待Accept()函数调用。

listen的第二个参数

listen函数的第二个参数,是backlog。
这个参数的含义,不同的地方有不同的解释,比如与有的地方规定他是半连接队列和全连接队列的和。

在linux的新版本和Windows中,他表示的是全连接队列的大小。

而半连接队列的大小,Linux系统中由系统参数tcp_max_syn_backlog来控制,
全连接队列的大小,取backlog参数和系统参数somaxconn的较小者。
Linux中,在listen状态下,netstat或者ss 命令显示的 RECV-Q表示当前accept队列大小,SEND-Q表示accept队列的最大大小。

Windows中也有SOMAXCONN的宏,如果Listen()的第二个参数设置为这个宏,就会采用系统中的最大合理值来设置全连接队列的大小。

没有 listen,能建立 TCP 连接吗?

可以。
TCP Socket 在Bind()后,就可以connect它自己的IP和Port。

服务端如果只 bind 了 IP 地址和端口,而没有调用 listen 的话,客户端可以和服务端通信吗?

不可以。服务端的TCP的半连接队列和全连接队列是在Listen()的时候实现的。
由于没有Listen(),也就没有队列,没有地方存储客户端的连接。

如果此时,客户端对服务端发起了连接建立,服务端会回 RST 报文。

SYN攻击是什么

服务端在收到第一次握手的SYN包以后,会把连接加入半连接队列,为连接分配资源,并发处二次握手的包,

等待第三次握手的包,如果第三次握手的包超时没有收到,就会再次重发二次握手的包,要重发好几次,才会把连接移出半连接队列。

SYN攻击就是攻击者模拟大量的IP地址,向服务端发送大量的SYN包,来占用服务器资源,使服务器无法响应正常的连接。

在linux中可以用 netstat 命令检查处于SYN_RECV状态的TCP连接,如果出现很多随机地址,可能是SYN攻击。

通常现在的网关已经可以过滤SYN攻击了。
也可以在系统这设置减少超时事件、或者增大半连接队列。
也可以通过SYN Cookie技术防御SYN攻击。


半连接队列、全连接队列、SYN攻击,参考:
会把这个连接放入半连接队列。

TCP如何确保可靠性的

  1. 三次握手、四次挥手,建立可靠连接
  2. 通过序列号进行应答机制,可以丢弃重复的包,可以发现丢包
  3. 超时重传避免丢包
  4. 拥塞控制,避免网络拥堵导致的丢包
  5. 流量控制,避免接收方处理不完数据,接口窗口占满导致的丢包
  6. 校验和,TCP头中有校验和数据段,可以用来校验数据是否损坏

序列号

TCP头中有序列号(seq num)和确认号(ack num)各占4字节。
TCP协议会为发送的数据中的每一个字节分配序列号,本次发送的包中的第一个字节的序列号就是TCP头中的seq num。当对端收到了这个包以后,返回的确认包中,ack num 就是 接收到的seq num + 数据字节数 + 1。

ISN(Initial Sequence Number)是固定的吗

不是,初始的seq num不是固定的,是随机的。
是为了避免,收到了就得连接中的包的

序列号回绕了怎么办

序列号只有32位,也就是一个无符号int型的大小,是存在序列号用尽,从头开始的可能的。
为了避免这种情况,就有了TCP时间戳选项。

TCP时间戳

TimeStamp选项是默认开启的,它会随着时间增长,如图。
如果出现树序列号绕回,可以通过时间戳比较是不是最近的通信。
时间戳共8个字节,4个字节是发送该包的事件,4个字节是
在这里插入图片描述

TCP 重传机制

通过序列号与确认号,TCP协议可以确保数据的有序传输。也可以发现有哪些数据丢包了。当发生丢包时,就会启动重传机制。

超时重传

RTT

RTT是往返时延,Round-Trip Time ,即一个数据包发出,到收到确认包的时间。往返时延是一个动态变化的值。

RTO

RTO是超时重传时间,Retransmission Timeout ,RTO应该略大于RTT。

当发出的数据包丢失或者确认包丢失,都会引起超时重传。
TCP协议栈发出包的时候,会启动一个定时器,当达到RTO时间时,即发生了超时,认为数据包丢失,会再次发出包,然后重启RTO计时,此时的RTO应该是上一次的2倍。

这样每一次重新计算的RTO都是上一次的两倍,一般会进行3轮超时重传,如果还没有收到确认应答,会在等待一段时间后(2MSL?)关闭连接。

一旦发生了超时,会被认为网络拥塞,就会触发TCP的拥塞控制策略。

快速重传

为了避免每次都等到超时时间到了,才开始重传。TCP还有快速重传机制。
如果发送端发送了多个包,序列号为,1、2、3、4、5。
接收端收到了1,回复 ACK ack num = 2;
没收到2。
收到了3,回复 ACK ack num = 2;
收到了4,回复 ACK ack num = 2;
此时虽然没到超时事件,但是发送收到了连续三个确认包,要求发送2号包,就可以判断2号包丢包了。
此时虽然没有超时,客户端会立刻重传2、3、4、5包。
因为客户端不知道接收端除了2号包,3、4、5号包有没有收到,如果只重传了2号包,如果3号也丢包了,那还要等超时或者3次3号包的确认包才能确定丢包,效率很低,所以会全部重传。

如果想要避免全部重传,可以使用SACK(选择性重传)。

为何快速重传是选择3次ACK

3次是一个经验值。如果是2次ACK就进行重传,会收到更大的包乱序的影响。
详细参考:TCP 快速重传为什么是三次冗余 ACK,这个三次是怎么定下来的? - 车小胖的回答 - 知乎

SACK

SACK,Selective Acknowledgment,选择性确认。
启动了SACK功能后,会在TCP头部的选项区域中,增加一个SACK字段。
当发生快速重传的时候,服务器可以在SACK字段中记录收到了哪些包。
这样发送端就可以只重传丢失的包了。

但是SACK不是默认开启的。
需要通信双方在三次握手协商是否开启。如下图,一个SYN包中有SACK选项。
在这里插入图片描述

D-SACK

D-SACK,Duplicate SACK, 用于接收方告诉发送方哪些数据重复接收了。
如果接收端的ACK确认包丢失了,会导致发送端重传,当接收端接收到重传包后,发现重复接收,就会发送D-SACK,告诉发送端,重复接收了,我已经收到过这个包。

D-SACK可以方便发送端判断网络状况。
D-SACK使用的是TCP头中SACK的字段。
在 Linux 下可以通过 net.ipv4.tcp_dsack 参数开启/关闭这个功能

TCP 流量控制

滑动窗口

为了避免发送一个包,必须等到收到确认包才能继续发下一个包的低效率通信。
TCP协议采用了窗口机制。
每个TCP协议栈都一个发送窗口,一个接收窗口。窗口就是一个动态的缓冲区,当服务端的接收空口空闲的时候,客户端可以不必等待确认应答到来,就向服务端发送多个包。窗口动态变化的过程就是滑动窗口机制。

窗口的大小

在TCP的头中,有一个Windows字段,这就是窗口的大小。是接收方用于告诉发送方自己还有多少的接收窗口大小。发送方不会发送超出接收方窗口大小的数据。强行发出会导致数据丢失。

累计应答

由于接收端有接收窗口,可以接收多个TCP报文 ,所以即使中间有个报文的去人包丢失了也没关系,只要最终的报文的确认包成功发出去了就好。发送端你收到了最终发送的报文的确认包就知道前面全部数据都收到了。这就是累计应答。

滑动窗口缩放因子

滑动窗口缩放因子,Window Scaling。
在TCP的头的Windows字段,记录了窗口的大小,但是它是16位的,最大就是65536,在如今已经不满足要求了。
TCP头还有缩放因子选项,可以扩大窗口。

假设window scale为7,也就是要将Window Size的值左移七位,即乘以128。window scale最大为14。
在整个双方的交互过程中,发送方和接收方Window size scaling factor乘积因子必须保持不变,但是发送方的乘积因子和接收方的乘积因子可以不同,由各自决定。
——————
https://www.cnblogs.com/hongdada/p/11171068.html

例如这个包,TCP的头的windows字段是64240,缩放因子是左移8位,即放大256倍。此时表示的窗口的大小是:64240 * 256。
在这里插入图片描述

发送窗口的控制

发送窗口部分的内存,通过3个指针进行管理,其中两个绝对指针,一个相对指针。
绝对指针是指向具体序列号数据的指针,相对指针是通过绝对指针地址计算后计算出来的指针。

第一个绝对指针,指向已发送但是没收到确认数据的第一个字节。
第二个绝对指针,指向还没发送且可以发送的数据的第一个字节。
第三个相对指针,指向还没发送且不可发送的数据的第一个字节,通过第一个指针 + 滑动窗口大小(滑动窗口大小是会变化的)得到。

第一个指针指向的数据 和 第二个指针指向的数据,合在一起就是发送方的滑动窗口。
第二个指针指向的数据,就是可用窗口。

接收窗口的控制

发送窗口部分的内存,通过**=2个指针**进行管理,其中一个绝对指针,一个相对指针。

第一个绝对指针,指向可以接收数据但是还没收到数据的空间的第一个字节。
第二个相对指针,指向还没收到数据并且不可接收数据的空间的第一个字节,通过第一个指针 + 滑动窗口大小(滑动窗口大小是会变化的)得到。

第一个指针指向的数据 就是发送方的滑动窗口。在第一个指针之前,是已收到并且已确认的数据。(由于ACK直接由TCP协议栈回复,默认无应用延迟,不存在“已接收未回复ACK)

滑动窗口的流量控制

TCP 通过让接收方指明希望从发送方接收的数据大小(窗口大小)来进行流量控制。

如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,直到窗口变为非 0 为止,这就是窗口关闭。

死锁

在服务器因为某些情况,导致窗口关闭后,客户端不可以发送数据。
服务器窗口再度开放后,会给客户端发送一个ACK报文,告诉窗口非0。

如果这个报文丢失了,就会造成死锁。

死锁的解决方法

TCP 为每个连接设有一个持续定时器,只要客户端收到服务端的关闭窗口通知,就启动持续计时器。

如果持续计时器超时,就会发送窗口探测 ( Window probe ) 报文。服务器在ACK报文中会告知窗口大小。如果还是关闭窗口,客户端就再次计时。

如果客户端多次进行窗口探测,服务器窗口都是关闭,可能会断开连接。

糊涂窗口综合征

本部分内容引用自:小林Coding - 糊涂窗口综合征
如果接收方太忙了,来不及取走接收窗口里的数据,那么就会导致发送方的发送窗口越来越小。
到最后,如果接收方腾出几个字节并告诉发送方现在有几个字节的窗口,而发送方会义无反顾地发送这几个字节,这就是糊涂窗口综合症。
要知道,我们的 TCP + IP 头有 40 个字节,为了传输那几个字节的数据,要搭上这么大的开销,这太不经济了。

所以,糊涂窗口综合症的原因是:
接收方可以通告一个小的窗口
而发送方可以发送小数据

对策方向是:
让接收方不通告小窗口给发送方
让发送方避免发送小数据

对策
让接收方不通告小窗口

当「窗口大小」小于 min( MSS,缓存空间/2 ) ,也就是小于 MSS 与 1/2 缓存大小中的最小值时,就会向发送方通告窗口为 0,也就阻止了发送方再发数据过来。

延迟确认

==延迟确认也有避免发送小窗口的作用==参照上面延确认内容

等到接收方处理了一些数据后,窗口大小 >= MSS,或者接收方缓存空间有一半可以使用,就可以把窗口打开让发送方发送数据过来。

怎么让发送方避免发送小数据呢?
使用 Nagle 算法。

Nagle算法

Nagle :/ˈneɪgəl/

该算法的思路是延时处理,只有满足下面两个条件中的任意一个条件,才可以发送数据:
条件一:要等到窗口大小 >= MSS 并且 数据大小 >= MSS;
条件二:收到之前发送数据的 ack 回包;

Nagle算法会避免发送小数据。但是有些场合可能就是需要发送小数据。可以
可以在 SetSockopt中通过TCP_NODELAY 选项来关闭本Socket的Nagle算法。

TCP 拥塞控制

流量控制,是用来控制窗口的。
拥塞控制是根据网络状况而进行的控制。

一般来说,计算机网络都处在一个共享的环境。因此也有可能会因为其他主机之间的通信使得网络拥堵。
在网络出现拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失等,这时 TCP 就会重传数据,但是一重传就会导致网络的负担更重,于是会导致更大的延迟以及更多的丢包,这个情况就会进入恶性循环被不断地放大…
所以,TCP 不能忽略网络上发生的事,它被设计成一个无私的协议,当网络发送拥塞时,TCP 会自我牺牲,降低发送的数据量。
于是,就有了拥塞控制,控制的目的就是避免「发送方」的数据填满整个网络。
来源:小林 Coding - 拥塞控制

拥塞窗口

TCP用发送窗口,和拥塞窗口。
拥塞窗口是根据网络拥塞状况来动态变化的。
对于发送端来讲,需要获取接收端的接收窗口大小,也要获取拥塞窗口大小。
发送端的发送窗口 约等于 ming(接收端的接收窗口,拥塞窗口)

只要发生了超时重传,就会认为网络出现了拥塞,就会减小拥塞窗口。反之拥塞窗口会增大。

而拥塞窗口的变化,主要是依赖四个算法:
慢启动
拥塞避免
拥塞发生
快速恢复

慢启动

慢启动就是TCP刚建立连接时,在其实拥塞窗口的基础上,慢慢增大拥塞窗口的意思,其增大规则是:
发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1。

例如,当前窗口,发送方可以发送10个TCP报文,然后收到了10个确认应答报文。此时发送方的拥塞窗口增大到可以发送20个TCP报文。
慢启动算法是指数性增长。

慢启动门限

慢启动门限, ssthresh (slow start threshold),一般是65535 字节。
当 拥塞窗口 < ssthresh 时,使用慢启动算法。
当 拥塞窗口 >= ssthresh 时,就会使用「拥塞避免算法」。

拥塞避免

拥塞避免阶段,每当收到一个 ACK 时,cwnd 增加 1/cwnd。
此时是线性增长。

虽然拥塞避免阶段,拥塞窗口增长的速率放慢了,但是最终可能还是会进入拥堵,开始出现丢包。一旦出现丢包,进入拥塞发生阶段。

拥塞发生

在发生丢包后,会进入超时重传,或者快速重传。
不同的重传方式,对应了不同的拥塞发生算法。

超时重传- 拥塞发生算法

此时,慢启动门限ssthresh 设为 cwnd/2,同时cwnd 重置为 初始化值,然后再进行慢启动。

门启动门限ssthresh初始值

Linux 针对每一个 TCP 连接的 cwnd 初始化值是 10,也就是 10 个 MSS,我们可以用 ss -nli 命令查看每一个 TCP 连接的 cwnd 初始化值。
小林Coding - 拥塞发生

快速重传- 拥塞发生算法

此时认为网络情况没有那么糟糕。
拥塞窗口 =拥塞窗口/2 ,也就是设置为原来的一半,这里和上面一样。
但是慢启动门限设置为和拥塞窗口一样大。然后,进入快速恢复算法。

快速恢复

快速恢复和 拥塞发生-快速重传 算法搭配使用,此时认为网络拥塞没有那么糟。
本部分内容引用自:小林Coding - 快速恢复
在 拥塞发生-快速重传的基础上,拥塞窗口 = ssthresh + 3 ( 3 的意思是确认有 3 个数据包被收到了);
重传丢失的数据包;
如果再收到重复的 ACK,那么 cwnd 增加 1;
如果收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值,原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态;

超时重传的拥塞算法图像

在这里插入图片描述
来源:小林Coding - 拥塞发生

快速重传的拥塞算法图像

在这里插入图片描述
来源:小林Coding - 拥塞发生


参考:4.2 TCP 重传、滑动窗口、流量控制、拥塞控制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/402359.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode--代码详解 59. 螺旋矩阵 II

59. 螺旋矩阵 II 题目 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]]示例 2&#xff1a; 输入&a…

django配置视图并与模版进行数据交互

目录 安装django 创建一个django项目 项目结构 创建视图层views.py 写入视图函数 创建对应视图的路由 创建模版层 配置项目中的模版路径 创建模版html文件 启动项目 浏览器访问结果 安装django pip install django 创建一个django项目 这里最好用命令行完成&#xf…

力扣OJ题——随机链表的复制

题目&#xff1a; 138. 随机链表的复制 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 要求&#xff1a;构造这个链表的 深拷贝 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中…

websocket与Socket的区别

概念讲解 网络&#xff1a;通俗意义上&#xff0c;也就是连接两台计算器 五层网络模型&#xff1a;应用层、传输层、网络层、数据链路层、物理层 应用层 (application layer)&#xff1a;直接为应用进程提供服务。应用层协议定义的是应用进程间通讯和交互的规则&#xff0c;不…

数据库事物复习

事务 比如说将张三的银行账户拿出一千给李四&#xff0c;首先需要查询张三的账户余额&#xff0c;扣除1000&#xff0c;然后如果给李四加上1000的过程中出现异常会回滚事务&#xff0c;临时修改的数据会回复回去。 -- 1. 查询张三账户余额 select * from account where name …

【2024软件测试面试必会技能】Selenium(6):元素定位_xpath定位

XPATH是什么 XPATH是一门在XML文档中查找信息的语言&#xff0c;XPATH可用来在XML文档中对元素和属性进行遍历&#xff0c;主流的浏览器都支持XPATH&#xff0c;因为HTML页面在DOM中表示为XHTML文档。Selenium WebDriver支持使用XPATH表达式来定位元素。 Xpath常用如下6种定位…

安卓APP和小程序渗透测试技巧总结

本文章仅供学习和研究使用&#xff0c;严禁使用该文章内容对互联网其他应用进行非法操作&#xff0c;若将其用于非法目的&#xff0c;所造成的后果由您自行承担。 由于安卓7开始对系统安全性做了些改动&#xff0c;导致应用程序不再信任客户端证书&#xff0c;除非应用程序明确…

OpenTiny Vue 组件库适配微前端可能遇到的4个问题

本文由体验技术团队 TinyVue 项目成员岑灌铭同学创作。 前言 微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略&#xff0c;每个应用可以选择不同的技术栈&#xff0c;独立开发、独立部署。 TinyVue组件库的跨技术栈能力与微前端十…

搜维尔科技:【周刊】适用于虚拟现实VR中的OptiTrack

适用于 VR 的 OptiTrack 我们通过优化对虚拟现实跟踪最重要的性能指标&#xff0c;打造世界上最准确、最易于使用的广域 VR 跟踪器。其结果是为任何头戴式显示器 (HMD) 或洞穴自动沉浸式环境提供超低延迟、极其流畅的跟踪。 OptiTrack 主动式 OptiTrack 世界领先的跟踪精度和…

医药之链:基于Django的智能药品管理系统

框架 Python 3.7 django 3.2.13 Bootstrap&#xff08;前端&#xff09; sqlite&#xff08;数据库&#xff09;导包 pip install django3.2.13 pip install pandas pip install xlwt环境搭建 登录 zfx 123456

docker 容器内服务随容器自动启动

docker 容器内服务随容器自动启动 背景准备工作方案一&#xff0c;直接修改.bashrc文件&#xff08;简单粗暴&#xff09;方案二&#xff0c;编写启动脚本加入.bashrc文件&#xff08;文明一点&#xff09;制作nginx服务自启动镜像测试新镜像&#xff0c;nginx服务随容器自动启…

HGAME week2 web

1.What the cow say? 测试发现可以反引号命令执行 ls /f* tac /f*/f* 2.myflask import pickle import base64 from flask import Flask, session, request, send_file from datetime import datetime from pytz import timezonecurrentDateAndTime datetime.now(timezone(…

【Java多线程】分析线程加锁导致的死锁问题以及解决方案

目录 1、线程加锁 2、死锁问题的三种经典场景 2.1、一个线程一把锁 2.2、两个线程两把锁 2.3、N个线程M把锁&#xff08;哲学家就餐问题&#xff09; 3、解决死锁问题 1、线程加锁 其中 locker 可以是任意对象&#xff0c;进入 synchronized 修饰的代码块, 相当于加锁&…

OpenGauss数据库本地搭建并结合内网穿透实现远程访问

文章目录 前言1. Linux 安装 openGauss2. Linux 安装cpolar3. 创建openGauss主节点端口号公网地址4. 远程连接openGauss5. 固定连接TCP公网地址6. 固定地址连接测试 前言 openGauss是一款开源关系型数据库管理系统&#xff0c;采用木兰宽松许可证v2发行。openGauss内核深度融合…

【AIGC】开源音频工具AudioCraft

AudioCraft是一个开源框架&#xff0c;旨在生成高质量的音频&#xff0c;适用于音乐、声音生成和压缩等多种应用。 先听效果&#xff1a; aimusic 它由三个模型组成&#xff1a;MusicGen、AudioGen和EnCodec。 MusicGen&#xff1a; 这个模型使用了Meta拥有和特别许可的音乐进…

如何使用Docker本地部署Jupyter+Notebook容器并结合内网穿透实现远程访问

文章目录 1. 选择与拉取镜像2. 创建容器3. 访问Jupyter工作台4. 远程访问Jupyter工作台4.1 内网穿透工具安装4.2 创建远程连接公网地址4.3 使用固定二级子域名地址远程访问 本文主要介绍如何在Ubuntu系统中使用Docker本地部署Jupyter Notebook&#xff0c;并结合cpolar内网穿透…

UE4 C++联网RPC教程笔记(三)(第8~9集)完结

UE4 C联网RPC教程笔记&#xff08;三&#xff09;&#xff08;第8~9集&#xff09;完结 8. exe 后缀实现监听服务器9. C 实现监听服务器 8. exe 后缀实现监听服务器 前面我们通过蓝图节点实现了局域网连接的功能&#xff0c;实际上我们还可以给项目打包后生成的 .exe 文件创建…

edge安装fdm插件

下载 https://www.crxsoso.com/webstore/detail/ahmpjcflkgiildlgicmcieglgoilbfdp 安装 进入edge插件管理页面 edge://extensions/2. 将下载的crt文件拖到这个页面&#xff0c;就能自动安装了 在其他网页不能安装&#xff0c;会变成下载。

2024年noc比赛Coding创意编程赛项-创意实验室初赛模拟题

【单选题】 1.角色本来面向的方向是右方,执行下方积木后,角色面向的方向是() A.面向右上方 C.面向左上方 B.面向右下方 D.面向左下方 2.下列选项中关于图中按钮功能说法错误的是() A."本地传”按钮可以从本地电脑上传素材 B."重新画”按钮可以自己设计素材 C"…

QT的UI入门

二、UI入门 QWidget类&#xff08;熟悉&#xff09; QWidget类是所有组件和窗口的基类&#xff0c;内部包含了一些基础的界面特性。 常用属性&#xff1a; 修改坐标 x : const int 横坐标&#xff0c;每个图形的左上角为定位点&#xff0c;横轴的零点在屏幕的最左边&#xff0c…
最新文章