传输层TCP协议

一、TCP协议格式

我们看到报头固定有20字节,最后选项大小不固定。

4位首部长度(二进制0000 ~ 1111,十进制范围[0, 15])单位是4字节(存放字节大小范围[0, 60])包括了20字节固定长度 +选项长度。若选项大小为0,四位首部长度是5,即0101,用于解包。

二、TCP各种机制

1、确认应答机制

保证双方请求发送可靠性。

发送方和接收方不一定就是 client 和 server ,身份可以互换,上图举例。

只要 client 收到应答,就能保证上次 client 发送的请求 server 一定接收到了,这是保证了从 client 到 server 的数据发送可靠性。但是我们不能对应答再次应答来保证从 server 到 client 数据发送的可靠性,因为这样会死循环,所以一般不对应答进行处理。

可靠性:不是TCP协议保证每一个请求都能让发送方收到应答,而是能确认接收方是否得到了请求,如果得到发送方收到应答,没有就是发送方没收到应答。

TCP通信模式

首先由于应用层会有发送缓冲区和接收缓冲区来进行通信,Linux中把缓冲区以字节为单位逻辑上看成是数组,数字就是数组的下标,序号与确认序号就是这样的缓冲区中的数组下标。

2、超时重传机制

由确认应答机制可以知道,我收到了 ACK 就说明对方一定收到了数据,经过一段时间我没有收到 ACK,TCP协议规定我认为对方没有收到数据,我要补发数据。

没有收到 ACK 两种情况:

那么经过多长时间进行数据补发呢?与网络相关,Linux 中第一次超时0.5s,第二次超时1s,第三次超时2s,第一次超时4s...重复一定次数还没有收到就关闭不传了。

3、连接管理机制

正常情况下,三次握手建立连接,四次挥手断开连接。

(1)三次握手建立连接

标记位 SYN 表示申请建立连接

标记为 RST 表示要再次建立连接

我们看到最后一次服务器没有对 ACK 进行应答,实际上就算连接在最后一步出现问题,导致没有连接成功,客户端在发起 ACK 之后就认为连接已经建立,此时对服务器发送请求时,服务器发现没有连接成功就会返回 RST,客户端看到后就知道没有建立连接,重新三次握手建立连接。

(2)四次挥手断开连接

第一次挥手表示客户端向服务器发送的数据已经发完,之后客户端不会主动发消息给服务器,但由于没有第二次挥手,服务器还可以继续给客户端发消息,客户端也必须 ACK 应答,但应用层已经关闭,只能发 ACK 应答报头。

所以允许客户端还要收到服务器的消息就只能关闭套接字的写端。

用函数 int shutdown(int sockfd, int how) how中的选项 SHUT_WR 关闭写端。

(3)建立连接为什么要三次握手?

网络中有许多连接,为了管理连接就要先描述再组织,连接就是一个结构体,既要有空间存储,也要有时间来进行属性更新。所以三次握手也是在维护双方的连接结构体。

而且三次握手要有三次才能建立连接,对抗 SYN 洪水效果好。

原因:

a、验证全双工:即保证双方网络连通性,用最小的次数验证。

b、建立双方通信共识:客户端发送 SYN 说明客户端想连接服务器,服务器也发 SYN 说明服务器想连接客户端。

c、协商双方接受能力,在 ACK 应答报头中会带有16位窗口大小,这就是说当前接受缓冲区剩余量,用于为下一次数据发送进行发送量的确定。

(4)四次挥手中状态含义

服务器 CLOSE_WAIT 状态:客户端已经退出,服务器知晓,但是还不能立即关闭自己的 connfd

客户端 TIME_WAIT 状态:TCP规定,处于 TIME_WAIT 状态下在 2 * MSL(maximum segment lifetime 报文最大生存时间) 时间内不能再次监听同一个端口。

主动断开连接的一方最终会进入 TIME_WAIT 状态。

那如何不用等就可以重新监听端口呢?

int setsockopt(int sockfd, int level, int optname, void* optval, socklen_t* optlen)

level:所在层,SOL_SOCKET

optname:选项名字,SO_REUSEADDR,重新使用地址

optval:选项值,int opt = 1

optlen:选项大小,sizeof opt

函数作用是允许创建端口号相同,但IP地址不同的 socket 描述符,不用等就可以连接相同端口。

为什么要等 2MSL 时间,要有 TIME_WAIT ?

a、保证两个朝向尚未接受或迟到的报文在 2MSL 时间内被接受后丢弃(因为已经是超时报文)如果没有 TIME_WAIT,立即建立相同的连接,就会接收到之前的旧数据,导致数据错误。

b、同时也保证四次挥手最后的 ACK 服务器能就收到,要是没有 TIME_WAIT,客户端立即退出,ACK 传递出错,服务器再次向客户端发送 FIN 时客户端退出就会导致错误。

(5)连接管理总流程

4、流量控制

发送方会根据接收方的接收缓冲区剩余空间来动态调整发送速度。

所以接收方可以在 ACK 应答中的16窗口大小填写缓冲区剩余量来告知发送方。

窗口探测:主机A给主机B发空报头,B必须 ACK 应答带上窗口大小,主机A就能了解情况。

窗口更新通知:主机B终于能有空间接收请求时,会给主机A发消息,提醒他可以发数据了。

标记位 URG:紧急指针标记位(0无效,1有效)数据中有紧急数据,16位紧急指针就会标识紧急数据在数据中的偏移量。因为TCP协议中紧急数据大小1字节,所以只有偏移量没有大小。举例:0代表正常,1代表暂停,2代表取消...读取紧急数据知晓方法对应上层对于数据的处理方法。

recv 和 send 函数里面的 flag 标记设成 MSG_OOB 就可以对紧急数据设置

5、滑动窗口

问题

流量控制:具体怎么控制发送数据大小?

超时重传:超时时间内,已发送的数据不能丢弃,保存在哪里?

发送方规定了一个滑动窗口,在窗口内的数据可以直接发送,暂时不用收到应答。

根据感性的理解,滑动窗口是可以变大变小变成0,只能向右移。

构建模型:

缓冲区在Linux里面逻辑上看成是字节为单位的数组,则滑动窗口就是用双指针(win_start , win_end)维护的内存空间。

接收到 ACK 应答之后,win_start = 确认序号,win_end = win_start + 16位窗口大小。

考虑一个问题:如果数据丢包,滑动窗口如何移动?

建立模型


1、最左侧报文丢失

即2000的报文丢失,此时传来的 ACK 确认序号只能是1001,由于我们一次发送多条数据,应答回来的都是1001,超过3次重复应答,发送方就会意识到1001~2000的报文已经丢失,再次补发,下一次 ACK 确认序号就直接是5001,这就是快重传机制。

上述情况是报文丢了,但如果应答丢了呢?没关系,只要相信最新一次应答的确认序号就能确定下一次报文从哪里开始发送(由确认序号定义得知)

所以最左侧报文丢失移动策略:
a、由于确认序号规定的约束,滑动窗口左边界不动。

b、快重传和超时重传对左侧报文补发。

快重传 vs 超时重传

快重传要连续3次相同确认序号才能触发(保证效率),但是在极端情况下超时重传一定能识别到报文丢失(保证安全)

2、中间报文丢失

a、win_start = 确认序号,此时新滑动窗口就是最左侧报文丢失

b、由于确认序号规定的约束,滑动窗口左边界不动。

c、快重传和超时重传对左侧报文补发。

3、最右侧报文丢失

a、win_start = 确认序号,此时新滑动窗口就是最左侧报文丢失

b、由于确认序号规定的约束,滑动窗口左边界不动。

c、快重传和超时重传对左侧报文补发。

所以最后我们来回答最开始的两个问题。

流量控制:具体怎么控制发送数据大小?滑动窗口机制

超时重传:超时时间内,已发送的数据不能丢弃,保存在哪里?滑动窗口里面,出问题转化成最左侧报文丢失问题即可解决。

6、拥塞控制

(1)介绍网络拥塞

由于网络拥堵导致发送报文大面积的丢包,TCP会采用慢启动机制,先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据。

TCP解决网络拥塞的问题,最大的价值在于:在多个使用同一网络进行通信的主机有拥塞避免的共识。即TCP协议是全体主机都要遵守的,在网络拥塞的情况下,不能只从一台主机考虑,全体主机都必须减少报文发送才能缓解网络拥塞。

具体的拥塞控制:发送方的滑动窗口 = min(接收方的应答窗口,拥塞窗口)

(2)拥塞窗口

网络状况是浮动的,就说明拥塞窗口的大小肯定也是动态变化的。所以要经过多轮尝试才能得出准确的拥塞窗口大小。

慢启动机制

但是如果只是单纯向上面一样指数级增长就会导致测出来的拥塞窗口不准确,所以慢启动之后我们要让拥塞窗口线性增长来保证准确。

加法增大:达到阈值之后只能线性增长来探测到准确的网络拥塞值。

乘法减少:上一次的网络拥塞值的一半就是下一次慢启动的阈值。

但是不会无限线性增大下去,一定会有拥塞窗口极限值限制线性增长。

7、延迟应答

收到一批报文之后不会立即应答,等上层处理完一些请求之后能空出更多的接收窗口了,再 ACK 应答告知对方有更大的接收窗口,可以在网络不拥堵时保证传输效率。

但是延迟应答也有限制。

数量限制 : 每隔 N 个包就应答一次。
时间限制 : 超过最大延迟时间就应答一次。
具体的数量和超时时间 , 依操作系统不同也有差异。   一般 N 2, 超时时间取 200ms。

8、捎带应答

发送数据时带上上一次的应答数据

三、理解TCP面向字节流

创建一个 TCP socket, 同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区。
流程:
调用 write , 数据会先写入发送缓冲区中。
如果发送的字节数太长, 会被拆分成多个 TCP 的数据包发出。
如果发送的字节数太短, 就会先在缓冲区里等待 , 等到缓冲区长度差不多了 , 或者其他合适的时机发送出去。
接收数据的时候, 数据也是从网卡驱动程序到达内核的接收缓冲区 ;
然后应用程序可以调用 read 从接收缓冲区拿数据。
全双工: TCP 的一个连接 , 既有发送缓冲区 , 也有接收缓冲区 , 那么对于这一个连接, 既可以读数据 , 也可以写数据。
面向字节流特点:
由于缓冲区的存在, TCP 程序的读和写不需要一一匹配
例如: 写 100 个字节数据时, 可以调用一次 write 100 个字节 , 也可以调用 100 次write, 每次写一个字节。读 100 个字节数据时 , 也完全不需要考虑写的时候是怎么写的 , 既可以一次
read 100 个字节 , 也可以一次 read 一个字节 , 重复 100 次。

四、TCP粘包问题

1、介绍粘包问题

首先要明确 , 粘包问题中的 " " , 是指的应用层的数据包。
在 TCP 的协议头中 , 没有如同 UDP 一样的 " 报文长度 " 这样的字段 , 但是有一个序号这样的字段。 序号只保证数据到应用层的有序性。
站在传输层的角度, TCP 是一个一个报文过来的。   按照序号排好序放在缓冲区中。
站在应用层的角度, 看到的只是一串连续的字节数据。
那么应用程序看到了这么一连串的字节数据, 就不知道从哪个部分开始到哪个部分, 是一个完整的应用层数据包。

2、解决粘包问题

(1)对于定长的包 , 保证每次都按固定大小读取即可。 例如上面的 Request 结构 , 是固定大小的, 那么就从缓冲区从头开始按 sizeof(Request) 依次读取即可。
(2)对于变长的包, 可以在包头的位置 , 约定一个包总长度的字段 , 从而就知道了包的结束位置。
(3)对于变长的包, 还可以在包和包之间使用明确的分隔符 ( 应用层协议 , 是程序员自己来定的, 只要保证分隔符不和正文冲突即可)

3、UDP不用考虑粘包问题

对于 UDP, 如果还没有上层交付数据 , UDP 的报文长度仍然在 . 同时 , UDP 是一个一个把数据交付给应用层. 就有很明确的数据边界。
站在应用层的站在应用层的角度, 使用 UDP 的时候 , 要么收到完整的 UDP 报文, 要么不收 . 不会出现 " 半个 " 的情况。面向数据报。

五、TCP异常

进程终止 : 进程终止会释放文件描述符 , 仍然可以发送 FIN,依然可以四次挥手
机器重启 : 和进程终止的情况相同。
机器掉电 / 网线断开 : 接收端认为连接还在 , 一旦接收端有写入操作 , 接收端发现连接已经不在了, 就会进行 reset. 即使没有写入操作 , TCP 自己也内置了一个保活定时器 , 会定期询问对方是否还在. 如果对方不在 , 也会把连接释放。
另外, 应用层的某些协议, 也有一些这样的检测机制. 例如 HTTP 长连接中, 也会定期检测对方的状态. 例如 QQ, 在 QQ 断线之后, 也会定期尝试重新连接.

六、TCP小结

可靠性: 校验和,序列号(按序到达) ,确认应答机制,超时重传机制,连接管理机制,流量控制机制,拥塞控制机制

提高性能: 滑动窗口,快速重传机制, 延迟应答机制,捎带应答机制

七、TCP / UDP对比

我们说了 TCP 是可靠连接 , 那么是不是 TCP 一定就优于 UDP ? TCP UDP 之间的优点和缺点, 不能简单 , 绝对的进行比较。
TCP 用于可靠传输的情况 , 应用于文件传输 , 重要状态更新等场景。
UDP 用于对高速传输和实时性要求较高的通信领域 , 例如 , 早期的 QQ, 视频传输等. 另外 UDP 可以用于广播。
归根结底 , TCP UDP 都是程序员的工具 , 什么时机用 , 具体怎么用 , 还是要根据具体的需求场景去判定。

八、用UDP实现可靠性传输

1、引入超时重传机制,防止丢包

2、引入确认应答机制,保证对方收到数据

3、引入序号,保证数据接收顺序

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

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

相关文章

PWA(Progressive web APPs,渐进式 Web 应用): manifest.json、 Service Worker

文章目录 引言I 什么是 PWA功能特性技术上分为三个部分:II Web 应用清单将Web 应用清单文件链接到站点manifest.json字段说明III Service WorkerService worker 本质Service worker 运行在 worker 上下文注册服务辅助角色扩展知识将 PWA 作为脱机应用定义当前文档与被链接文档…

用Python实现运筹学——Day 4: 线性规划的几何表示

一、学习内容 线性规划的几何表示: 线性规划问题的解通常位于一个凸多边形(即可行解空间)的顶点上,这意味着在求解线性规划问题时,只需要找到可行解空间中的顶点并计算出目标函数值,再选择其中的最优解。 可…

C++之分割字符串的两种方式

方式一 #include <string> #include <vector> #include <sstream> #include <iostream>std::vector<std::string> split(const std::string& str, char delim) {std::stringstream ss(str);std::string item;std::vector<std::string>…

C语言贪吃蛇小游戏演示和说明

C语言贪吃蛇小游戏演示和说明 设计贪吃蛇游戏的主要目的是让大家夯实C语言基础&#xff0c;训练编程思维&#xff0c;培养解决问题的思路&#xff0c;领略多姿多彩的C语言。 游戏开始后&#xff0c;会在中间位置出现一条只有三个节点的贪吃蛇&#xff0c;并随机出现一个食物&am…

keepalived+lvs集群,实现高可用

环境准备&#xff1a;两台虚拟机&#xff0c;关闭防火墙&#xff0c;selinux,配置阿里云仓库&#xff0c;配置epel 192.168.88.21 dr1 负载均衡器 master 192.168.88.22 dr2 负载均衡器 backup 192.168.88.23 rs1 web1 192.168.88.24 rs2 web2 实验说明&…

项目启动错误

说明&#xff1a;记录一次项目启动&#xff0c;报数据库访问错误&#xff0c;如下&#xff1a; 错误信息&#xff1a;Invalid default&#xff1a;public abstract java.lang.Class tk.mybatis.spring.annotation.MapperScan.fatoryBean() 解决 没有引入mybatis依赖&#xff…

通信工程学习:什么是VIM虚拟化基础设施管理器

VIM:虚拟化基础设施管理器 VIM(Virtualized Infrastructure Manager)虚拟化基础设施管理器,是一种负责管理和控制虚拟化环境中所有虚拟资源的工具和系统。以下是关于VIM虚拟化基础设施管理器的详细解释: 一、定义与功能 VIM是网络功能虚拟化(NFV)架构中…

HarmonyOS---权限和http/Axios网络请求

网络请求(http,axios) 目录 一、应用权限管理1.1权限的等级1.2授权方式1.3声明权限的配置1.4如何向用户进行申请 二、内置http请求使用三、Axios请求使用&#xff08;建议&#xff09;3.1 使用方式一3.2 使用方式二&#xff08;建议&#xff09; 一、应用权限管理 应用权限保护…

Leetcode面试经典150题-322.零钱兑换

给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。 你可以认为每种硬币的数量是无限的。 示…

uniapp 常用高度状态栏,导航栏,tab栏,底部安全高度

实际效果 使用 //使用 let posConfig this.getPosConfig(); // 传false返回值为 px大小 console.log(posConfig.safeBottomH) // 入参 是否转换为rpxgetPosConfig(toRpx true) {const systemInfo uni.getSystemInfoSync();// #ifdef MPconst menuButtonInfo uni.getMenuBu…

基于RPA+BERT的文档辅助“悦读”系统 | OPENAIGC开发者大赛高校组AI创作力奖

在第二届拯救者杯OPENAIGC开发者大赛中&#xff0c;涌现出一批技术突出、创意卓越的作品。为了让这些优秀项目被更多人看到&#xff0c;我们特意开设了优秀作品报道专栏&#xff0c;旨在展示其独特之处和开发者的精彩故事。 无论您是技术专家还是爱好者&#xff0c;希望能带给…

Linux云计算 |【第四阶段】NOSQL-DAY2

主要内容&#xff1a; Redis集群概述、部署Redis集群&#xff08;配置manage管理集群主机、创建集群、访问集群、添加节点、移除节点&#xff09; 一、Redis集群概述 1、集群概述 所谓集群&#xff0c;就是通过添加服务器的数量&#xff0c;提供相同的服务&#xff0c;从而让…

计算机毕业设计之:微信小程序的校园闲置物品交易平台(源码+文档+讲解)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

卷积神经网络-迁移学习

文章目录 一、迁移学习1.定义与性质2.步骤 二、Batch Normalization&#xff08;批次归一化&#xff09;三、ResNet网络1.核心思想2.残差结构&#xff08;1&#xff09;残差块&#xff08;2&#xff09;残差结构类型 四、总结 一、迁移学习 迁移学习&#xff08;Transfer Lear…

书生大模型实战营学习[9] OpenCompass 评测 InternLM-1.8B 实践

准备工作 打开开发机&#xff0c;选择cuda11.7环境&#xff0c;A100选择10%&#xff0c;点击创建&#xff0c;然后进入开发机即可&#xff0c;和之前的操作一样。接下来创建环境&#xff0c;下载必要的依赖包 conda create -n opencompass python3.10 conda install pytorch2…

Altium Designer(AD)百度云下载与安装(附安装步骤)

在我们日常使用当中&#xff0c;Altium designer常常也被简称为AD&#xff0c;是一款一体化的电子产品开发系统软件&#xff0c;主要运行在Windows操作系统上。 我们通过Altium designer把原理图设计、电路仿真、PCB绘制编辑、拓扑逻辑自动布线、信号完整性分析和设计输出等技…

Eclipse Memory Analyzer (MAT)提示No java virtual machine was found ...解决办法

1&#xff0c;下载mat后安装&#xff0c;打开时提示 jdk版本低&#xff0c;需要升级到jdk17及以上版本&#xff0c;无奈就下载了jdk17&#xff0c;结果安装后提示没有jre环境&#xff0c;然后手动生成jre目录&#xff0c;命令如下&#xff1a; 进入jdk17目录&#xff1a;执行&…

鸿蒙界面开发(九):列表布局 (List)

列表布局 当列表项达到一定数量&#xff0c;内容超过屏幕大小时&#xff0c;可以自动提供滚动功能。它适合用于呈现同类数据类型或数据类型集&#xff0c;例如图片和文本。在列表中显示数据集合是许多应用程序中的常见要求&#xff08;如通讯录、音乐列表、购物清单等&#xf…

Uniapp 微信小程序 最新 获取用户头像 和 昵称 方法 有效可用

文章目录 前言代码实现运行效果技术分析 前言 同事有个需求 授权获取用户头像 和 昵称 。之前做过线上小程序发版上线流程 就实现了下 最新的方法和 api 有些变化 记录下 代码实现 先直接上代码 <template><view class"container"><buttonclass&qu…

解决macOS安装redis以后不支持远程链接的问题

参考文档:https://blog.csdn.net/qq_37703224/article/details/142542179?spm1001.2014.3001.5501 安装的时候有个提示, 使用指定配置启动: /opt/homebrew/opt/redis/bin/redis-server /opt/homebrew/etc/redis.conf那么我们可以尝试修改这个配置文件: code /opt/homebrew/…