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

Linux网络——传输层

目录

1. 再谈端口号

2. UDP

① UDP 协议的报文

② UDP 协议报头的本质 

3. TCP

① TCP 缓冲区的理解

② TCP 协议的报文

1. 16位源端口号 && 16位目的端口号 && 4位首部长度

2. 16位窗口大小

3. 32位序号 && 32位确认序号

4. 六个标记位

③ TCP 的面向连接特性

④ TCP 的可靠传输特性

1. TCP 确认应答机制

2. TCP 超时重传机制

3. TCP 流量控制

4. TCP 滑动窗口

5. TCP 延迟应答

6. TCP 捎带应答

7. TCP 拥塞控制

⑤ TCP 可靠与效率小结

⑥ TCP 的面向字节流特性

粘包问题 

⑦ TCP 的异常情况

4. 文件与 socket 的关系


我们之前已经谈到过传输层负责的是保证数据能够完整的从发送端发送到接收端

1. 再谈端口号

我们将 源IP、源端口号、目的IP、目的端口号、协议号 五元组标识为一个通信。我们来看看端口号的划分

0~1023:知名端口号,包含 HTTP、FTP、SSH 等广为人知的协议

1024~65535:OS 动态分配的端口号

我们来看看一些常见的知名端口号

SSH -> 22

FTP -> 21

telnet -> 23

HTTP -> 80

HTTPS -> 443

我们在使用端口号时,应该尽量避免使用这些较为知名的端口号,此外我们之前提到过,一个进程可以绑定多个端口号,但是一个端口号是不能被多个进程绑定的! 

我们可以使用 netstat 来查看通信情况,如图

2. UDP

之前我们曾说过,在学习一个新层级时需要学习下面几个东西

1. 报头与有效载荷如何分离

2. 有效载荷应该交付给上层的哪个协议

3. 认识报头

4. 学习该协议的相关问题

① UDP 协议的报文

UDP 协议的报文如图所示

源端口号与目的端口号不必多说,我们将 UDP 长度减去一个8,就能得到正文长度,稍微计算一下正文的最大长度

2 ^ 16 Byte = 64 KB

那假如需要传输的数据大小超过了64KB,我们还能使用 UDP 来传输吗?——可以!不过我们要将自己的报文拆解成 64KB 然后再发送; 而我们用 UDP 检验和来检验整个报文是否完全送达,如果发现数据出错,就会将当前报文直接丢弃。

可以看到,整个 UDP 运作的模式基本上有三个特点,即

无连接

不可靠

面向数据报

注:面向数据报意为:应用层交给了 UDP 多长报文,UDP 照原样发送,既不拆分也不合并。举个例子,发送端调用一次 sendto 函数发送100字节的数据,接收端也只能一样操作,不能循环调用10次 recvfrom 函数,每次接收10字节。也是因此原因,我们可以说 UDP 没有真正意义上的发送缓冲区,如图

虽然 UDP 也是全双工的工作模式,但是如果有一方的缓冲区满了之后,再接收到 UDP 报文就会将其直接舍弃。

② UDP 协议报头的本质 

我们需要知道,Linux 系统本身是使用 C 语言写的,也就是说整个报头在实际上是如下这种形式

// 报头
struct udp_header
{// 使用位段来划分各个部分uint32_t src_port:16;   // 源端口号uint32_t dst_port:16;   // 目的端口号uint32_t length:16;     // UDP 长度uint32_t check_code:16; // UDP 检验和
}

而 OS 会为其提供一个缓冲区,也就是说实际使用如图所示

这样就通过先描述后组织完成了 UDP 的使用。

我们最后再介绍一些基于 UDP 协议的应用层协议

NFS: 网络文件系统协议,实现跨网络访问文件。

TFTP: 简单文件传输协议,用于设备固件升级。

DHCP: 动态主机配置协议,自动分配网络设置。

BOOTP: 引导程序协议,用于自动网络配置。

DNS: 域名系统,将域名解析为IP地址。

3. TCP

① TCP 缓冲区的理解

TCP 的全名为 Transmission Control Protocol,即传输控制协议,如图

传输控制协议在整个过程中决定数据在什么时候发送,发送多少,发送出错了怎么办?也就是说,这些问题全部由 TCP 协议自主决定!而上层在调用诸如 write, read, send, recv 等函数时,其本质并不是将数据发送到网络中,而是将数据拷贝到 Linux 内核中,由 TCP 决定数据何时发送等。而发送缓冲区本身也存在于 Linux 中,也就是由多个 4KB 的struct_page 组成的。所以,我们在学习网络时,将网卡更换为磁盘,整体与文件管理相似。

② TCP 协议的报文

TCP 协议的报文如图所示

接下来我们来看看各个字段的意义

1. 16位源端口号 && 16位目的端口号 && 4位首部长度

之前我们说过,要想学习一个协议,首先需要了解如何将有效载荷与报头分离?分离出来后如何将有效载荷交付给上层?

交付给上层很简单,我们在学习 UDP 协议的时候已经学过,只要有源端口号与目的端口号就可以有效交付。

接下来就是有效载荷如何与报头分离的问题,我们可以除有效载荷外,我们使用的是固定长度报头 + 自定义描述,而报头长度为20位,而选项长度为40位

4位首部长度就可以很好地解决这个问题,4个数位可以表示 [0, 15] 范围的数字,我们在计算的时候,会以4字节为单位,也就是说4个数位可以表示 [0, 60] 字节的范围

举几个例子

如果首部长度的数字为 0101 -> 5 -> 5*4字节 -> 20,意思是这个报文不带选项;

如果首部长度的数字为 1101 -> 13 -> 13*4字节 -> 52,意思是这个报文带32字节的选项。

而将整个报文减去这个长度就能得到有效载荷! 

2. 16位窗口大小

传输过程如图所示

整个通信过程是基于 TCP 协议的,也就是说,互发消息时,任何一方发送的都是一个完整的 TCP 报文,即一定携带完整的 TCP 报头。那我们为什么说相对于 UDP 来说,TCP 是可靠的呢?

TCP 为了保证可靠性,最基本的一个特点就是确认应答机制!具体一点来说,就是不管是客户端还是服务端,在发送了任何消息后,都需要收到回应,如图所示

 

回到这个16位窗口大小,我们需要先了解一下流量控制机制

流量控制:接收方的接收缓冲区快满或已满时,会让发送方减缓发送频率或者不发,从而减少丢包率

那我们要让发送方发慢一点,依据是什么呢?——16位窗口大小中填写的是 自己的接收缓冲区 剩余的空间大小,我们先前提到过每次发送信息与确认应答都带有一个完整的 TCP 报文,而当发送方收到响应后,会根据接收方的接收缓冲区剩余空间大小来控制发送速度!所以,16位窗口大小填写的是接收方的接收缓冲区的剩余大小。

3. 32位序号 && 32位确认序号

我们来想想,世界上存在完全可靠的网络协议吗?肯定不是,那么我们可以退而求其次,即保证局部上的可靠即可,举个例子

在操场的百米跑道上站着两个人(A 与 B),A 向 B 询问“你吃了吗?”,B 向 A 回答“收到!”,B 向 A 再回答“吃了!”,A 向 B 回答“收到!”

在这个例子中

1. 只要收到了应答,就能确认自己最近发送的消息被对方收到了

2. 我们也能发现,如果没有应答的数据,我们就无法保证可靠性,因此最新的一条消息是没有应答的,所以无法保证发送出去的消息是 100% 可靠的

那为了保证可靠性,如果在一段时间内没有收到应答的话,发送方就会认为数据丢失,然后进行重传。而在操场传话的例子中,回答收到后再回答吃了这一行为效率有点低,因此我们可以将他们合在一起,也就是 捎带应答。在实际的 TCP 运行中,发起请求操作一般来说都是并行的,如图

而由于我们一次性发了很多个报文,经转多个设备,最终接收方接收到的顺序可能会不同(如4132),也就是说出现了乱序问题,这会导致 TCP 变得不可靠,因此我们需要使用32位序号来保证数据的按序到达(如1234等序号)!

那么32位序号是什么呢?如图所示

发送的数据块的最后一个字符的下标就是32位序号中要填入的内容!那32位确认序号又是什么?——填充的是序号报文的+1数字,为什么呢?

确认序号的意义是确认之前的数据,我现在已经收到了,下一次发送数据请从确认序号处开始发送

举个例子

如果在收到确认应答时,收到了3001与4001而没有收到1001与2001,我们认为前3000个数据都收到了,也就是说 TCP 允许应答有少量的缺失。这里还有一个疑问,我们只需要将序号+1填回序号中返回即可,那为什么还要有确认序号呢?——由于捎带应答的存在,可能接收方也有自己要发送的数据,所以需要使用序号来发送数据!

4. 六个标记位

TCP 在进行通信时,会有很多行为,比如:建立连接,进行通信,断开连接等,也就是说 TCP 收到的报文本身是有各种类型的。而不同的类型决定了接收方要做不同的动作!那接收方如何得知报文的类型呢?——查看六个标记位,也就是说六个标记位的存在意义就是区分 TCP 的报文类型。

六个标记位分别是:URG(Urgent Pointer), ACK(Acknowledgment), PSH(Push Function), RST(Reset), SYN(Synchronize), FIN(Finish)

具体来说

ACK:确认,表示确认序号字段是有效的,用于确认已成功接收的数据,即表示这是一个确认应答报文。

SYN:同步,表示请求建立连接,即表示这是一个同步报文。

FIN:结束,表示发送方已经发送完毕,请求关闭连接。

我们如何理解“连接”这一行为呢?——服务器对客户端进行先描述后组织!

PSH:推送,提示接收方立刻从 TCP 缓冲区中将数据读走

如果上层(如 CP 模型)一直不取数据的话,就会导致接收缓冲区变满,这样就会让发送端发送不了数据(在 OS 系统层面的体现是同步与互斥,OS 网络层面的体现则是流量控制),也就是说接收缓冲区越大,另一端发送的速度就越快。

RST:重置,表示要求重新建立连接,即表示这是一个复位报文。

三次握手如图所示

什么时候 C 与 S 认为自己已经完成连接了呢?——只要认为自己发出去了 ACK 就认为成功!TCP 虽然保证可靠性,但是 TCP 是允许建立连接失败的,也就是说 TCP 在进行三次握手时可能会出现问题,那就是万一发出去的 ACK 丢失,CS 对于建立连接的认知就会产生不一致(C 认为建立连接成功,S 认为没有建立连接)!

为了解决这个问题,如果此时 C 向 S 发送数据,S 给 C 的应答报文中带上 RST,C 就会意识到建立未成功,然后重新发送连接请求。 

URG:紧急指针,表示16位紧急指针有效,即数据包中有紧急数据,需要优先处理

举个例子

假设现在有一个 TCP 数据段,序列号是 1000,并且该段的 URG 标志被设置为 1,紧急指针为 50。序列号 1000 表示这个数据段的起始位置。紧急指针 50 表示从序列号 1000 开始,紧急数据结束于序列号 1050。因为 URG 标志被设置为 1,接收方就知道数据段中的序列号范围 [1000, 1050) 这部分数据是紧急数据,应该优先处理。

我们再举一个实际中的例子,假如现在有一个服务器,当客户端多次向服务器发起请求后,服务器没有任何反应,此时就需要以紧急指针的方式询问服务器的状态! 

③ TCP 的面向连接特性

如图,TCP 是一个基于连接的协议,整个通信的建立和断开又叫做 三次握手四次挥手

那么为什么需要三次握手呢?

1. 可靠地验证全双工工作模式

2. 只要是奇数次的握手都可以建立连接!

那我们为什么要采用三次握手呢?如果是一次握手的话,很容易将服务器的连接队列占满,也就是会导致 SYN 洪水;如果是两次握手也会导致 SYN 洪水的情况。这两种方式都优先让 Server 端做出建立连接的动作,而使用三次握手可以将这个动作嫁接到 Client 端,即三次握手能确保在一般情况下,握手失败后的连接成本是嫁接在 Client 端的,同时在这个握手过程中 C 与 S 都能可靠地保证一次报文的收与发,这也能验证全双工通路是否通畅!

那为什么要进行四次挥手呢?

如果想断开连接,意思就是没有数据给对方发送了,但是发送数据这个行为是双方都有可能会做的,因此需要断开两次!也就是所谓的四次挥手。

接下来让我们看看整个连接过程中的细节

1. 连接的建立与 accept 函数无关,也就是说三次握手是双方的 OS 自动完成的

2. listen(listenfd, backlog) 函数中,第二个参数是什么?

如图

backlog 表示这个全连接队列的最大长度,accept 函数实际就是向这个队列中添加一个新元素,这个参数为什么不能太长呢?

如果上层一直不取走,而下层却一直添加,就会导致资源被占据而没有创造价值,因此这个长度一般来说是视情况而定的!

那可不可以没有这个参数呢?——不行!

举个简单的例子,一般海底捞的店门口都会放上一些椅子,当店内没有满载时就可以直接进入,这样的设计可以让服务器充分利用资源。 

而除了全连接队列以外,还存在一个队列

在服务端第一次接收到连接请求后会进入 SYN_RCVD 状态,也就是说被连接的一方处于 SYN_RCVD 状态,也称为半连接,而对于半连接队列来说,半连接节点并不会长时间进行维护。 

3. 在四次挥手中,主动断开连接的一方,在四次挥手完成后,要进入 TIME_WAIT 状态,然后等待若干时间后,自动释放 

也就是说,如果是服务器发起的断开连接,在一段时间内是无法使用同一端口号重启的!我们可以使用 setsockopt 来关闭这一行为。

那为什么要进入 TIME_WAIT 状态呢?它会持续多长时间呢?

TIME_WAIT 状态可以让双方的历史数据得以消散。从而防止对后续的通信造成伤害;还可以让我们断开连接(四次挥手)具有较好的容错性。举个例子

在 TCP 的四次挥手关闭过程中,如果客户端发送的最后一个ACK在网络中丢失,那么服务器端会因为超时未收到确认而重传FIN包。如果客户端不进入TIME_WAIT状态,而是直接关闭连接,那么当服务器端重传FIN包时,客户端将无法识别并响应这个FIN包,导致服务器端无法正常关闭连接。TIME_WAIT状态确保了客户端在足够长的时间内能够接收到服务器端可能重传的FIN包,并发送相应的ACK确认,从而保证连接能够可靠地关闭 

TIME_WAIT 的持续时间一般是 2个MSL(一个报文在网络中存在的最长时间),而一个  MSL 在 RFC1122 定制的标准中规定为 2 分钟,也就是说 TIME_WAIT 实际的持续时间大概在 60s ~ 120s 左右。

④ TCP 的可靠传输特性

1. TCP 确认应答机制

如图所示

确认应答机制就是在每次收到报文后,都会向发送方回应一个应答报文。

2. TCP 超时重传机制

如图所示

对于这个特定时间间隔来说

最理想的情况下,我们应该找到一个最小的时间,以保证"确认应答一定能在这个时间内返回"。但是这个时间的长短,随着网络环境的不同,会产生差异,也就是说:如果我们设置的超时时间太长,会影响整体的重传效率;但是如果设置的超时时间太短,有可能会频繁发送重复的包!

那我们如何解决这个问题呢?

TCP 为了保证无论在任何环境下都能比较高性能的通信,会动态计算这个最大超时时间。

在 Linux 中(BSD Unix 和 Windows 也是如此),超时以 500ms 为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。如果重发一次之后,仍然得不到应答,等待 2*500ms 后再进行重传;如果仍然得不到应答,等待 4*500ms 进行重传。以此类推,超时时间以指数形式递增。累计到一定的重传次数,TCP 会认为网络或者对端主机出现异常,从而强制关闭连接。

现在让我们想想另一个问题:对于 S 端来说,若其被 C 端进行超时重传发送了重复报文怎么办?

报文是有序号的!S 端可以根据序号来对报文进行去重。 

3. TCP 流量控制

我们在前面介绍 16位窗口大小时已经提到了流量控制,接下来我们更加深入的了解一下

流量控制如图所示

对接收端来说,一旦窗口更新通知在传送途中丢失,会导致双方无法继续通信,因此发送端主机时不时就会发送窗口探测包。接下来我们还有几个问题 

1. 我们在第一次发送数据的时候,怎么保证发送的数据量是合理的呢?

三次握手做到的事不仅仅是握手,在握手这个过程中 CS 双方已经交换了报文,也就是已经协商好了双方的接收能力!

2. 由于已经协商好了接收能力,所以第三次握手的时候可以携带一部分数据!也就是我们所说的捎带应答

TCP 窗口大小最大为 65535字节吗?——并不是,TCP 选项中有一个 M 选项(窗口扩大因子),TCP 窗口的实际大小为 窗口大小左移 M 位

3. 流量控制是属于 TCP 的可靠特性,还是属于效率特性呢?——可靠为主,效率为辅!

4. TCP 滑动窗口

实际的交流过程图如下

在这个数据传输过程中,已经发送出去的,但是暂时没有收到应答的报文,会被 TCP 暂时保存起来,这样的报文发送方可能会存在很多个,那这些报文会被保存到哪里呢?发送缓冲区如图所示

其中,对于已发送已确认的区域,我们会将其设置为可被覆盖的状态,这样就叫做从 TCP 发送缓冲区中移除它了!而我们将已经发/可以发送数据但是尚未收到应答的区域称为 滑动窗口!正是因为有滑动窗口区域,才能一次向对方发送最大的 TCP 报文。

它是否与我们学过的算法中的滑动窗口(算法——滑动窗口)有关系呢?——有的,窗口滑动本质上指的就是指针右移!那么我们还有几个疑问

1. 滑动窗口在哪里?——发送缓冲区的一部分

2. 滑动窗口的范围大小是多少?——目前我们认为是对方的接收窗口的大小

3. 如何理解发送缓冲区的区域划分?——通过指针/下标区分即可

滑动窗口如图所示

我们对于确认序号的定义如下

如果确认序号是 X,则 X 之前的报文我们都收到了,这个过程允许少量的 ACK 丢失 

确认序号保证了滑动窗口线性地向后更新,不会出现跳跃的情况。 接下来我们加深一下对滑动窗口的理解

1. 如果在传输过程中出现了丢包问题,我们如何理解滑动窗口? 

假如 1~1001, ... , 4001~5000 的 ACK 丢失了,但是 5001~6000 的 ACK 没丢失怎么办?——不怕有什么影响,只要收到了 5001~6000 的应答报文,就直接将滑动窗口的左端指针移动到 6001即可。

那假如 1~1001, ... , 4001~5000 的 ACK 没丢失,但是 5001~6000 的 ACK 丢失了怎么办?——将滑动窗口的左端指针移动到 4001,然后对 4001 其后的数据进行补发即可。

那假如在数据的传输过程中,接收方没有收到 1001~2000 的数据,而收到了1~1000 与 2001~6000 的数据怎么办?——如图所示

我们将连续收到3个同样的 ACK 后进行补发这一行为,称为快重传。那已经有了快重传这一机制,为什么我们还要有超时重传呢?——快重传是有条件的,它是为了提高效率而存在的,而超时重传是兜底的。

2. 如何理解滑动窗口中的指针向右移动?移动的时候,大小会变化吗?怎么变化?会变为0吗?

我们先回答后面的问题,大小是会变化的,一般来说大小是动态变化的,也可能会变成0。指针在右移的时候,一般有3种情况

1. 右指针不动,左指针移动 -> 范围缩小

2. 左右指针都移动 -> 范围扩大

3. 左右指针都移动 -> 范围缩小

左指针 = 收到的确认序号,右指针 = 确认序号 + min(窗口大小,有效数据),我们之前讲的流量控制就是通过滑动窗口来进行控制的,它不仅能将传输速率往小了控制,也能往大了控制。

3. 滑动窗口在发送缓冲区中移动指针会发生越界的情况吗?

不会的,TCP 的滑动窗口机制是基于一种“环形缓冲区”的思想设计的。通过这个机制,发送方和接收方在建立连接时会协商一个初始序号(例如 1234)。从这个初始序号开始,数据会被分配到序号加上数组下标的相应位置。由于滑动窗口使用环形缓冲区的方式,发送方的“发送指针”会随着数据的发送和确认逐步前进。如果发送窗口达到发送缓冲区的末尾,它会“回绕”到缓冲区的起始位置,继续发送未发送的数据。因此,滑动窗口的指针不会越界,它始终会保持在有效的缓冲区范围内。

5. TCP 延迟应答

在 CS 端互相发送数据的时候,我们已经知道,发送方一次发送更多的数据,发送的效率就会更高,也就是说发送方认为接收方能够接受更多的数据,这样的效率就会变高,意味着接收方应该尽量给发送方通报一个更大的窗口!

那我们如何让接收方给对方通告一个更大的窗口呢?——收到报文不着急应答,也就是延迟应答,这从根本上来说是一个赌概率的机会,赌上层能否尽快地取走数据,从而延迟应答时能回应一个更大的窗口。也是因此,如果我们要设计一个服务器,我们比较推荐的做法就是每次都尽快地通过read, recv 等函数将数据全部从 TCP 中拿上来!

那么所有的包都可以延迟应答么?当然不是,延迟应答有数量限制与时间限制,即

数量限制:每隔N个包就应答一次
时间限制:超过最大延迟时间就应答一次

具体的数量和超时时间,不同的操作系统有差异。一般来说 N 取 2,超时时间取 200ms。

6. TCP 捎带应答

捎带应答(piggybacking)是 TCP 协议中一个常见的机制,尤其是在数据传输阶段。这个机制具体来说是指在某个数据包的应答(ACK)中“捎带”回传数据,避免单独发送确认包,从而提高效率,减少网络负担。

7. TCP 拥塞控制

目前我们学习到的几乎所有策略,都是在两端的机器上起作用的。而 TCP 还为我们考虑了网络的问题——也就是拥塞控制!

如果任意主机在发送数据的时候出现了问题,这个问题可能不仅仅是对方主机出现了问题,也有可能是网络出现了问题。举个例子

  • 如果通信的时候,出现了少量的丢包 -> 正常情况
  • 如果通信的时候,出现了大量的丢包 -> 网络出现了问题

 这个网络出现了问题只是一个笼统的概述,具体问题可能是硬件出现了问题,也有可能是数据量太大,引起了阻塞。

如果通信双方都出现了大量的数据丢包(大量数据超时)问题,那么 TCP 就会判断网络出问题了(网络拥塞了)。那我们作为发送方,应该怎么办呢?——暂缓一下,间歇性的发送少量数据,绝对不能立刻对报文进行超时重发!这样做只会加重网络的拥塞情况。

由于各台主机均使用 TCP/IP 协议,所以网络出现了拥塞对多台主机来说是“共识”,因此拥塞控制的策略是每台识别到当前网络已经拥塞的主机都要做的事。实际上,滑动窗口的大小其实也与拥塞窗口相关,即

滑动窗口大小 = min(接收窗口大小,拥塞窗口大小)

接收窗口大小考虑的是对方主机的接收能力,而拥塞窗口大小则考虑的是动态的网络的接收能力。所以我们现在可以重新定义一下滑动窗口的右指针了,即

右指针 = min(接收窗口,有效数据,拥塞窗口) 

拥塞窗口是主机判断网络健康程度的指标,如果超过了拥塞窗口,会引发网络拥塞,反之则不会。如果网络出现了拥塞,当双方发送少量报文都表示可行的时候,网络应该趋于健康了,此时应当尽快恢复正常通信了!但是网络是动态的,所以拥塞窗口肯定不是静态的,如图所示

⑤ TCP 可靠与效率小结

目前我们可以看到 TCP 为了数据传输的可靠性,采取了如下方案

  • 校验和
  • 序列号(按序到达) -> 去重工作
  • 确认应答 -> 保证可靠性的核心策略
  • 超时重发
  • 连接管理
  • 流量控制
  • 拥塞控制

而为了提供数据传输的效率,采取了如下方案 

  • 滑动窗口
  • 快速重传
  • 延迟应答
  • 捎带应答

⑥ TCP 的面向字节流特性

面向字节流的意思是读写不需要一一匹配,对用户层来说,用户只关心应用层的协议;对于传输层来说,OS 负责具体的网络通信细节。由于缓冲区的存在,TCP 程序的读和写不需要一一匹配,举个例子

  • 写 100 个字节数据时,可以调用一次 write 写 100 个字节,也可以调用 100 次 write ,每次写一个字节。
  • 读 100 个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以一次 read 100 个字节,也可以一次 read 一个字节,重复 100 次。

在 TCP 这一层,它并不关心上层协议,也不关心上层的报文格式,只有字节的概念!

在应用层这一层,用户对报文进行处理必须一个一个进行处理!将提取上来的字节流转换成一个一个完整的请求,这之后进行反序列化。

粘包问题 

如果用户在提取字节流后没有将其处理成一个个请求或者少处理了几个请求,对于应用层来说就会产生粘包问题,而我们解决粘包问题最简单的做法就是定制协议!这也是我们在做网络版计算器时封装了 Encode 与 Decode 函数的原因(详见Linux网络——应用层中的网络计算器)。

所以,如何解决用户层粘包问题呢?——在应用层通过协议,明确报文与报文之间的边界。举几个例子

1. 使用定长报文

2. 使用特殊字符

3. 使用自描述字段 + 定长报头

4. 使用自描述字段 + 特殊字符(如 HTTP)

⑦ TCP 的异常情况

TCP 协议本身是基于连接的,而连接本身是和文件直接相关的!文件的生命周期是跟随进程的。如果在 CS 双方进程通信时,如果 C 端因为一些原因导致进程终止,那么连接就会正常终止。那要是 C 端直接断电/网线断开,那 S 端会一直维持住 TCP 吗?——TCP 有保活机制,即 S 端在一段时间内不会断开,而超过了一定时间会自动断开。

4. 文件与 socket 的关系

如图所示

对于我们目前学习的协议栈来说,OS 负责的是传输层与网络层,OS 在每一层中需要做的只有两件事

1. 使用特定的数据结构表述协议

2. 生成与特定协议相匹配的方法集

而对于每一层来说,均会收到多个报文,服务器需要对他们进行先描述后组织,我们查看 Linux 内核可以看到

事实上,我们在协议栈中向上向下交付,本质上就是在向头空间/尾空间添加报头,也就是说我们需要进行的封包与解包操作本质就是在移动指针!而我们在不同层间移动时,并不需要进行频繁的拷贝,只需要移动指针即可! 

相关文章:

Linux网络——传输层

目录 1. 再谈端口号 2. UDP ① UDP 协议的报文 ② UDP 协议报头的本质 3. TCP ① TCP 缓冲区的理解 ② TCP 协议的报文 1. 16位源端口号 && 16位目的端口号 && 4位首部长度 2. 16位窗口大小 3. 32位序号 && 32位确认序号 4. 六个标记位 ③…...

2024年认证杯SPSSPRO杯数学建模A题(第一阶段)保暖纤维的保暖能力全过程文档及程序

2024年认证杯SPSSPRO杯数学建模 A题 保暖纤维的保暖能力 原题再现: 冬装最重要的作用是保暖,也就是阻挡温暖的人体与寒冷环境之间的热量传递。人们在不同款式的棉衣中会填充保暖材料,从古已有之的棉花、羽绒到近年来各种各样的人造纤维。不…...

采药 刷题笔记 (动态规划)0/1背包

P1048 [NOIP2005 普及组] 采药 - 洛谷 | 计算机科学教育新生态 动态规划 0/1背包 的本质在于继承 一行一行更新 上一行是考虑前i个物品的最优情况 当前行是考虑第i1个物品的情况 当前行的最优解 来自上一行和前i个物品的最优解进行比较 如果当前装了当前物品&#xff…...

LabVIEW MathScript工具包对运行速度的影响及优化方法

LabVIEW 的 MathScript 工具包 在运行时可能会影响程序的运行速度,主要是由于以下几个原因: 1. 解释型语言执行方式 MathScript 使用的是类似于 MATLAB 的解释型语言,这意味着它不像编译型语言(如 C、C 或 LabVIEW 本身的 VI&…...

大数据新视界 -- Hive 数据湖架构中的角色与应用(上)(25 / 30)

💖💖💖亲爱的朋友们,热烈欢迎你们来到 青云交的博客!能与你们在此邂逅,我满心欢喜,深感无比荣幸。在这个瞬息万变的时代,我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…...

电脑关机的趣味小游戏——system函数、strcmp函数、goto语句的使用

文章目录 前言一. system函数1.1 system函数清理屏幕1.2 system函数暂停运行1.3 system函数电脑关机、重启 二、strcmp函数三、goto语句四、电脑关机小游戏4.1. 程序要求4.2. 游戏代码 总结 前言 今天我们写一点稍微有趣的代码,比如写一个小程序使电脑关机&#xf…...

elasticsearch是如何进行搜索的?

请求与转发 协调节点确定参与搜索的目标索引,及其通过分片路由表确定分片对索引所在分片中选择任意节点并发请求多个分片的副本分片 副本选择策略 副本选择主要考虑 分片健康状态:选择状态为 green 或 yellow 的副本节点负载情况:避免查询…...

【Linux课程学习】:站在文件系统之上理解:软硬链接,软硬链接的区别

🎁个人主页:我们的五年 🔍系列专栏:Linux课程学习 🌷追光的人,终会万丈光芒 🎉欢迎大家点赞👍评论📝收藏⭐文章 Linux学习笔记: https://blog.csdn.net/d…...

如何做好一份技术文档

要做好一份技术文档,你需要遵循以下步骤: 1. 明确文档目标: 确定文档的目的和受众。 了解受众的技术水平和背景,以便调整内容的深度和语言风格。 2. 收集信息: 搜集所有与主题相关的技术细节、数据、图表和…...

优雅关闭进程

原文地址:优雅关闭进程 – 无敌牛 欢迎参观我的个人博客:无敌牛 – 技术/著作/典籍/分享等 介绍 本文涉及到进程对信号的响应,关于信号的一些基本知识,可以参考往期文章:linux系统信号简介 – 无敌牛 一个进程正常…...

【WEB开发.js】addEventListener事件监听器的绑定和执行次数的问题(小心踩坑)

假设我们有一个按钮,用户点击该按钮后,会选择一个文件,且我们希望每次点击按钮时只触发一次文件处理。下面我会给你一个简单的例子,展示放在函数内部和放在函数外部的区别。 1. 将事件监听器放在函数内部(问题的根源&…...

将 x 减到 0 的最小操作数 C++滑动窗口

给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。 如果可以将 x 恰好 减到 0 ,返回 最小操作数 &#x…...

深入解析 C++ 中的 common_reference_with 和 common_with:设计原理与复杂性

在 C 标准库的设计中,类型之间的兼容性和安全性是至关重要的,尤其是在泛型编程中。为了实现高效且安全的类型推导和转换,C 提供了一些复杂的概念和工具来确保不同类型之间能够正确协同工作。common_reference_with 和 common_with 这两个概念…...

从CPU缓存与指令重排序探讨JMM

目录 问题背景 解决思路 hb关系的应用 原子性问题 问题背景 1. 编译器和CPU优化: 编译器和CPU为了提升单线程程序的性能,会对代码进行优化,如指令重排序、延迟计算等。这些优化在单线程环境下不会影响程序的最终结果,但在多线…...

ETSI EN 300328 标准的一些笔记

ETSI - European Telecommunications Standards Institute 欧洲电信标准化协会 ETSI EN 300328 是欧洲协调标准,此标准适用于工作在2.4G频段范围内运行的宽频传输系统和设备的无线电频谱。 例如 WIFI、Zigbee、蓝牙、 (国内的星闪)。不涵盖UWB。 符合了EN 300328标…...

各大浏览器(如Chrome、Firefox、Edge、Safari)的对比

浏览器如Chrome、Firefox、Edge等在功能、性能、隐私保护等方面各有特点。以下是对这些浏览器的详细对比,帮助你选择合适的浏览器。 1. Google Chrome 市场份额:Chrome是目前市场上最流行的浏览器,约占全球浏览器市场的65%以上。 性能&#…...

AD7606使用方法

AD7606是一款8通道最高16位200ksps的AD采样芯片。5V单模拟电源供电,真双极性模拟输入可以选择10 V,5 V两种量程。支持串口与并口两种读取方式。 硬件连接方式: 配置引脚 引脚功能 详细说明 OS2 OS1 OS2 过采样率配置 000 1倍过采样率 …...

双向长短期记忆(Bi-LSTM)神经网络介绍

长短期记忆(Long Short-Term Memory, LSTM)神经网络: 1.是Hochreiter和Schmidhuber设计的循环神经网络(Recurrent Neural Network, RNN)的改进版本。LSTM模型借鉴了人类大脑的选择性输入和选择性遗忘机制,获取序列中的关键信息,遗忘和当前预测…...

openGauss开源数据库实战十八

文章目录 任务十八 openGauss逻辑结构:构:用户和权眼管理任务目标实施步骤一、准备工作二、用户和角色管理1.使用CREATE USER语句创建用户2.使用CREATE ROLE语句创建用户3.删除用户和角色 三、权限管理1.系统权限清理工作 任务十八 openGauss逻辑结构:构:用户和权眼管理 任务目…...

JVM 性能调优 -- JVM 调优常用网站

前言: 上一篇分享了 JDK 自带的常用的 JVM 调优命令和图形化界面工具,本篇我们分享一下常用的第三方辅助 JVM 调优网站。 JVM 系列文章传送门 初识 JVM(Java 虚拟机) 深入理解 JVM(Java 虚拟机) 一文搞…...

现在的电商风口已经很明显了

随着电商行业的不断发展,直播带货的热潮似乎正逐渐降温,而货架电商正成为新的焦点。抖音等平台越来越重视货架电商,强调搜索功能的重要性,预示着未来的电商中心将转向货架和搜索。 在这一转型期,AI技术与电商的结合为…...

基于AT89C52单片机的电子时钟与温湿度检测系统

✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...

Wwise SoundBanks内存优化

1.更换音频格式为Vorbis 2.停用多余的音频,如Random Container的随机脚步声数量降为2个 3.背景音乐勾选“Stream”。这样就让音频从硬盘流送到Wwise,而不是保存在内存当中,也就节省了内存 4.设置最大发声数Max Voice Instances 5.设置音频…...

Next.js 独立开发教程(十三):错误处理(Error Handling)

系列文章目录 Next.js 开发教程(一):入门指南-CSDN博客 Next.js 开发教程(二):从零构建仪表盘应用-CSDN博客 Next.js 开发教程(三):CSS 样式的完整指南-CSDN博客 Next.js 独立开发教程&…...

【Halcon】边缘检测算子汇总(一)

frei_amp 功能:使用Frei-Chen算法检测图像的边缘振幅。 参数: 输入图像(Image):待处理的原始图像。输出梯度图像(ImageEdgeAmp):经过Frei-Chen算法处理后的边缘振幅图像。工作原理:frei_amp算子通过计算图像一阶导数的近似值来检测边缘。它使用两个特定的滤波器掩模(…...

前端开发中Token存储:选择Cookie还是localStorage?

在现代前端开发领域,用户身份验证与状态管理不可或缺,而 token 作为身份验证机制的核心要素,对保障用户信息安全至关重要。然而,token 的存储方式对应用安全性和用户体验有着直接影响。本文将从安全性、便捷性和使用场景等维度,深度剖析在 Cookie 和 localStorage 中选择存…...

Scala中的正则表达式01

规则类型具体规则示例说明单字符大多数字符匹配自身正则表达式 abc,文本 abca 匹配 a,b 匹配 b,c 匹配 c方括号 [ ][ ] 定义字符集,匹配其一[abc],文本 a、b 或 c[abc] 匹配 a、b 或者 c排除字符集 [^ ][^ ] 开头加 ^&…...

机器学习——决策树模型

决策树是如何工作的? 假设你在经营一家猫收养中心,并提供了一些功能,你想训练一个分类器来快速告诉你,动物到底是不是猫,这里有10个训练例子,并与这10个例子中的每一个相关联,我们将有关于动物…...

#3003. Jed‘s MEX

刚开始直接硬来,要么TLE要么WA 后面改成另一种思路: 先把ai大于n的全都转换为刚好小于n的数,记录在cnt[i]中,代表ai有多少个 然后从高往低走,把cnt大于1的分到下面去,使数尽可能分布得广一些 然后从低往…...

数据集增强:提升深度学习模型泛化能力的关键技术

在深度学习中,数据是模型性能的基石。大规模、高质量的数据集通常能显著提高模型的泛化能力,帮助模型在真实场景中做出更准确的预测。然而,在很多实际应用中,数据收集困难、昂贵或者受限,尤其是当数据集相对较小或标注…...

JS实现高效导航——A*寻路算法+导航图简化法

一、如何实现两点间路径导航 导航实现的通用步骤,一般是: 1、网格划分 将地图划分为网格,即例如地图是一张图片,其像素为1000*1000,那我们将此图片划分为各个10*10的网格,从而提高寻路算法的计算量。 2、标…...

在wordpress添加自定义文章类型

实现思路 在Once主题中,有文章,页面等编辑的文案类型,文章类型主要做文案输出,而页面类型主要做一些界面菜单的操作。参考文章类型,使用自定义页面模板,实现一个自定义文章类型,例如**笔记(nod…...

[node.js] [HTTP/S] 实现 requests 发起 HTTP/S/1.1/2.0 请求

node.js 使用 V8 引擎来编译运行 javascript 代码,与浏览器中的环境不同的是,node.js 不包含 DOM 和 BOM 模块。 本文使用 node.js 的官方库来实现一个简单的 requests() 函数,可以用来发送 HTTP/1.1 和 HTTP/2.0 的请求。有关 HTTP/1.1 和 …...

基于Java Springboot线上约拍摄影预约微信小程序

一、作品包含 源码数据库全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui uniapp 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse 微信开发者工…...

node.js基础学习-cheerio模块-简单小爬虫(五)

学习cheerio模块,简单做一个爬取图片网站的图片,并且将这些图片下载到本地指定的文件夹下,很多图片网站都有一些反爬取的机制,找的好几个都会报302错误,所以我找了一个小的图片网站,这个没有反爬取机制&…...

吾杯网络安全技能大赛WP(部分)

吾杯网络安全技能大赛WP(部分) MISC Sign 直接16进制解码即可 原神启动 将图片用StegSolve打开 找到了压缩包密码 将解出docx文件改为zip 找到了一张图片和zip 再把图片放到stegSlove里找到了img压缩包的密码 然后在document.xml里找到了text.zip压缩包密码 然后就出来fl…...

【python自动化一】pytest的基础使用

1.pytest简述 pytest‌ 是一个功能强大且灵活的Python测试框架,其主要是用于流程控制,具体用于UI还是接口自动化根据个人需要而定。且其具有丰富插件,使用时较为方便。咱们具体看下方的内容,本文按照使用场景展开,不完…...

使用 CFD 仿真进行阀门性能分析:第 II 部分

了解如何使用 Ansys Discovery 通过优化模式获得准确的阀门性能结果。 第 II 部分:优化模式下的模拟 阀门的模拟可以在 Explore (探索) 模式和 Refine (优化) 模式下执行。Explore 模式允许快速仿真,在长达…...

Node.js实现WebSocket教程

Node.js实现WebSocket教程 1. WebSocket简介 WebSocket是一种在单个TCP连接上提供全双工通信的协议,允许服务器和客户端之间进行实时、双向通信。本教程将详细讲解如何在Node.js中实现WebSocket。 2. 技术选型 我们将使用ws库来实现WebSocket服务器,…...

使用 Statsmodels 进行统计建模与分析

使用 Statsmodels 进行统计建模与分析 Statsmodels 是 Python 中一个功能强大的库,用于执行统计建模和计量经济学分析。它提供了一系列经典的统计模型和评估方法,涵盖线性回归、时间序列分析和广义线性模型等。 本文将带你深入了解 Statsmodels 的功能…...

【Linux】进程间通信

目录 一、管道 (一)概念 (二)匿名管道 1、概念 2、函数介绍 3、示例代码 4、原理 (三)命名管道 1、概念 2、函数介绍 3、示例代码 4、原理 (四)管道的读写规则 &#x…...

Python 网络爬虫高级教程:分布式爬取与大规模数据处理

经过基础爬虫和进阶爬虫的学习,我们已经掌握了爬虫的基本原理、动态内容处理及反爬机制的应对。然而,当我们面对海量数据或需要高效爬取多个站点时,分布式爬虫和数据存储、处理能力就显得尤为重要。本篇博客将带你迈向网络爬虫的高级阶段&…...

猫爪背后的情感密码

当家中那只可爱的猫咪时不时用它的小爪子轻拍我们时,很多人或许只当作是调皮捣蛋,实则背后大有深意。 猫用爪子打,可能是在向我们发出玩耍的邀约。在猫咪的天性里,捕猎本能根深蒂固。它们在幼年时与同伴的嬉戏打闹,便…...

【自用】管材流转项目前端重部署流程 vue2 webpackage4 vuecli4

一、配置 1.下载项目,使用 IDEA 打开,并配置 Nodejs 它提示我,需要 Node.js,因为 nodejs 14 的 installer 已经官网已经找不到了,使用 fnm 又太麻烦, 所以直接采用在 IDEA 中下载的方式就好了。 2.清除缓…...

关于c的子进程 fork()

fork() 是一个非常重要的系统调用,用于在 Unix-like 操作系统中创建一个新的进程。它会将当前进程(父进程)复制成一个新的进程(子进程)。子进程会从父进程的代码处继续执行,但具有不同的进程 ID。 fork() …...

耀圣控制设备有限公司:优质压滤机阀门的引领者

耀圣控制设备有限公司:优质压滤机阀门的引领者 在压滤机阀门领域,耀圣控制设备有限公司以其卓越的品质和领先的技术,成为了行业内备受瞩目的品牌。 耀圣控制设备有限公司专注于压滤机阀门的研发与生产,凭借着先进的工艺和严格的质…...

【C语言】结构体(四)

本篇重点是typedef关键字 一,是什么? typedef用来定义新的数据类型,通常typedef与结构体的定义配合使用。 简单来说就是取别名 ▶ struct 是用来定义新的数据类型——结构体 ▶ typedef是给数据类型取别名。 二,为什么&#xf…...

面向源代码的软件可信度量模型 T_{na}

面向源代码的软件可信度量模型 T n a T_{na} Tna​ 课程:软件质量分析 作业 可编写下面的java程序: package org.example;public class SourceCodeOrientedModel {public static void main(String[] args) {int total 41;int[] m {9, 22, 9, 5, 7, 1…...

Java11使用JVM同一日志框架启用日志记录

你可以使用-Xlog选项配置或启用Java虚拟机同一日志框架的日志记录。 -Xlog:gc*trace:file/Users/xx/gc-%t.log:time,tags,level,pid,tid,hostname,path:filecount3,filesize10K -Xlog:gc*trace:stdout:time,tags,level,pid,tid,hostname:filecount3,filesize10K -Xlog:gc*trac…...

k8s容器存储接口 CSI 相关知识

容器存储接口 CSI 相关知识 参考: https://blog.csdn.net/lovely_nn/article/details/122880876 https://developer.aliyun.com/article/783464 https://www.cnblogs.com/varden/p/15139819.html存储商需实现 CSI 插件的 NodeGetVolumeStats 接口,Kube…...