传输层重要协议之UDP协议和TCP协议详解

news2025/1/22 15:45:35

更多关于UDP协议和TCP协议请移步官网:https://www.rfc-editor.org/standards#IS

UDP标准协议文档-RFC 768

TCP标准协议文档-RFC 793

UDP协议详解

UDP协议的特点:

无连接、不可靠传输、面向数据报和全双工。

UDP协议报文结构:

关于端口号:

端口都是使用2个字节,16个bit位来表示,一个端口号的取值范围在0-65535之间。但是并不是所有的端口都可以被咱们程序猿所使用。我们自己写的程序绑定的端口一般是从1024开始的。0-1023这个范围的端口被称为“知名端口号”,即这些端口号是属于已经分配了一些知名的广泛使用的应用程序。1023以下的端口号一般是不建议使用。

关于UDP报文长度:

对于UDP来说,报头一共就是8个字节,分成四个部分(每个部分两个字节)。UDP报文长度也是用2个字节来表示的,2个字节的范围就是0-65535,换算成单位就是64kb,也就是说一个UDP数据报最大也就能够传输64kb的数据。

这个数值对于目前的传输要求来说显然是很小的,如果应用层数据报超过了64KB,对于UDP来说就需要在应用层通过代码的方式针对应用层数据报进行手动的分包,拆成多个包通过多个UDP数据报进行传输(本来需要send一次,现在需要send多次),或者换成TCP协议,因为TCP协议没有长度的限制。

关于校验和:

校验和的作用就是验证传输的数据是否是正确的。网络传输的本质就是光电信号,在传输过程中可能会受到一些干扰,就可能出现“比特翻转”的情况,即1->0 或 0 -> 1,就会造成数据改变。这样的现象是客观存在的,因此就引入了校验和来进行鉴别。

针对数据内容进行一系列的数学运算,得到一个比较短的结果(2个字节),当接收方收到这个数据之后,也会进行相同逻辑的数学运算,也会得到一个结果。如果两次校验和的结果是一样的,则证明传输的数据是正确的,反之,证明数据在传输过程中发生了改变。当然,在低端情况下,就算数据内容不一致,那么也会得到一个相同的校验和,那么这种是极小概率的,可以忽略不计。

TCP协议详解(10个核心机制)

TCP协议的特点:

有连接、可靠传输、面向字节流和全双工。

确认应答:

实现可靠传输的最核心机制。A给B发一个消息,B收到之后就会返回一个应答报文(ACK),此时A收到之后就知道刚才发送的信息已经顺利到达B了。

但是如果A给B发送了多条消息,网络传输中存在“先发后至”现象,那么B收到的信息的顺序也是错乱的,那么B返回A的应答报文到达的顺序也是可能发生变动的,这就有可能会因为顺序错乱而带来语义上的歧义。为了解决上述问题,给传输的数据和应答报文都进行编号。这样即使顺序错乱了,也可以根据序号来区分当前应答报文是针对哪个数据进行的了。

任何一条数据(包括应答报文)都是有序号的,确认序号是只有应答报文有,一条报文是否是应答报文,取决于ACK这个标志位是否是1。

TCP可靠传输能力,最主要就是通过确认应答机制来保证的。通过应答报文,就可以让发送方清楚的知道传输是否成功,进一步的引入了序号和确认序号,针对多组数据进行详细的区分。

超时重传

当发送的数据丢了或者返回的ack丢了,这种情况叫做丢包。但是对于发送方来说,就是没有收到ack,区分不了到底是哪一种情况造成的。因此,TCP就引入了重传机制,在丢包的时候重新再发一次同样的数据。TCP引入了一个时间阈值,发送方发了一个数据之后,就会等待ack,此时开始计时,如果超过了这个时间阈值还没有收到ack,就视为丢包,就重新传输。

但是会引入一个问题,由于重传,就会导致接受方同样的数据就会被收到多次,这是比较恐怖的,比如支付问题。但是,TCP对于这种情况是有处理的,TCP可以对重复数据去重。TCP存在一个“接受缓冲区”这样的存储空间(接收方操作系统内核里的一段内存),每个TCP的socket对象,都有一个接受缓冲区(其实也有一个发送缓冲区)。根据数据的序号,很容易识别当前接受缓冲区里面的重复数据,如果重复了,就丢弃后来的这份数据,保证了应用程序read读取到的数据一定是不重复的。

由于去重和重新排序机制的存在,发送方只要发现ACK没有按时到达,就会重传数据。即使重复了,即使顺序乱了,都没事,接收方都能很好的处理好(去重和排序都依赖TCP报头的序号)。

重传达到一定次数的时候,就不会再继续重传,会认为网路出现故障。接下来TCP会尝试重置连接(相当于断开重连一样),如果重置还是失败,彻底断开连接了。

小结:可靠传输是TCP最核心的部分。TCP的可靠传输就是通过确认应答+超时重传来进行体现的。其中确认应答描述的是传输顺利的情况。超时重传描述的是传输出现问题的情况。这两者相互配合,共同支撑整体的TCP可靠性。

连接管理

建立连接(三次握手)

建立连接就是通信双方各自要记录对方的信息,彼此之间要相互认同。

客户端主动给服务器发起的建立连接请求,称为"SYN",同步报文段。

所谓的三次握手,本质上是四次交互,通信双方,各自要向对方发起一个“建立连接”的请求,同时,再各自向对方回应一个ack。但是,中间两次交互,是可以合并成一次交互的,因此就构成了“三次握手"过程。中间两次的交互是必须合并的,因为封装分用两次一定比封装分用一次的成本更高。

三次握手的重要作用就是建立彼此之间的认同,验证通信双方各自的发送能力和接收能力是否正常,在一定程序上保证了TCP传输的可靠性。

如果是只有两次握手,则验证不了双方通信都是正常的。举个例子:

如果只有两次握手,那么站在男生的角度,知道了女神同意我请她吃饭。但是站在女神的角度,她不知道男生是否同意她请她吃饭。

在建立连接阶段,主要有两个状态:

LISTEN(listen):为服务器的状态,表示服务器已经准备就绪,随时可以有客户端来连接。

ESTABLISHED(established):客户端和服务器都有这个状态,表示连接建立完成,接下来就可以正常通信了。

断开连接(四次挥手)

四次挥手,和三次握手非常类似,都是通信双方各自向对方发起一个断开连接的请求,再各自给对方一个回应。

四次挥手中间的两次交互是不能合并的,之所以三次握手中间两次能够合并是因为,三次握手的中间两次能够合并是因为他俩是同一时机(SYN 和 ACK),具体来说,三次握手这三次交互过程,是纯内核中完成的(应用程序感知不到,也干预不了),服务器的系统内核收到syn之后,就会立即发送ack 也会立即发送syn。

对于四次挥手来说,FIN的发起,不是由内核控制的,而是由应用程序调用socket的close方法(或者进程退出)才会触发FIN。ACK则是由内核控制的,是收到FIN之后,立即返回ACK,而服务器的应用程序执行到对应的close方法,才会触发的FIN,这两者之间就会隔了一个时间差,具体时间差是多少取决于代码的写法。

所以,基于上述原因,FIN 和 ACK 并不完全是同一时机的,所以四次挥手中的中间两次是不能合并的

四次挥手涉及到两个重要的状态:

CLOSE_WAIT: 出现在被动发起断开连接的一方(建立连接一定是客户端主动发起请求,断开连接可能是客户端主动发起,也可能是服务器主动发起),等待关闭(等待调用close方法关闭socket)。

TIME_WAIT: 出现在主动发起断开连接的一方,假设是客户端主动发起断开连接,那么当客户端进入TIME_WAIT状态的时候,相当于四次挥手在客户端这边已经结束了,但是此时这里的TIME_WAIT要保持当前的TCP连接状态不要立即释放。TIME_WAIT保留一段时间的连接是因为,此时最后一个ack刚刚发出去,还没有到呢,防止ack丢包。

在三次握手和四次挥手过程中都是存在超时重传的。如果是最后一个ACK 丢包了,站在服务器的视角来看,服务器是不知道是因为ACK丢了,还是自己发的FIN 丢了,所有统一视为FIN丢了,统一进行重传操作。

既然服务器可能要重传FIN,客户端就需要能够针对这个重传的FIN进行ACK响应,很明显,如果刚才彻底把连接释放了,这样的ACK就无法进行了。因此使用TIME_WAIT状态保留一定的时间,就是为了能够处理最后一个ACK 丢包的情况,能够在收到重传的FIN之后,进行ACK响应。

那TIME_WAIT具体保持多长时间,就真正释放呢?

按照一般的经验来说,定义MSL为A传输数据到B所花费的时间(经验值),那么就约定TIME_WAIT保持2 MSL。

滑动窗口

可靠性和传输效率本身来说是矛盾的,在保证可靠性的基础上,来尽可能的提高传输效率,就引入了滑动窗口。

对于基本确认应答的情况来说,每次发一个数据,都需要等,等 ack到了再发下一个。滑动窗口的本质就是不等待的批量发送一组数据,然后使用一份时间来等待着一组数据的多个ACK 。把不需要等待,就能直接发送的数据的最大的量,称为"窗口大小"。

如果出现丢包的情况,就分为两种情况了:

第一种就是ack丢了,这种情况是不需要做任何处理的,关键在于有确认序号的存在,表示从该序号往前的所有数据都已经确认到达了。

第二种就是数据丢了:

由于1001-2000丢包了,接下来2001-3000到达主机B之后,B给A返回的ACK确认序号仍然是1001(此时和你发来的这个数据序号是啥,关系不大了),会一直索要1001开头的数据,直到1001开头的数据传输过来(“快速重传-超时重传在滑动窗口下的变形”),如果下面还有数据丢包的情况,那么还会继续索要下一个,直到没有数据丢包。

流量控制

流量控制是一种干预发送的窗口大小的机制。滑动窗口,窗口越大,传输效率就越高(一份时间,等的ack 就越多)。但是滑动窗口无限大也有以下问题:

1.完全不等待ack,可靠性可能得不得保障

2.窗口太大,也会消耗大量的系统资源

3.发送速度太快,接收方处理不过来

接收方的处理能力,就是一个很重要的约束依据,发送方发的速度,不能超出接收方的处理能力。流量控制要做的工作就是根据接收方的处理能力,协调发送方的发送速率。

流量控制的实现方式就是:

根据接收方接受缓冲区的剩余大小,然后把这个值通过ack报文返回给发送方,然后发送方就根据这个值来决定接下来发送的速率是多少(窗口的大小是多少)。

由于接收方缓冲区剩余空间是一直在动态变化的,所以每次返回ack 带的窗口大小都在变化,发送方也是在动态调整。

当窗口大小为0,发送方A就要暂停发送,暂停发送的等待过程中,会给主机B定期发送窗口探测报文。这个报文不携带具体的业务数据,只是为了触发ack查询窗口大小。

拥塞控制

流量控制和拥塞控制共同决定发送方的窗口大小是多少。流量控制考虑的是接收方的处理能力,而拥塞控制描述的是传输过程中中间节点的处理能力。

根据木桶效应,应该要找出处理能力最差的节点,因为发送方和接收方的处理能力可以被衡量,但是中间节点的处理能力不容易被衡量。把中间所有的节点看作是一个大的节点,通过实验的方式,逐渐找到了一个合适的窗口的大小。

拥塞窗口和流量控制的窗口,共同决定了发送方实际的发送窗口(拥塞窗口和流量控制窗口的较小值)

延时应答

延时应答也是提升效率的机制,也是在滑动窗口的基础之上再完善的一些机制。延时应答就是在收到数据之后,不是立即返回ACK了而是稍微等会再返回。等待的时间里,接收方的应用程序,就能够把接收缓冲区的数据给消费一些。

实际上延时应答采取的方式,就是在滑动窗口下,ack 不再每一条数据都返回了,而是比如隔1条或者多条返回一个ack。实际上剩余空间的大小变化是一个复杂的过程,既取决于发送方的发送也取决于接收方的处理。

捎带应答

也是提高效率的方式,在延时应答的基础之上,引入的捎带应答。服务器客户端程序,最典型的模型,就是"一问一答"。由于延时应答机制,就导致等待ACK的过程中,B就要给A发送业务数据了,就可以让业务数据捎上这个ACK一起发过去就行了。

本来返回ACK和发送业务数据是不同的时机,但是在延时应答下,可能成为相同时机,就合并了。

TCP三次握手,本身就是相同时机,所有三次握手是一定会合并的。但是此处引入了延时应答,就和四次挥手的合并比较像了,有一定概率会被合并。

粘包问题

因为TCP是面向字节流的,所有在接收缓冲区,其实是把收到多个数据都放到一起了,那么应用程序read读取的时候,读到哪里才算是一个完整的应用层数据报呢?这就导致一次读到的数据,可能是半个应用层数据报,可能是一个应用层数据报,也有可能是多个应用层数据报。

所以解决方案其实很简单,在应用层约定好应用层协议,尤其是明确应用层数据报和应用层数据报之间的边界。有两种方式:一是约定好分割符;二是约定每个包的长度。

异常情况

传输过程中出现了不可抗力的因素,比如进程奔溃、主机关机(正常流程关机)、主机掉电、网线断开等等。

对于进程奔溃和主机关机(正常流程关机),就是进程没了,对应的PCB就没了,对应的文件描述符表就释放了。相当于socket.close(),此时内核会继续完成四次挥手。此时其实仍然是一个正常断开的流程,主机关机要先杀进程,然后才正式关机(杀死进程的过程中,也就是和上面一样触发四次挥手)。

对于主机掉电和网线断开,显然是来不及四次挥手了。

假设是接收方掉电了,发送方仍然在继续发数据,发完数据要等待ack.ack ,如果等不到就会超时重传,但是再怎么重传,也收不到ack ,重传几次,还没有应答,尝试重置 tcp连接(复位报文段RST)。显然这个重置也会失败,然后单方面放弃连接了。

如果是发送方掉电,接收方发现没数据了。没数据是发送方挂了?还是发送方要组织下语言,稍等会再发?接收方完全不知道,只能先等。接收方需要周期性的给发送方发送一个消息,确认下对方是否还工作正常,也就是发送心跳包。

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

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

相关文章

Ubuntu系统为程序创建桌面快捷方式

为了不用每次都要进入命令行启动应用程序,为程序创建快捷方式是一个很方便的方法,尤其是你的程序需要在团队外部使用的时候。桌面创建快捷方式一般使用.desktop为后缀的文件实现,该文件的内容格式基本要素如下:[Desktop Entry] Na…

Git与IDEA强强联合(HTTPS协议连接)

最近在写项目的时候,在台式机和笔记本之间频繁切换,竟然还是用qq传压缩包,我自己都感觉无语,有git这样强大的版本管理工具,我竟然没想起来。然后也没有相关的博文就想来更新一篇。 那么如何使idea和git强强联合呢&…

果实可采摘点论文汇总

文章目录2019基于Mask R-CNN的芒果实例分割及采摘点检测研究与实现2021A mango picking vision algorithm on instance segmentation and key point detection from RGB images in an open orchard2022基于深度学习的多品种鲜食葡萄采摘点定位Method for Identifying Litchi Pi…

webpack(高级)--Prefetch和Preload shimming

webpack Prefetch和Preload webpack v4.6.0 增加了对预获取和预加载的支持 在声明import时 使用下面这些内置指令 来告知浏览器 prefetch(预获取)::将来某些导航下可能需要的资源 preload(预加载):当前导航下可能需要资源 import(/* webpackChunkName…

Leetcode详解JAVA版

目录1. 两数之和14. 最长公共前缀15. 三数之和18. 四数之和19. 删除链表的倒数第 N 个结点21. 合并两个有序链表28. 找出字符串中第一个匹配项的下标36. 有效的数独42. 接雨水43. 字符串相乘45. 跳跃游戏 II53. 最大子数组和54. 螺旋矩阵55. 跳跃游戏62. 不同路径70. 爬楼梯73.…

Jenkins连接Maven自动化部署构建SpringBoot

目录1.首先下载maven拉取到服务器2.解压maven并进入解压文件修改setting.xml2.拉取jdk18到服务器并解压3.将jdk还有maven移动到jenkins的挂载目录4.进入jenkins容器5.在jenkins的全局配置中配置jdk与maven6.jenkins安装插件 Publish Over SSH7.jenkins服务器创建一个jar包存放目…

ArcGIS API for JavaScript 4.15系列(6)——Dojo中的事件绑定

1、前言 在Web界面中,用户点击按钮、选择下拉选项、移动鼠标都涉及到dom元素的事件机制。下面就来介绍一下Dojo中的事件绑定操作。 2、dojo/on模块绑定事件 我们就从最简单的按钮click事件入手,Dojo中的dojo/on模块可以实现dom元素的事件绑定&#xf…

高性能办公娱乐迷你主机——Maxtang大唐AMD5600U

今天给大家介绍一款AMD5600U迷你主机,说起这款处理器大家应该并不陌生,像联想小新、YOGA以及ThinkBook等很多款用的都是这个型号,不过笔记本的价格基本都在3999-4999这个价位区间,同样的处理器,笔记本卖那么贵&#xf…

内网渗透(二十八)之Windows协议认证和密码抓取-Windows RDP凭证的抓取和密码破解

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

帮公司面试了一个30岁培训班出来的程序员,没啥工作经验...

首先,我说一句:培训出来的,优秀学员大有人在,我不希望因为带着培训的标签而无法达到用人单位和候选人的双向匹配,是非常遗憾的事情。 最近,在网上看到这样一个留言,引发了程序员这个圈子不少的…

Python程序设计-第5章Python面向对象

第5章Python面向对象一.预习笔记 1.类的相关概念 类的定义,类对象,实例对象,类属性 类属性是跟类绑定的,如果要修改类的属性就必须使用类对象访问,只使用实例对象是无法修改的。 权限访问:name与age是公…

【每日随笔】手指训练 ( 产品需求探索、技术无关 | 手指训练作用 | 哪些人需要手指训练 | 手指操 | 手指康复训练器材 )

文章目录一、手指训练作用二、哪些人需要手指训练三、手指操四、手指康复训练器材产品需求探索 , 研究下手指训练的市场 , 前景 , 是否可以开发 ; 一、手指训练作用 手指训练作用 : 改善 上肢协调性手眼 协调性训练提高 手指 抓握 能力提高 手指 灵活性提高 上肢运动 准确性 和…

Linux进程间通信(system V共享内存)

共享内存原理 看上面这张图,其实只要是进程间通信都离不开让他们看到同一块资源(内存),其实共享内存这里和动态库那里一样,都是要加载到共享区,共享内存提供者,是操作系统,操作系统要不要管理共享内存&…

Python环境搭建指南

Python能做太多有趣使用的事了,不仅可以做现在火热的人工智能、数据分析,还可以做爬虫、Web开发、自动化运维的事情。 随着Python为我们工作与生活带来更多的便捷后,很多人开始学习Python,关注Python的发展前景、薪资和职业素养的…

RabbitMQ——高级特性(SpringBoot实现)

本篇文章的内容与我之前如下这篇文章一样,只是使用技术不同,本篇文章使用SpringBoot实现RabbitMQ的高级特性! RabbitMQ——高级特性_小曹爱编程!的博客-CSDN博客RabbitMQ——高级特性:1、RabbitMQ高级特性;…

IDEA2021.3新建一个maven-archetype-webapp项目,发现在新建文件汇总,没有新建servlet文件选项

问题 我使用maven-webapp 模板新建了一个web项目&#xff0c;但是在新建文件的时候&#xff0c;发现没有servlet选项 解决 第一种&#xff1a;在pom中直接配置 <dependencies><dependency><groupId>javax.servlet</groupId><artifactId>java…

Linux进程信号产生以及捕捉

一.什么是信号 生活中&#xff0c;有哪些信号相关的场景呢&#xff0c;比如&#xff1a;红绿灯&#xff0c;闹钟&#xff0c;转向灯等等 1.这里我们要知道&#xff0c;你为什么认识这些信号呢&#xff0c;记住了对应场景下的信号&#xff0b;后续是有”动作“要你执行的 2.我们…

spring boot 项目打包镜像方式以及区分环境打包

springboot项目打包成docker镜像-贾玉珍-2023年2月8日方法一&#xff1a;将项目jar包手动推送到docker服务器上1.用maven对项目进行打包&#xff0c;打包成功后会有一个target目录&#xff0c;目录下有打好的项目jar包2.将jar包上传到服务器上&#xff0c;我用的是finalshell工…

Zabbix Agent item监控项讲解

前言 agent与snmp是Zabbix两种重要的监控方式&#xff0c;这一期主要介绍Zabbix Agent item监控项。。Zabbix agent分为主动代理、被动代理&#xff0c;配置item类型时&#xff0c;可以选择需要的类型&#xff1a; Zabbix agent &#xff1a;用于被动检查 Zabbix agent&…

【C语言进阶】结构体、位段、枚举和联合

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前是C语言学习者 ✈️专栏&#xff1a;C语言航路 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章对你有帮助的话 欢迎 评论&#x1f4ac; 点赞&a…