当前位置: 首页 > news >正文

Linux网络 - 再谈、详谈UDP和TCP协议

文章目录

  • 前言
  • 预备
    • netstat
    • pidof
    • cat /etc/services
  • 一、UDP协议
    • UDP协议端格式
    • UDP的缓冲区
    • 基于UDP的应用层协议
  • 二、TCP协议
    • 1.TCP协议段格式
    • 确认应答(ACK)机制
      • 三次握手
        • 疑问1 最后一次客户端发给服务端的ACK请求怎么保证服务端能够收到?
      • 四次挥手
        • 疑问2 为什么挥手是四次,握手只有三次?
    • 4位首部长度
    • 32位序号
    • 32位确认序号
    • 16位窗口大小
    • 16位紧急指针
      • 什么场景才会用到发送紧急数据
    • 六个标记位
    • 超时重传机制
      • 重复报文如何处理
      • 超时重传,那我们要超时多久才重传呢?
    • 连接管理机制
      • 三次握手状态
        • ESTABLISHED
        • 模拟三次握手状态变化
        • 全连接队列
        • 半连接队列
      • 四次挥手状态
        • TIME_WAIT等待多长时间呢?
          • 什么是MSL?
        • 模拟四次挥手环境
            • setsockopt
    • 流量控制
    • 滑动窗口
    • 延迟应答
    • 阻塞控制
    • 用户数据报和字节流
      • 数据粘包问题
    • TCP异常问题


前言

上一章我们已经学习了HTTPS协议,知道了HTTPS协议的基本原理。
本章我们将回过头来重新深度讲解UDP协议和TCP协议,


预备

这里再介绍一下之前用过的工具

netstat

netstat是一个用来查看网络状态的重要工具。

  • -n 拒绝显示别名,能显示数字的全部转化成数字
  • -l 仅列出有在 Listen (监听) 的服務状态
  • -p 显示建立相关链接的程序名
  • -t (tcp)仅显示tcp相关选项
  • -u (udp)仅显示udp相关选项
  • -a (all)显示所有选项,默认不显示LISTEN相关

pidof

之前我们想查看一个进程的pid,然后kill杀死它,我们一直都是使用的ps axj来查看的,pidof [进程名] 可以直接查看进程的pid。

在这里插入图片描述

cat /etc/services

可以查看服务器绑定的特殊知名端口号

比如
mysql服务器,使用3306端口
ssh服务器, 使用22端口
ftp服务器, 使用21端口
telnet服务器, 使用23端口
http服务器, 使用80端口
https服务器, 使用443端口

一、UDP协议

我们本次重谈UDP协议通过UDP报头的分析和UDP的发送缓冲区来讲解。

UDP协议端格式

在这里插入图片描述
首先对于16位的源端口号和16位的目的端口号,很好理解,这里就不再重复。

16位UDP长度是什么? 代表的是下面携带的数据的字节长度,通过16位的UDP长度,我们就可以分析出一个UDP报文可以携带的数据最多为2^16 - 1= 65535字节,64kb的数据。

16位UDP检验和我们这里不讲。

UDP的缓冲区

UDP只有接收缓冲区,没有发送缓冲区。
这是因为UDP协议属于不可靠、无连接协议。

不可靠代表UDP只需要向目标主机发数据即可,不考虑数据丢失等其他问题,也就不需要像TCP协议一样如果数据发送失败或者数据丢失、乱序,需要重传数据,也就不需要发送缓冲区

无连接代表使用UDP协议的主机不需要与目标主机建立连接。

因为没有发送缓冲区,所以我们调用sendto就是直接将数据从应用层直接交给OS的传输层,再向下交给网络层。

UDP具有接收缓冲区,是因为我们可能会同时收到一连串的报文,而应用层未必能来得及全部读完,所以就需要有接收缓冲区。
不过需要注意的是,如果接收缓冲区满了,之后再接收的报文会被丢失。

基于UDP的应用层协议

NFS: 网络文件系统
TFTP: 简单文件传输协议
DHCP: 动态主机配置协议
BOOTP: 启动协议(用于无盘设备启动)
DNS: 域名解析协议

二、TCP协议

本章主要重点是放在TCP协议上,首先我们需要再重新重视一下TCP的英文全称(Transmission Control Protocol)传输控制协议,那么它这里的"控制"是如何体现的?

我们也从TCP的报头来分析

1.TCP协议段格式

在这里插入图片描述

16位的源端口号和16位目的端口号我们这里也不再重复了。
由于TCP协议的报头格式比较复杂,所以我们拆开来讲

确认应答(ACK)机制

Tcp为确保可靠性,要尽可能的保证我发出去的报文是已经被对方收到的,如果没有被对方收到,就需要补发重传。 那么发数据的一方是怎么知道对方是否收到呢? 需要对方来告知,也就是确认应答。、

三次握手

在这里插入图片描述
SYN就是发送连接请求,我们等会还会再讲SYN。
ACK这里就是确认应答。

所以看上图的三次发送数据,也就是三次挥手。

第一次是客户端发给服务端SYN连接请求。

第二次是服务端给客户端发送第一次SYN的ACK确认应答,同时也向客户端也发送SYN连接请求。

第三次是客户端给服务端发送ACK确认应答。

这就是三次握手,同时我们可以发现客户端和服务端都通过至少发送和接受一次数据来验证了自身socket的全双工是否工作正常。

三次握手是Tcp协议中十分重要的一环,等会我们还会讲到。

疑问1 最后一次客户端发给服务端的ACK请求怎么保证服务端能够收到?

不能保证,不过大家可以想一想,就算服务端也再发一次ACK请求给客户端,那客户端要不要响应呢? 如果又发送响应是不是就一直这么循环下去了? 所以干脆我们就只需要三次握手就行了。

四次挥手

在这里插入图片描述
这里的四次挥手,与上面的三次握手也有异曲同工之妙。
第一次挥手是 客户端向服务器发送的FIN断开连接请求,就是告诉服务器我没有数据向你再发了,我想断开连接。

第二次挥手是 服务器对于客户端的断开连接请求发出ACK应答,证明我收到了你了连接请求了,但是不代表我同意关闭。 因为你客户端没数据发了,不代表我服务端没有数据再发了,所以中间服务端可以再向客户端发消息,而这个时候客户端也是能接受的,因为四次挥手还没有完成,不会断开连接。

第三次挥手是 当服务端也没有数据可以再发了,而且自身也处于CLOSE_WAIT状态,它也会向客户端发送FIN断开连接请求。

第四次挥手是 客户端对服务端的FIN断开连接请求发送ACK确认应答。当服务器收到了ACK之后,立马就将建立的连接断开并把资源释放了。

四次挥手是Tcp协议中十分重要的一环,等会我们还会讲到。

疑问2 为什么挥手是四次,握手只有三次?

因为我们可以看到握手的第二次,服务端在发送SYN请求的同时,还做了捎带应答ACK,所以才能压缩成三次。

那为什么我的挥手不能压缩成三次呢?
这是因为服务端在收到客户端的FIN断开连接请求之后,我的服务器可能还会有数据要继续发送,但是又要给客户端作应答表示我收到了你的FIN断开连接请求,所以也就不能作握手一样的捎带应答,所以也就必须要四次挥手。

4位首部长度

在这里插入图片描述
4位首部长度代表的就是TCP协议的报头长度,也就是整个报文除了正文数据的长度。

那么4位可以代表多少字节呢? 2 ^ 4 - 1 = 15,可是我们光16位源端口号到16位紧急指针都已经共有20字节了,这还不包括选项的长度。 所以这里的15的单位不是1字节,而是4字节。 这样通过4位首部长度我们就可以代表15 * 4 = 60字节了,而选项我们通过计算可以知道 ,选项最多可以有40个字节。

32位序号

讲32位序号之前,我们先来了解一些前备知识,当我们建立好连接之后,我们是可以同时连续发送多次请求给服务器的,而当报文数据在传输的时候,能不能保证我们传送过去的报文数据会被按序被目标主机接收?
答案是不能的,因为可能会存在不同报文不通过同一个网络传输过去的情况,而有些网络传输可能会比较缓慢。

那么如果目标主机接收到了这些没有按指定顺序到达的报文,并直接对报文做解析,势必会出现问题,TCP对于可靠协议是不能容忍这种问题的出现,所以也就需要保证报文能够按序被目标主机解析。 虽然不能控制报文到达的顺序,但是我们可以在报头中添加关于顺序的说明,这个顺序的说明,就是我们现在要讲的32位序号。

在这里插入图片描述
只需要对比我们的24位确认序号,我们就可以知道这一连串报文数据的顺序,服务端也就能按序解包报文。

需要注意的是,实际两个主机在进行通信时,我们最初通信时的起始序号可能并不是从1开始的,而是一个随机值,这个随机值是服务端和客户端在开始建立通信,也就是在三次握手的时候就开始协商好的,至于这是为什么?我们后面再讲。

32位序号等会我们还会详细再次讲解。

32位确认序号

既然Tcp要保证可靠性有了确认应答机制,那么我们就需要在确认应答中携带32位确认序号来保证我们已经读取了多少字节的数据。

在这里插入图片描述
当我们收到序号为1000的报文请求时,我们要做出ACK应答,应答就需要带上确认序号,确认序号是你的序号+1。 比如说我发了1000字节的数据给服务端,确认序号是1000,服务端就需要在它的应答报文里的确认序号里填上1001,代表1001之前的数据我读取到了。

通过这样的方式,我们就可以保证客户端知道我们的服务端已经读了多少字节的数据。

如果说我分四次发报文数据一共发了4000个字节,服务端只收到了其中3个或两个,那么服务端就必不可给我发一个确认序号为4001的响应报文,当客户端发现服务端没有完全读完,然后根据响应报文中的确认序号,知道对方收到了多少字节数据,就知道我需要重发,从多少开始重发。

32位确认序号等会还会再相信讲解。

16位窗口大小

窗口大小指的是无需等待确认应答而可以继续发送数据的最大值。

当我们的服务端压力比较大的情况下,接收缓冲区的内容没办法短时间内快速取出,而客户端又在不断向服务端发送报文数据的时候,接收缓冲区的剩余空间就在不断缩小。

这个时候就需要告知客户端,服务端的接收缓冲区快顶不住了,让客户端发慢一点或者别发了,再发接受缓冲区满了,满了的话再发的报文就会被丢弃。 所以也就需要16位窗口大小来告诉客户端服务端的接收缓冲区剩余大小,也可以理解为可以继续发送数据的最大值。

16位窗口等会我们还会详细讲解。

16位紧急指针

这个我们基本很少会用到,只有在传送紧急数据的时候,16位紧急指针才会被设置,并且还有URG标记位也会被设置,当URG被置1时,说明有紧急数据。
在这里插入图片描述

16位紧急指针是报文正文的偏移量,且紧急数据最多只能有1个字节。
我们可以通过在send的flags参数设置MSG_OOB来携带带外数据(也就是紧急数据)
在这里插入图片描述

什么场景才会用到发送紧急数据

一般我们很少几乎用不到发送紧急数据,很少的应用程序会用到这个。

但是有一种场景我们可以了解一下,就是当客户端向服务端发送一个报文,确始终得不到应答,但是我又能确认连接是没问题的,我的消息发送是没问题的。 我就需要知道服务器那边是处于一种什么情况,为什么对我的报文不做回应,这个时候就可以穿一个紧急数据过去,然后服务端也专门用一个线程用来接收紧急数据,然后再应答回去告诉客户端服务端可能处于一种什么状态,没办法及时处理你的报文。

六个标记位

在这里插入图片描述
URG:标记紧急指针是否有效,是否携带紧急数据。
ACK:只要有确认应答属性,就必须要带上ACK标记位!确认序号是否有效。
PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走,
RST: 对方要求重新建立连接; 我们把携带RST标识的称为复位报文段。
SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段。
FIN: 通知对方, 本端要关闭了。

超时重传机制

为了确保数据会被对方接收,我们已经知道了有确认应答机制,只有接收数据一方收到数据后发出ACK确认应答,并且这个应答被发数据的一方也接收到了,至此,才完成一次数据的通信。

那么如果数据在中途丢包了呢? 或者应答在网络传输过程中也丢包了呢? 又或者我们的数据被由于网络状况不好,阻塞在网络中了呢?
在这里插入图片描述
以上图为例,只要我们的主机A没有收到确认应答(确认应答是携带确认序号的),就无法确定主机B是否真的收到了数据,那么我们就要在特定的时间间隔进行数据重传。

所以不管是上面提出的哪三种情况,我们的超时重传机制都能够处理这些情况,其中如果是网络状态不好,造成主机A发出的数据阻塞在网络中,主机A也需要进行超时重传,如果这个时候第一次发出的数据由于网络又通畅了,被主机B接收到了,两次发出的数据就都有可能会被主机B收到,那么对于主机B就收到了重复的报文数据,对于重复的报文数据我们应该如何处理呢?

重复报文如何处理

因为我们的报文数据是携带了序号的,所以完全不用担心收到重复报文,只需要对比序号,将多余的重复报文直接丢弃即可。

超时重传,那我们要超时多久才重传呢?

超时重传的这个时间间隔是比较难以界定的,如果设置的时间间隔太短,可能由于网络本身传输就需要一定时间,就会导致时间间隔内一直都收不到应答,并不断重发重复的报文数据。 那如果设置的时间间隔太长,又会影响整体的重传效率,

所以,针对这一问题,Tcp采用的是一种动态的时间间隔。
这个时间间隔一500ms为单位,之后判定重发的时间间隔都是这个单位的整数倍。
比如第一次超时重传的时间间隔为500ms,500ms内没有收到应答之后,就重传一次。然后第二次的时间间隔就变为了500 * 2 = 1000ms,而如还是没有收到应答,就再重传一次,以此类推。 等累计到了一定次数,Tcp就会认为网络或者对方出现问题,强制关闭连接。

连接管理机制

在这里插入图片描述
这里的连接管理机制,我们主要详谈客户端与服务器在三次握手、四次挥手过程中的状态变化。


三次握手状态

当客户端第一次发送SYN请求的时候,自身tcp连接处于SYN_SENT状态。
当服务端收到SYN请求后,向对方发送应答和SYN请求时,将自身置为SYN_RCVD状态。
当客户端接收到服务端的SYN请求和应答时,给服务端发送ACK应答的同时,将自身tcp连接置为ESTABLISHED,ESTABLISHED代表自身连接已经建立好了。

ESTABLISHED

只有tcp连接处于ESTABLISED才认为是建立好了连接!

所以,对于第一次发送握手请求的主机来说,当我发出应答时,tcp就默认建立好了连接。
对于另一台主机而言,只有我收到了应答,才能算式建立好了连接。

那么为什么这样设计有什么好处呢?

如果我们的请求连接的过程造成了请求报文或应答丢包的情况,前两次握手都还好,因为我们双方都没有建立好连接,都是处于请求连接的状态。 那么如果第三次握手的应答丢失了,就会造成了发送第一次握手请求的主机已经建立好了连接,但是由于应答丢失,另一台主机收到确认应答也就没有真正建立好连接。 这样就会造成发送第一次握手请求的主机单方面以为自己建立好了连接。而为了维护这个连接,OS就需要耗费资源去维护,也就浪费了系统资源。

一般实际情况其实都是客户端向服务端发起连接,所以这种机制就将上面会发生的情况的后果成本转移到了客户端身上,而不是服务端。毕竟服务端是要为许多客户端服务的,本身就需要可能维护大量的tcp连接,OS资源的浪费就有可能导致服务器挂掉。

如果发生上述情况,我们可以再进一步向下推演,客户端单方面以为双方都建立好了连接,于是开始传数据,但是我们的服务端还正处于SYN_RECV状态,突然收到了一段不是应答的数据,服务端就大概知道什么情况了,于是就发送一段将RST标记位为1的报文报头数据,要求客户端重新建立连接。


现在再回过头来看三次握手的这些状态。

我们能否看到这三次握手的状态的变化呢?

模拟三次握手状态变化

通过我们之前写的TcpServer代码,我们进行一些改动
在这里插入图片描述
为什么这里要特意不进行accept呢? accept函数其实并不参与连接过程,Accept的作用主要在于将内核传输层中建立好的连接拿到应用层。 不进行accept可以更好的让我们看到模拟实验现象。

在这里插入图片描述
将backlog设置为1,backlog是我们listen的参数,之前我们一直不理解这个参数是什么意思,等会我们就知道了。

我们提前准备好3个客户端窗口,1个服务端窗口,2个检测tcp连接的窗口。

现在现将服务端运行起来,并让两个客户端连接服务端,我们就会看到这样的一个现象。
在这里插入图片描述
而现在,我们开始让第三个客户端连服务端,会发生什么?

在这里插入图片描述
这里我们第三个客户端的连接在服务端是处于SYN_RECV状态。 可是为什么呢?

导致的原因有两个,一个原因是我们没有进行accept,我们的backlog被设置为了1。

全连接队列

当我们的客户端和服务端进行好了三次握手之后,我们成立建立了连接,也就是处于了ESTABLISHED状态的tcp连接,就需要进行管理起来,而OS就是通过全连接队列来进行管理的,它会将三次握手成功的tcp连接都放入这个队列,等待我们的accept将它们拿到应用层。

所以这我们特意让服务端不调用accept,也就是把那些已经建立好连接的tcp连接一直留在了全连接队列当中。

那为什么第三个连接是处于SYN_RECV状态呢?
先再来看看三次握手的这些状态。
在这里插入图片描述

SYN_RECV也就是处于收到了客户端第一次发来的SYN请求并给客户端发ACK+SYN的时间段。

那当客户端再发ACK应答时,我们这里的tcp连接为什么还没处于ESTABLISHED状态呢?

这是因为我们的全连接队列满了!
至于为什么会满,是因为我们的backlog被我们设置为了1。
全连接队列的长度等于logback+1!

半连接队列

对于已经建立好连接的tcp连接,我们需要进行管理,对于那些还正在进行三次握手的tcp连接,我们也需要进行管理,我们会将这些连接放置在半连接队列中!

而当我们的半连接队列中有连接如果在一定时间内还未收到应答,就会被自动释放。

所以当我们过段时间再调用netstat -ntp查看连接状态时,刚刚处于SYN_RECV的那个连接就消失了。

之前我们说了是因为我们的全连接队列满了所以我们第三个连接才无法进入全连接队列,并不是因为我们没有收到应答,这个应答其实是被tcp协议忽略掉了,因为全连接队列满了!


四次挥手状态

在这里插入图片描述
通过上面的三次握手,四次挥手其实也差不多,不过我们需要注意的是这里的TIME_WAIT状态。

第一次发送FIN请求的主机在收到对方也发来的FIN请求之后,不光也要返回一个ACK应答,同时将自身状态设置为TIME_WAIT状态,并在一段时间之后,将连接关闭。

首先想一想,为什么要等待一点时间再关闭?
因为我们不知道对方主机是否收到了我们的ACK应答,万一它没收到,它就需要补发重传。 如果我在发出应答就直接关闭连接,出现上述情况,我们就没办法收到它补发的FIN请求,就会造成另一个主机迟迟收不到应答,虽然最后的重传机制在重传一定次数后仍会关闭连接,但是这段时间也是耗费OS资源的。所以我们就需要有TIME_WAIT时间。

那么这段等待时间我们是尽量让客户端来维持的, 原因与三次握手一样,服务器的资源要尽可能的利用,尽量少一些浪费资源的行为。

TIME_WAIT等待多长时间呢?

等待时间长度也是有讲究的,我们一般设定时间为 2MSL时间。

什么是MSL?

MSL就是一段TCP报文数据在网络中最大生存时间。
所以,等待2MSL的时间就可以在出现问题的时尽可能的让服务端有时间进行补发重传。
还能让一些中间由于网络堵塞原因的一些报文数据消散,因为我们要考虑到这一种情况,我突然又想再次建立连接,那么这些上次连接的残余报文就有可能会影响我们新的连接,不过这种情况是几乎不可能发生的,因为我们的客户端在连接服务端时采用的是随机端口机制。 并且我们还有24位序号能保证这种情况的发生,也是因为这种原因,我们刚刚上面讲到的,随机序号也是为了避免残余报文影响正常连接。

模拟四次挥手环境

我们一个客户端一个服务端即可,然后一个窗口来观察tcp连接状态。

先启动服务端,然后客户端连接服务端。
此时我们ctrl+c关掉服务端,服务端的tcp就需要向客户端发送FIN断开连接请求,客户端并发送ACK回应,此时我们就可以看到服务端的tcp连接处于FIN_WAIT2状态。
在这里插入图片描述

然后接着我们也关闭掉客户端,就能看到服务端的连接处于 TIME_WAIT状态了。
在这里插入图片描述
等过一点时间再检查连接状态,我们就发现连接消失了。

现在我们再来回过头来想一想之前碰到的问题,我们以前在关闭服务端的时候,然后再重启启动服务端,是不是会碰到
在这里插入图片描述
现在不是就明白了呢?

这是因为我们虽然关闭掉了服务端的进程,但是tcp连接还是处于TIME_WAIT状态,也就是这个端口号正在被使用,自然也就不能重复绑定被使用的端口号。

而我们之前也使用过setsockopt函数能够解决这个情况,可以让刚刚被关闭的服务端,立马重启不用改变端口号,也就是针对这个问题的解决方案,因为对于服务端而言,我虽然出于各种原因挂掉了,但是我需要立马重启服务,否则即使短短几秒没办法重启服务也可能会造成我较大的经济损失。

setsockopt

在这里插入图片描述

int opt = 1;
setsocket(listensocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof opt)

流量控制

刚才我们学习32位窗口大小的时候,我们就已经介绍了,双方主机可以通过ACK回应中设置32位窗口大小,就可以让对方主机知道我们的接收缓冲区还剩多少空间,还能接收的最大数据长度。

如果接收方缓冲区满了, 就会将窗口置为0。这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收方把窗口大小告诉发送方。

这种流量控制的好处就在于,可以避免因为接收缓冲区空间不足,而导致接收方不断丢包,发送方不断重传的情况,有效地提高了传输的效率。

滑动窗口

要想理解滑动窗口,我们先来重新再理解一下接收缓冲区。

我们可以将接收缓冲区分为三个部分
在这里插入图片描述

第一个部分是确认应答的数据,已经确认被对方收到的数据我们其实就可以进行覆盖了,不过对于如何覆盖,我们等会再说。

第二个部分是我发送但未应答的数据,这个部分,我们就可以视他为滑动窗口,因为滑动窗口,是受对方缓冲区大小也就是收到的24位窗口大小影响的。

通过这样的一个窗口,我们不仅可以同时维护发送但未应答的数据,区分其他两个部分的数据,也不必再重新开辟一段空间来维护,直接在发送缓冲区放置即可。 这个窗口的区间是通过双指针来划分的。

滑动窗口的大小取决于对方的接收缓冲区的窗口大小,所以我们就可以将滑动窗口内的数据一次性连续发送出去,然后开始等待应答。
滑动窗口大小 = min(对方缓冲区窗口大小,有效数据,阻塞窗口) 这里的阻塞窗口我们等会再谈。

那么它的滑动体现在哪里呢?
这就又要涉及到我们之前说的 序号和确认序号了。
begin = 确认序号。
end = 确认序号 + 窗口大小
当我们将滑动窗口的报文数据连续发送过去之后,我们开始等待应答报文,再通过应答报文中的确认序号和窗口大小,再动态调整滑动窗口的位置和大小。
这就是滑动窗口。

延迟应答

如果接收数据的主机立刻返回ACK应答, 这时候返回的窗口可能比较小。
我们的服务器在一定时间内是会把接收缓冲区内的数据取走一部分的,所以延迟应答,可能就可以让我们返回的窗口大小更大一些。

那么这个延迟多久呢?
对于不同的操作系统有不同的方案,不过大多数都是这两种方案之一。

  • 数量限制: 每隔N个包就应答一次; N一般取2
  • 时间限制: 超过最大延迟时间就应答一次; 时间一般取200ms

阻塞控制

有的时候,我们的网络环境会变得拥堵,发出的数据迟迟阻塞在里面。
所以如果当出现大面积丢包的情况,tcp就需要考虑到有可能是因为网络拥堵阻塞的原因,于是就需要做出相关策略。 我们称这种策略就叫做阻塞控制。

当被tcp判断可能是网络阻塞时,就要开始引入慢启动机制和阻塞窗口(一个阻塞窗口为一个报文数据),所谓的慢启动机制就是第一次一个阻塞窗口的数据,如果能收到应答,下一次就发送2倍的阻塞窗口数据,一次类推,期间如果又发生网络阻塞大面积丢包则再重新轮回一次。

每次发送数据包的时候, 将拥塞窗口和接收端主机反馈的窗口大小做比较, 取较小的值作为实际发送的窗口;
所以我们就能理解上面的 滑动窗口大小 = min(对方缓冲区窗口大小,有效数据,阻塞窗口)。

但是,如果是以2倍的方式增大阻塞窗口,对于这种指数级的增长,后面的增长是十分夸张的,但是指数级增长的前期是相对比较缓慢的,我们对于阻塞的试探只需要慢启动即可,所以我们就需要有一个阈值来限制指数的增长。

当阻塞窗口达到阈值后,就不再以指数进行增长,而是+1。
在这里插入图片描述
这里的乘法减小是对于再次碰到网络阻塞做出的算法调整阈值,将阈值设为碰到拥堵时堵塞窗口/2。

用户数据报和字节流

对于UDP协议,它是面向用户数据报的,而对于TCP协议,它是面向字节流的。
那么 用户数据报和字节流 的最大区别在哪里呢?
如果是采用的是面向数据报的方案,应用层交给UDP多长的报文, UDP原样发送, 既不会拆分, 也不会合并。而当报文到达目标主机,对方主机在从接受缓冲区拿数据时,只能一次性把整个报文全部拿上来,不能分批拿,这就是面向数据报。

如果采用的是面向字节流的方案,应用层交给TCP的报文,TCP可以通过滑动窗口来自己控制数据的传发,保证可靠性的同时提高效率。 当报文到达目标主机时,对方主机在从接受缓冲区拿数据时,可以通过控制一次拿多少个字节,可以分批次拿,这就是面向字节流、

数据粘包问题

所以对于Tcp协议而言,就会出现这样一个问题,应用层从接收缓冲区一次性拿到的字节长度,里面可能存在不完整的报文,可能是1.5个报文,也有可能是2.3个报文,意思就是一次拿到的报文数据可能不完整,这就叫做数据粘包问题。

所以针对这一问题提出的解决方案就是,我们需要在应用层进行处理,至于如何处理,之前我们其实就已经写过处理方案了,那就是我们的序列化和反序列化。 我们会将有效数据以\n进行区分,并在第一个部分填上我们的主体长度,通过这样的方式,我们就可以分析出我们是否拿到的是一个不完整的报文。

TCP异常问题

首先提出三种情况

  1. 进程终止
  2. 主机关机/重启
  3. 主机断电/主机断网

第一种情况:进程由于异常终止退出了,其实在OS层面上,与进程正常退出并无太大的区别,OS都是要对退出/终止的进程进行资源回收处理,而TCP是系统层面的,所以进程是因为异常退出还是正常退出这都并不会影响内核层面的TCP协议,当进程退出时,我们的TCP协议也是要保证每个连接正常断开的。

第二种情况: 主机在关机或者重启时,首先要做的就是要关闭所有进程,然后再关机,在这个过程中,也是会正常断开所有连接。

第三种情况: 主机断电/主机断网, 这种突然断电或者断网的情况,TCP是无法预料到的,不管是断网还是断电,TCP都是没有办法再进行四次挥手的,自然TCP的连接就无法保证正常断开。 如果是断电,那就不用说了,整个主机都不在运行了。 如果是断网,那么主机就会强制性断开自己的所有TCP连接;但是连接的另一台主机是没有办法收到四次挥手的,也就是还要维持连接一定时间,不过等到一定时间通过TCP设定的策略如果长时间一个连接没有发出任何应答,也是会断开连接的。

相关文章:

Linux网络 - 再谈、详谈UDP和TCP协议

文章目录 前言预备netstatpidofcat /etc/services 一、UDP协议UDP协议端格式UDP的缓冲区基于UDP的应用层协议 二、TCP协议1.TCP协议段格式确认应答(ACK)机制三次握手疑问1 最后一次客户端发给服务端的ACK请求怎么保证服务端能够收到? 四次挥手疑问2 为什么挥手是四次…...

el-form重置后input无法输入问题

新增用户遇到的问题: 如果你没有为 formData 设置默认值,而只是将其初始化为空对象 {},则在打开dialog时,正常输入, formdata会变成如下 但是,打开后,直接使用 resetFields 或直接清空表单&…...

Java网络编程(JavaWeb的基础)

Java网络编程(JavaWeb的基础) 文章目录 Java网络编程(JavaWeb的基础)前言一、网络编程概述1.1 软件架构&网络基础1.2 网络通信要素:IP/端口/通信协议1.3 传输层协议:tcp/udp 二、网络编程API2.1 InetAddress类2.2 Socket类&am…...

鸿蒙Harmony开发实战案例:使用OpenGL绘制3D图形

XComponent控件常用于相机预览流的显示和游戏画面的绘制,在OpenHarmony上,可以配合Native Window创建OpenGL开发环境,并最终将OpenGL绘制的图形显示到XComponent控件。本文将采用"Native C"模板,调用OpenGL ES图形库绘制3D图形&…...

DM达梦数据库存储过程

💝💝💝首先,欢迎各位来到我的博客,很高兴能够在这里和您见面!希望您在这里不仅可以有所收获,同时也能感受到一份轻松欢乐的氛围,祝你生活愉快! 💝&#x1f49…...

【python】OpenCV—Color Correction

文章目录 cv2.aruco 介绍imutils.perspective.four_point_transform 介绍skimage.exposure.match_histograms 介绍牛刀小试遇到的问题 参考学习来自 OpenCV基础(18)使用 OpenCV 和 Python 进行自动色彩校正 cv2.aruco 介绍 一、cv2.aruco模块概述 cv2.…...

Java基础知识整理笔记

目录 1.关于Java概念 1.1 谈谈对Java的理解? 1.2 Java的基础数据类型? 1.3 关于面向对象的设计理解 1.3.1 面向对象的特性有哪些? 1.3.2 重写和重载的区别? 1.3.3 面向对象的设计原则是什么? 1.4 关于变量与方…...

知识图谱——Neo4j数据库实战

数据与代码链接见文末 1.Neo4j数据库安装 JDK 安装:https://www.oracle.com/java/technologies/javase-downloads.html Neo4j 安装:https://neo4j.com/download-center/ 配置好 JDK 和 Neo4j 的环境变量...

第十一次Javaweb作业

4.登录校验 4.1会话 --用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。 会话跟踪:一种维护浏览器状态的方法,服务器需要识别多次请求…...

人工智能AI风口已开:如何赋予UI设计与视频剪辑新生命

随着科技的浪潮不断向前推进,人工智能(AI)正以惊人的速度重塑着我们的世界,特别是在创意产业的核心领域——UI设计与视频剪辑中,AI正逐步成为驱动行业创新与变革的关键力量。在这个AI技术全面开花的新时代,…...

计算机专业课面试常见问题-编程语言篇

目录 1. 程序的编译执行流程? 2. C浅拷贝和深拷贝的区别? 3. C虚函数? …...

CSS|05 继承性与优先级

继承性 一、继承性的特点: 1.外层元素身上的样式会被内层元素所继承 2.如果内层元素与外层元素身上的演示相同时,外层元素的样式会被内层元素所覆盖 二、关于继承性的问题 是不是所有样式都能被继承? 答:并不是所有样式能被继承…...

KVM性能优化之内存优化(宿主机)

linux系统自带了一技术叫透明巨型页(transparent huge page),它允许所有的空余内存被用作缓存以提高性能,而且这个设置是默认开启的,我们不需要手动去操作。 Centos下,我们用cat /sys/kernel/mm/transpare…...

【Linux杂货铺】Linux学习之路:期末总结篇1

第一章 什么是Linux? Linux 是 UNIX 操作系统的一个克隆;它由林纳斯 本纳第克特 托瓦兹从零开始编写,并在网络上众多松散的黑客团队的帮助下得以发展和完善;它遵从可移植操作系统接口(POSIX)标准和单一 UNIX 规范…...

GPT-5的到来:智能飞跃与未来畅想

IT之家6月22日消息,在美国达特茅斯工程学院的采访中,OpenAI首席技术官米拉穆拉蒂确认了GPT-5的发布计划,预计将在一年半后推出。穆拉蒂形象地将GPT-4到GPT-5的飞跃比作高中生到博士生的成长。这一飞跃将给我们带来哪些变化?GPT-5的…...

gin中间件

在web应用服务中,完整的业务处理在技术上包含客户端操作,服务端处理,返回处理结果给客户端三个步骤。但是在在更负责的业务和需求场景。一个完整的系统可能要包含鉴权认证,权限管理,安全检查,日志记录等多维…...

swagger常用注解

最近查看接口文档的时候发现,POST方法中的query没法在swagger中显示,查了才发现这是因为Swagger或OpenAPI规范默认将HTTP POST请求的参数识别为请求体(body)参数,而不是查询字符串(query)参数。…...

【Flink metric(1)】Flink指标系统的系统性知识:获取metric以及注册自己的metric

文章目录 一. Registering metrics:向flink注册新自己的metrics1. 注册metrics2. Metric types:指标类型2.1. Counter2.2. Gauge2.3. Histogram(ing)2.4. Meter 二. Scope:指标作用域1. User Scope2. System Scope ing3. User Variables 三. Reporter ing四. System…...

命令模式(Command Pattern)

命令模式(Command Pattern) 定义 命令模式是对命令的封装,每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行操作。 命令模式解耦了请求方和接收方,请求…...

掌握Symfony的模板继承:构建强大且灵活的Web界面

掌握Symfony的模板继承:构建强大且灵活的Web界面 在Symfony框架中,模板继承是一个强大的功能,它允许开发者创建可重用的布局模板,并通过扩展这些模板来构建具体的页面。这种机制不仅提高了代码的可维护性,还使得页面结…...

uboot基本使用网络命令和从服务器端下载linux内核启动

网络命令ip地址设置: setenv gmac_debug 0; setenv mdio_intf rgmii; setenv bootdelay 1; setenv ethaddr 00:xxxx:81:70; // mac地址 setenv ipaddr xxx; //开发板 IP 地址 setenv netmask 255.255.255.0; setenv gatewayip xxx.1; setenv serverip xxxx; //服…...

解决ArcGIS导出的svg格式的图片插入Word后的字体问题

背景 在ArcGIS中设置字体为Times New Roman,但导入Word后字体转为等线。 ArcGIS中的Layout 导入Word​​​​​​ 原因分析 Word无法识别嵌入进SVG格式文件中的字体。 解决方案 在Export Layer窗口中,将Embed fonts取消勾选,Convert cha…...

如何确保 Puppet 配置在复杂网络环境中的可靠分发和同步?

在复杂网络环境中确保 Puppet 配置的可靠分发和同步可以采取以下措施: 网络拓扑规划:在复杂网络环境中,首先需要进行网络拓扑规划,确保网络结构合理,并能够支持可靠的分发和同步机制。 Puppet Master 多节点部署&…...

2024最新!将mysql的数据导入到Solr

Solr导入mysql的数据 如何安装导入数据前准备配置Solr的Jar包以及Mysql驱动包1.1、将solr-8.11.3\dist下的两个包进行移动1.2、将mysql-connect包也移动到该位置1.3、重启Solr项目 配置xml2.1、第一步我们需要创建核心2.2、第二步修改xml(这里是结合19年的教程)2.3、 创建data-…...

Python数据分析第二课:conda的基础命令

Python数据分析第二课:conda的基础命令 1.conda是什么? conda是一个开源的包管理系统,可以帮助我们进行管理多个不同版本的软件包,还可以帮助我们建立虚拟环境,以便对不同的项目进行隔离。 简单来说,conda是一个软…...

LayoutInflater加载流程

简介 LayoutInflater在日常的Android开发中是经常使用的类,常常用于XML中View的加载相关流程。本文主要总结一些其常见api的源码流程。 获取LayoutInflater 我们一般会在Activity的onCreate方法中会通过setContentView方法设置自己的布局layoutId,Act…...

PLC数据采集案例

--------天津三石峰科技案例分享 项目介绍 项目背景 本项目为天津某钢铁集团下数字化改造项目,主要解决天津大型钢厂加氢站数字化改造过程中遇到的数据采集需求。项目难点PLC已经在运行了,需要采集里面数据,不修改程序,不影响P…...

基于单片机和LabVIEW 的远程矿井水位监控系统设计

摘要 : 针 对 现 有 矿 井 水 位 监 控 系 统 存 在 结 构 复 杂 和 不 能 远 程 监 控 的 问 题 , 设计了基于单片机和LabVIEW 的远程矿井水位监控系统 , 详…...

element 表格嵌套表单验证指定行

elementui表格嵌套动态表单&#xff0c;单独验证某一行输入项是否符合校验规则&#xff1b; input动态绑定校验 :prop"imgTable. scope.$index .bxName" <el-form :model"formTable" ref"formTable" inline size"small"><…...

CORE Mobility Errorr的调试

在运行CORE tutorial 3中的mobility示例时&#xff0c;出现如下错误&#xff1a; 当看到这个问题的时候&#xff0c;并没有仔细去分析日志和现象&#xff0c;在core-daemon的进程打印界面只看了一下最后的出错堆栈&#xff1a; 2024-06-27 10:43:48,614 - ERROR - _server:_ca…...

基于weixin小程序乡村旅游系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;商家管理&#xff0c;旅游景点管理&#xff0c;景点类型管理&#xff0c;景点路线管理&#xff0c;系统管理 商家帐号账号功能包括&#xff1a;系统首页&#xff0c;旅游景点管理&…...

详解三种常用标准化 Batch Norm Layer Norm RMSNorm

参考&#xff1a; BN究竟起了什么作用&#xff1f;一个闭门造车的分析《动手学深度学习》7.5 节 深度学习中&#xff0c;归一化是常用的稳定训练的手段&#xff0c;CV 中常用 Batch Norm&#xff1b; Transformer 类模型中常用 layer norm&#xff0c;而 RMSNorm 是近期很流行…...

云计算运维工程师面试

1. 云计算运维工程师的角色和职责是什么? 回答: 云计算运维工程师负责确保云计算环境(包括硬件和软件系统)的高可用性和稳定性。他们的主要职责包括: 监测系统和应用程序的性能,确保它们正常运行。故障排除,快速响应并解决系统或应用程序中出现的问题。容量规划,根据…...

聚观早报 | iPhone 16核心硬件曝光;三星Galaxy全球新品发布会

聚观早报每日整理最值得关注的行业重点事件&#xff0c;帮助大家及时了解最新行业动态&#xff0c;每日读报&#xff0c;就读聚观365资讯简报。 整理丨Cutie 6月28日消息 iPhone 16核心硬件曝光 三星Galaxy全球新品发布会 苹果正多方下注布局AI商店 黄仁勋2024年薪酬3400…...

web前端之文档流、浮动、定位详解

目录 一、文档流 二、浮动 1.添加浮动 2.清除浮动 三、定位 1.相对定位 2.绝对定位 一、文档流 什么是文档流&#xff1f; ● 文档流指的是文档中的标签在排列时所占用的位置。 将窗体自上而下分成一行行 &#xff0c;并在每 行中按从左至右的顺序排放标签&#xff0c…...

[JS]节点操作

DOM节点 DOM树中的所有内容都是节点, 我们重点关注元素节点 作用 使开发者可以根据节点的关系获取元素, 而不是只能依赖选择器, 提高了编码的灵活性 节点分类 元素节点: 所有的标签都是元素节点, html是根节点属性节点: 所有的属性都是属性节点, 比如href文本节点: 所有的文…...

基于SpringBoot+Vue的论坛网站系统(带1w+文档)

基于SpringBootVue的论坛网站系统(带1w文档) 对于之前论坛网站的管理&#xff0c;大部分都是使用传统的人工方式去管理&#xff0c;这样导致了管理效率低下、出错频率高。而且&#xff0c;时间一长的话&#xff0c;积累下来的数据信息不容易保存&#xff0c;对于查询、更新还有…...

03逻辑门电路

分立门电路&#xff1a; 集成门电路&#xff1a; TTL门电路 MOS门电路&#xff1a;NMOS门电路、PMOS门电路、CMOS门电路 BICMOS门电路&#xff1a;CMOS的高输入阻抗和TTL的高放大倍数的结合 向更低功耗、更高速度发展 MOS管的Rdson在可变电阻区的阻值也一般会小于1000欧姆 …...

2毛钱的SOT23-5封装28V、1.5A、1.2MHz DCDC转换器用于LCD偏置电源和白光LED驱动等MT3540升压芯片

前言 之前发了一个TI的BOOST升压芯片&#xff0c;用于LCD偏置电压或LED驱动&#xff0c;请访问以下链接。 6毛钱SOT-23封装28V、400mA 开关升压转换器&#xff0c;LCD偏置电源和白光LED应用芯片TPS61040 国产半导体厂家发展迅猛&#xff0c;今天推荐一个公司带“航天”的升压…...

Zookeeper部署

Zookeeper部署 下载安装包Linux解压安装包修改配置文件编辑zoo.cf配置 启动服务停止服务常用zookeeper指令查看namespace列表创建namespace删除namespace 注意&#xff1a;该文章为简单部署操作&#xff0c;没有复杂的配置内容&#xff0c;用的是3.7.2版本。 下载安装包 进入z…...

2.x86游戏实战-跨进程读取血量

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 接下来会写C/C代码&#xff0c;C/C代码不是很难&#xff0c;然后为了快速掌握逆向这个技能&#xff0c;我…...

element-plus 日期选择添加确定按钮

需求&#xff1a;选择日期后&#xff0c;点击确定按钮关闭面板 思路&#xff1a; 使用shortcuts自定义确定和取消按钮选择日期后使用handleOpen()强制开启面板点击确定后使用handleClose()关闭面板 <template><el-date-pickerref"pickerRef"v-model"…...

Redis优化之持久化

目录 1.Redis高可用 2.Redis持久化 2.1 RDB持久化 2.1.1 触发条件 2.1.2 执行流程 2.1.3 启动时加载 2.2 AOF持久化 2.2.1 开启AOF 2.2.2 执行流程 2.2.3 文件重写触发方式 2.2.4 文件重写的流程 2.2.5 启动时加载 2.3 RDB和AOF的优缺点 3.Redis性能管理 3.1 查看…...

ubuntu22.04 编译安装libcurl C++ library

1. 安装必须的依赖项 sudo apt update #sudo apt install build-essential autoconf libtool pkg-config libssl-dev libz-dev 2. 下载及编译前准备 cd /opt mkdir curl && cd curl mkdir build && mkdir install git clone https://github.com/curl/curl.git…...

js函数闭包解析

闭包是JavaScript中非常重要的概念&#xff0c;理解闭包对于编写高质量的代码是至关重要的。本文将详细解析闭包的概念&#xff0c;并提供一些代码示例来帮助读者更好地理解闭包的使用。 什么是闭包&#xff1f; 闭包是指在一个函数内部定义的函数&#xff0c;该函数可以访问包…...

查看Oracle、MySQL、PostGreSQL中的依赖关系

查看Oracle、MySQL、PostGreSQL中的依赖关系 在有些程序员开发习惯中&#xff0c;喜欢为了应用代码的简洁或复用&#xff0c;而在数据库创建一个复杂关连查询的VIEW&#xff0c;甚至是VIEW套VIEW嵌套使用&#xff0c; 这里就有个问题如果上线后如发现依赖的表字段类型或长度不…...

多线程(基础)

前言&#x1f440;~ 上一章我们介绍了什么是进程&#xff0c;对于进程就了解那么多即可&#xff0c;我们作为java程序员更关注线程&#xff0c;线程内容比较多&#xff0c;所以我们要分好几部分才能讲完 目录 进程的缺点 多线程&#xff08;重要&#xff09; 进程和线程的区…...

BUG cn.bing.com 重定向的次数过多,无法搜索内容

BUG cn.bing.com 重定向的次数过多&#xff0c;无法搜索内容 环境 windows 11 edge浏览器详情 使用Microsoft Edge 必应搜索显示"cn.bing.com"重定向次数过多&#xff0c;无法进行正常的检索功能 解决办法 检查是否开启某些科_学_上_网&#xff08;翻_墙&#xf…...

【数据科学】学习资源汇总(不定时更新)

好书推荐&#xff1a;BooksPDF/数据科学/Python数据科学手册.pdf at master zhixingchou/BooksPDF GitHub...

完美解决ValueError: column index (256) not an int in range(256)的正确解决方法,亲测有效!!!

完美解决ValueError: column index (256) not an int in range(256)的正确解决方法&#xff0c;亲测有效&#xff01;&#xff01;&#xff01; 亲测有效 完美解决ValueError: column index (256) not an int in range(256)的正确解决方法&#xff0c;亲测有效&#xff01;&…...