协议栈——收发数据(拼接网络包,自动重发,滑动窗口机制)

news2025/1/11 18:29:28

目录

协议栈何时发送数据~

数据长度

IP模块的分片功能

发送频率

网络包序号~利用syn拼接网络包ack确认网络包完整

确定偏移量

服务器ack确定收到数据总长度

序号作用

双端告知各自序号

协议栈自动重发机制

大致流程

ack等待时间如何调整

是否需要等待收到ack号后在发送数据~滑动窗口

合并ack号和窗口大小

总结


1.发送方协议栈根据DNS提供的服务器ip端口确定和服务器通信使用的socket套接字, 填充tcp头部信息(发送接受方ip端口信息),将syn设置为1,修改当前socket状态为正在连接

2.发送方委托ip模块发送给服务器的ip模块。服务器的ip模块再给到服务器的tcp模块,根据头部信息找到发送发要连接的socket并填入对应的客户端ip端口,接着创建tcp头部信息(发送和接收方ip端口)此时的发送方是服务器,并填充syn报文值为1(代表链接成功)和一个ack等于1的报文(代表网络包和序号都收到)委托服务器ip模块发送(建立连接的确认过程文末讲解)

3.客户端收到后完善socket中的服务端ip地址端口信息,判断服务端返回的syn是否为1来确定是否链接成功 接着设置socket状态为连接完毕;最后 向服务端模块在发送一个ack报文1确认网络包收到(tcp头部信息还是必须要填写的)

协议栈何时发送数据~

建立连接后应用就可以和服务端进行通信了,应用发的数据会缓存到协议栈中,但是何时发送呢?有两种情况,下面介绍

数据长度

应用可以指定发送数据的大小,如果协议栈收到发送指令就进行发送的话,不可控而且效率低;因此协议栈内部会指定一个长度,当达到长度后在进行发送,此前发送的数据保存到缓冲区中。

这个长度就是mtu(包含了tcp,ip头部控制信息);除去网络包的头部信息后叫做真实数据的最大传输单元,叫做mss。

MTU:Maximum Transmission Unit,最大传输单元

头部信息是固定长度,MTU长度又是固定的,因此mss可知。

图中列出的是TCP头部,同理UDP也是一样将TCP头部换为UDP头部也一样

MTU:以太网中传输的一个网络包的最大数据长度,一般为1500字节。

MSS:除去TCP/UDP模块的头部控制信息之后,一个网络包所能容纳的数据的最大长度。

报头,起始分界符,FCS是协议栈下层的网卡模块添加的,网卡模块介绍。

IP模块的分片功能

image.png

我们来回顾下,协议栈的TCP和UDP模块填充各自模块的头部控制信息后,交给IP模块去发送数据,最后一层是IP模块,因此IP模块发送的数据长度也就是协议栈发出的数据长度。

TCP/UDP模块发送的数据肯定超过了以太网和通信线路的最大传输长度也就是MTU,这个时候IP模块就会对来自TCP/UDP模块的数据进行分片,在这篇文章中的分片重组进行了介绍。

接下来举个例子看下UDP模块可以发送的最大数据长度协议栈最终发出去的数据长度(也即IP模块发出去的数据长度) 是如何变化的(TCP也一样):

UDP模块可发送的最大传输数据长度是IP包的最大长度减去IP头部和UDP头部。IP包的最大长度为16比特(2的16次方-1)也就是65535字节,不考虑其他可选字段的话 IP头部最大长度为20字节,UDP头部是8字节,也就是UDP可传输最大长度为65507字节,超过了协议栈所规定的最大传输长度MTU 1500字节,因此需要使用IP模块的分片功能进行切割。,关于切割分片和重组分片请查看上面提到的文章有详细介绍。

可以看到TCP模块和UDP模块所规定的最大数据长度和协议栈最终发出的数据长度之间经过了IP模块的分片功能处理。也很好理解,越往底层传输的数据量越小,总不能让上层传输的是0101或者数据长度特别小的数据吧(使用者也不方便),越往下每一层都进行切割最后切割到可以在网线中传输的0101信号。

发送频率

除了考虑数据长度外,还有一个因素要考虑,就是应用发送数据的频率。

应用程序发送数据的频率不高的时候,如果每次都等到长度接近MSS时再发送,可能会因为等待时间太长而造成发送延迟,这种情况下,即便缓冲区中的数据长度没有达到MSS,也应该果断发送出去。为此,协议栈的内部有一个计时器,当经过一定时间之后,就会把网络包发送出去。

大致流程:

因此上层应用程序发送的数据会放到协议栈的缓冲区中,当满足上面两个因素条件之一时(应用程序也可以指定是否立即发送数据还是按照协议栈的规则判断时机)就可以发送数据了,首先切割mss为单位的数据块,在每个数据库开头都加上头部控制信息:

拆分示意图:协议栈的TCP模块负责添加tcp头部信息,接着委托协议栈的ip模块检查是否需要切片然后发送消息,ip模块会再分片后的包中添加ip头部和mac头部信息

网络包序号~利用syn拼接网络包ack确认网络包完整

上面说过网络包会拆分,那么服务器是怎么知道该怎么拼接这些网络包还原成最初的数据呢?答案是通过序号

确定偏移量

当拆分数据拼接成网络包的时候,会将这块数据相对于起始数据的偏移字节算出来携带到tcp头部;除此之外还会携带数据包的总长度

网络包的数据长度不是放到tcp头部中, 因为用整个网络包的长度(固定的mss)减去头部的长度就可以得到数据的长度,所以接收方可以用这种方法来进行计算。有了上面两个数值,我们就可以知道发送的数据是从第几个字节开始,长度是多少了。

服务器ack确定收到数据总长度

接收方会将到目前为止接收到的数据长度加起来,计算出一共已经收到了多少个字节,然后将这个数值写入TCP头部的ACK号中发送给发送方。

序号作用

序号的作用:上面的偏移量是携带到tcp头部中,很容易就会拿到自行还原数据破解的;因此在传递过程中每个包的数据长度都要进行基于某个值偏移,也就是将原来的偏移量要加上序号。

如第一个包默认的偏移量本来是0,序号值是666,那么现在添加序号之后第一个网络包的偏移量就是666

这样对方就搞不清楚偏移量到底是从多少开始计算的。

实现这种方式需要在开始收发数据之前将初始值告知通信对象。同样服务端也需要告知序号值这样客户端就知道该如何拆分。这个偏移量就是序号值

双端告知各自序号

那么什么时候把这个初始序号值发送给对方呢?


在发送连接的阶段中会将syn设置为1, 在将SYN设为1的同时,还需要同时设置序号字段的值,而这里的值就代表序号的初始值。这个syn就是通过告知序号值用于后面通信步调一致(知道每个网络包头部的偏移量是基于序号值+当前网络包相对于数据头部的偏移量),同样第二个阶段服务器返回syn1也是告知客户端之后发送包的序号值;同时发送ack数值告知客户端数据是否发送完整(需要利用客户端传过来的序号值,偏移量减去序号就是真正的偏移量了);客户端收到后同样也需要向服务端发送ack来确认服务端发过来的数据是否完整(服务端发送syn1的时候将服务端的序号也发送了过来,也是用的这个序号来拼接服务端发送过来的数据)

这个就是大致的网络包拼接流程:


协议栈自动重发机制

自动重发机制:协议栈会在收到ack号确认之前中会存放发送的数据,如果某一个ack号没有发送过来就会重发这个数据。

大致流程

这样一来,无论网络中发生任何错误,协议栈都可以发现并采取补救措施(重传网络包) 。反过来说,有了这一机制,就不需要在其他地方对错误进行补救了。

因此,网卡、集线器、路由器都没有错误补偿机制,一旦检测到错误就直接丢弃相应的包。应用程序也是一样,因为采用TCP传输,即便发生一些错误对方最终也能够收到正确的数据,所以应用程序只管自顾自地发送这些数据就好了。不过,如果发生网络中断、服务器宕机等问题,那么无论TCP怎样重传都不管用。这种情况下,无论如何尝试都是徒劳,因此TCP会在尝试几次重传无效之后强制结束通信,并向应用程序报错


ack等待时间如何调整

ack等待时间也叫超时时间,如果协议栈发送数据等待ack的返回这段时间超过了超时时间,就会重新发送这个网络包。

但是网络信号是可以改变的,所以超时时间也应该和网络信号的好坏动态调整;并且网络信号差的时候不仅仅只是重发一个包这么简单后面的所有网络包都会收到影响(这个和安卓的anr排查差不多)

这个等待时间是根据ACK号返回所需的时间来判断的。具体来说,TCP会在发送数据的过程中持续测量ACK号的返回时间,如果ACK号返回变慢,则相应延长等待时间;相对地,如果ACK号马上就能返回,则相应缩短等待时间。

是否需要等待收到ack号后在发送数据~滑动窗口

现在的链路是 客户端服务端确认好端口ip后就开始通信了,客户端每次发送数据包携带数据长度信息 服务端返回ack信息确认是否完整收到(反过来也是一样的流程)

但是有个缺点 就是接收方必须等到发送方返回ack才能继续传输这会增加延时降低通信效率

所以采用滑动窗口的方式。客户端只管发送 不管是否收到ack报文 。

所谓滑动窗口,就是在发送一个包之后,不等待ACK号返回,而是直接发送后续的一系列包。这样一来,等待ACK号的这段时间就被有效利用起来了。

这样虽说提高了效率但是如果不等返回ACK号就连续发送包, 服务端的处理缓冲区会造成背压问题 (发送包的频率超过接收方处理能力的情况)。

发送的数据会存储在接收方的缓冲区中,之后取出来处理拼接ack号,如果什么都不管一直发送,缓冲区的数据会溢出,某些数据就会被放弃,这个肯定不是想要的结果。因此需要出一种机制能够知道对方缓冲区可以接受多少数据,根据这个值来判断是否继续发送,当服务器的缓冲区数据处理后,也需要告知客户端(通过tcp头部中的窗口字段)

图示:

合并ack号和窗口大小

如果接收方可以告知发送方当前可以容纳多少数据呢?发送方自己判断已发送的数据是不是到达极限从而暂停 当接收方处理数据时再通知客户端当前缓冲区容纳数据客户端在发送,这样是不是完美了?

并不是,如何确定ack和发送缓冲区数量的包呢?是分开发送吗还是合并 如果每次都携带缓冲区数量是不必要的 发送方知道后自己就可以判断是否达到缓冲区处理极限决定是否发送之后数据,那分开呢?又会降低效率 增加包的数量无疑会导致延迟效率下降


因此 这两个得合并但是不能每次都发,所以加了个延时的操作。

分为以下这几种情况

  • 假如我等待发送ack数据时正好服务器处理了数据缓冲区容量更新需要通知到客户端这时候合并 就可以了。
  • 再比如我处理完数据了 等待一段时间如果ack也要发送也用合并发送
  • 并且如果需要连续发送缓冲区的数量时说明处理速度很快只要同步最后一次的缓冲区可用数量即可(比如200---400--600)等待一段时间发生减少包数量
  • 需要连续发送ack时也是一样 说明客户端连续发送数据 接收方这边等待一段时间之后直接把刚收到的数据总和返回给客户端就行

通过延时解决合并不必要和独立数量多的缺点 具体延时根据实际情况判断(很多时候服务器处理速度很快几乎发送完数据就能立马处理完不需要缓冲区数量这个信息)

总结

协议栈会检查收到的数据块和TCP头部的内容,判断是否有数据丢失,如果没有问题则返回ACK号。然后,协议栈将数据块暂存到接收缓冲区中,并将数据块按顺序连接起来还原出原始的数据,最后将数据交给应用程序。具体来说,协议栈会将接收到的数据复制到应用程序指定的内存地址中,然后将控制流程交回应用程序。将数据交给应用程序之后,协议栈还需要找到合适的时机向发送方发送窗口更新。

原文链接:协议栈——收发数据(拼接网络包,自动重发,滑动窗口机制) - 掘金 (juejin.cn)

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

动态链接那些事

1、为什么要动态链接 1.1 空间浪费 对于静态链接来说,在程序运行之前,会将程序所需的所有模块编译、链接成一个可执行文件。这种情况下,如果 Program1 和 Program2 都需要用到 Lib.o 模块,那么,内存中和磁盘中实际上就…

WEB3 solidity 带着大家编写测试代码 操作订单 创建/取消/填充操作

好 在我们的不懈努力之下 交易所中的三种订单函数已经写出来了 但是 我们只是编译 确认了 代码没什么问题 但还没有实际的测试过 这个测试做起来 其实就比较的麻烦了 首先要有两个账号 且他们都要在交易所中有存入 我们还是先将 ganache 的虚拟环境启动起来 然后 我们在项目…

【计算机组成原理】考研真题攻克与重点知识点剖析 - 第 1 篇:计算机系统概述

前言 本文基础知识部分来自于b站:分享笔记的好人儿的思维导图,感谢大佬的开源精神,习题来自老师划的重点以及考研真题。此前我尝试了完全使用Python或是结合大语言模型对考研真题进行数据清洗与可视化分析,本人技术有限&#xff…

关于算法复杂度的几张表

算法在改进今天的计算机与古代的计算机的区别 去除冗余 数据点 算法复杂度 傅里叶变换

WebSocket实战之五JSR356

一、前言 前几篇WebSocket例子服务端我是用NodeJS实现,这一篇我们用Java来搭建一个WebSocket服务端,从2011年WebSocket协议RFC6455发布后,大多数浏览器都实现了WebSocket协议客户端的API,而对于服务端Java也定义了一个规范JSR356,即Java API for WebSoc…

软件工程与计算总结(二)软件工程的发展

本章开始介绍第二节内容,主要是一些历史性的东西~ 一.软件工程的发展脉络 1.基础环境因素的变化及其对软件工程的推动 抽象软件实体和虚拟计算机都是软件工程的基础环境因素,它们能从根本上影响软件工程的生产能力,而且是软件工程无法反向…

设计模式(包括Spring)、贯穿项目梳理与源码知识点

目标:高复用性,高内聚,低耦合 目的:高可读性,重用性,可靠性 类的六种关系 依赖,类中用到了对方,没有对方连编译都通不过,如下的几种关系全部是依赖关系泛化/继承&…

05_对象性能模式

对象性能模式 面向对象很好地解决了“抽象”的问题,但是必不可免地要付出定的代价。对于通常情况来讲,面向对象的成本大都可以忽略计。但是某些情况,面向对象所带来的成本必须谨慎处理。 典型模型: SingletonFlyweight Singleton 单件模式…

你写过的最蠢的代码是?——AI领域的奇妙体验

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…

vertx的学习总结三

一、event bus是什么 各个verticle的通信 二、point-to-point, request-reply, publish/subscribe 通过 the event bus 例题一:点对点 package eventBus;import io.vertx.core.AbstractVerticle; import io.vertx.core.Vertx;public class EventBusExample exte…

【Redis】基础数据结构-字典

Redis 字典 基本语法 字典是Redis中的一种数据结构,底层使用哈希表实现,一个哈希表中可以存储多个键值对,它的语法如下,其中KEY为键,field和value为值(也是一个键值对): HSET key…

【Java 进阶篇】JDBC查询操作详解

在数据库编程中,查询是一项非常常见且重要的操作。JDBC(Java Database Connectivity)提供了丰富的API来执行各种类型的查询操作。本篇博客将详细介绍如何使用JDBC进行查询操作,包括连接数据库、创建查询语句、执行查询、处理结果集…

ARMv8如何读取cache line中MESI 状态以及Tag信息(tag RAM dirty RAM)并以Cortex-A55示例

Cortex-A55 MESI 状态获取 一,系统寄存器以及读写指令二,Cortex-A55 Data cache的MESI信息获取(AARCH 64)2.1 将Set/way信息写入Data Cache Tag Read Operation Register2.2 读取Data Register 1和Data Register 0数据并解码 参考…

Page Cache难以回收产生之直接内存回收引起 load 飙高或者业务时延抖动

相信你在平时的工作中,应该会或多或少遇到过这些情形:系统很卡顿,敲命令响应非常慢;应用程序的 RT 变得很高,或者抖动得很厉害。在发生这些问题时,很有可能也伴随着系统 load 飙得很高。 据我观察&#xf…

以太网基础学习(二)——ARP协议

一、什么是MAC地址 MAC地址(英语:Media Access Control Address),直译为媒体访问控制位址,也称为局域网地址(LAN Address),MAC位址,以太网地址(Ethernet Addr…

小白自己​制作一个苹果.ios安卓.apk文件app应用手机下载的代码合并文件一码双端的落地页面详细教程

小白自己制作一个苹果.ios安卓.apk文件app应用手机下载的代码落地页面详细教程 图片取自这里哈 我们在这篇文章中教你如何制作一个手机下载引导落地页。这个落地页将可以自动识别访问者使用的是安卓还是苹果设备,并引导下载相应的应用程序。让我们按照以下步骤一…

Sui第五轮资助: 17个项目共获105万美元资助金

近日,Sui基金会宣布17个项目获得了105万美元的资助,用于建设项目以推动Sui的采用和发展。要获得资助,项目必须提交详细的提案,说明构建的项目、预算明细、关键里程碑、团队经验,以及对Sui社区的预期贡献。了解更多Sui资…

Linux环境下gdb调试方法与演示

个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【Linux专栏】🎈 本专栏旨在分享学习Linux的一点学习心得,欢迎大家在评论区讨论💌 演示环境&#xff1…

《2023年中国科技论文报告》解读:高校-浙大TOP1,企业-华为TOP1

最近小编看到中国科学技术信息研究所发布了《2023年中国科技论文统计报告》,里面有很多有趣的数据,摘取部分跟大家分享,更多详细内容,请点击文章底部“阅读原文”下载原文件。 第一点:在高水平国际期刊论文排名中&…

大语言模型之十五-预训练和监督微调中文LLama-2

这篇博客是继《大语言模型之十二 SentencePiece扩充LLama2中文词汇》、《大语言模型之十三 LLama2中文推理》和《大语言模型之十四-PEFT的LoRA》 前面博客演示了中文词汇的扩充以及给予LoRA方法的预训练模型参数合并,并没有给出LoRA模型参数是如何训练得出的。 本篇…