文章目录
前言
再上一篇文章中 介绍了关于网络编程中应用层的一些基本知识,
本文会着重讲解传输层中Tcp和Udp两种协议
一、端口号
在网络通信中,我们以“源IP”,“源端口号”,“目的IP”,“目的端口号”,“协议号”这样的一个五元组来标识一个通信。
端口号的范围划分
端口号的范围是 0~65535,但在这其中还被划分为知名端口号和普通端口号
- 0~1024: 知名端口号,HTTP,FTP,SSH等一些广为使用的应用层协议,他们的端口号是固定的。
- 1024 - 65535: 操作系统动态分配的端⼝号. 客⼾端程序的端⼝号, 就是由操作系统从这个范围分配的.
谈到端口号,就要说说与它捆绑使用的进程了。
端口号是有限的,那么是否可以重复利用呢?
- 一个进程是否能绑定多个端口号呢?
- 一个端口号是否能被多个进程所绑定呢?
回答一下
一个进程可以绑定多个端口号
一个端口号不能被多个进程所绑定
我们把进程想象成房间,把端口号想象成进入房间的门,是不是就可以具象化的理解了。
到了具体的企业开发中,是这样使用的。
就拿游戏来说
打开游戏,此时游戏就是一个进程,这个进程提供了两个端口号,一个供玩家进入,一个共开发人员进入。
游戏玩家进入游戏,可以享受正常的对局,但是对于游戏内的参数设置以及充值系统不能更改。
而开发人员进入游戏,可以设置对应的游戏参数,调整地图,优化系统,更爱游戏内氪金点数。
二、UDP和TCP
TCP (Transmission Control Protocol) 和 UDP (User Datagram Protocol) 是两种不同的网络传输协议。
2.1 UDP
UDP协议格式
源端口号和目的端口号,标明了这个数据报从哪里来,要到哪里去。
UDP长度: UDP数据报能传输64KB大小的数据
UDP长度在整个UDP数据报中占2个字节,也就是16位,能表示的范围就是0~65535.单位是字节 1024*64=65536。虽然UDP数据报的报头还有8个字节,64KB和64KB-8在实际的开发中,是忽略不计的,只要要传输的数据接近于64KB时,就需要注意了。UDP校验和: 使用CRC的方式来完成。大致就是通过固定的公式来对要传输数据进行计算得到一个结果,发送方和接收方对两个结果进行比较,如果相等就说明传输的数据没有问题。感兴趣的可以去网上搜一搜相关的介绍,这里不就展开介绍了。
在数字电路中,电平通常用两个状态来表示,分别是高电平和低电平。 高电平表示逻辑1,低电平表示逻辑0。也就是计算机内部的那一串串的二进制数据,当受到电磁波、电信号、光信号的干扰时,可能就会发送比特翻转,造成数据传输的错误。
2.2 UDP的特点
UDP传输的过程类似于寄信.
- ⽆连接: 知道对端的IP和端⼝号就直接进⾏传输, 不需要建⽴连接;
比特就业课 - 不可靠: 没有确认机制, 没有重传机制; 如果因为⽹络故障该段⽆法发到对⽅, UDP协议层也不会给应
⽤层返回任何错误信息; - ⾯向数据报: 不能够灵活的控制读写数据的次数和数量;
UDP的主要用途
应用于对性能要求高,但是对可靠性要求不高的场景
三、TCP
TCP是一种面向连接的协议,它在传输数据之前会建立一条专用的通信连接。这意味着在数据传输过程中,两台计算机之间会有一条稳定的数据传输通道。因此,TCP可以保证数据传输的可靠性,但会带来一定的延迟
TCP协议格式
- 16位源端口号和16位目的端口号: 表⽰数据是从哪个进程来, 到哪个进程去;
- 32位序号: 发送数据每个字节都有一个序号,这个在后续的TCP确认应答机制中会看到实例
- 32位确认序号: 接收方会给出一个确认序号,这个在后续的TCP确认应答机制中会看到实例
- 4位首部长度: 这个字段是表示TCP数据报的报头有多大。
4位能表示 0~15,它的单位是‘4个字节’,也就是能表示最大60个字节,也就是表示TCP数据报的报头最大可以是60个字节,在报头部分我们可清楚的看到字段所占据的比特位,只有选项和数据是没有给出大小的。选项包含在报头部分,也就说选项这一部分最多可以表达40个字节大小的数据。
TCP数据报整个报长在协议中没有规定 - 保留(6位): 这个部分是留白,里面没有任何东西,是设计师预留出的给未来可能出现的功能的位置,
- 6个标志位:
- URG(Urgent): 紧急指针是否有效
- ACK(Acknowledgment): 确认号是否有效。*
- PSH(Push): 提⽰接收端应⽤程序⽴刻从TCP缓冲区把数据读⾛
- RST(Reset): 对⽅要求重新建⽴连接; 我们把携带RST标识的称为复位报⽂段
- SYN(Synchronization): 请求建⽴连接; 我们把携带SYN标识的称为同步报⽂段。
- FIN(Finish): 通知对⽅, 本端要关闭了, 我们称携带FIN标识的为结束报⽂段
- 16位检验和: 发送端填充, CRC校验. 接收端校验不通过, 则认为数据有问题. 此处的检验和不光包含
TCP⾸部, 也包含TCP数据部分. - 16位紧急指针: 标识哪部分数据是紧急数据;
- 选项: Optional(可选项),可以自定义的勾选某些功能
四、TCP的十个核心机制
TCP最核心的机制就是可靠传输
而实现可靠传输就时依靠确认应答机制来实现的,其中超时重传是对确认应答的重要补充。
1.确认应答机制
对于用于应答的数据,称为应答报文
对于序号的解释:
TCP会将每个字节的数据都进行编号,即位序列号
例如:
发送端,发送一个TCP数据报,载荷中的字节TCP会对其进行编号,当接收端接收到数据报后,会获取到数据报中的序号,进行一系列的业务逻辑后,对发送端数据报的序号+1,作为确认序号,并将ACK的值设为1,返回给发送端。
返回的确认序号有两层含义
- 告诉发送端这个序列号之前的数据我已经全部接收了
- 请发送端下次发送数据时,从这个序号列开始发送
2.超时重传
在传输过程中出现丢包这种情况,
主机A将数据发送给主机B后,等待主机B的确认应答
当一段时间后还没有收到主机B的确认应答消息,此时主机A就会重新发送。
主机A是如何判断是否收到确认应答的呢?
主机A是通过是否收到了“ACK“来判断是否收到了应答。
如果一段时间内没有收到ACK,那么就认为是发生了丢包,就会重新发送。
那么问题又来了,ACK有没有可能会丢失呢?
答案是 会
如上就发生了丢失ACK的现象。
此时,主机B就收到了两份相同的数据,这样的情况肯定是不能存在的,在你游戏充值界面,你充值了50元,而由于服务器卡顿,给你的账号增加了100快价值的游戏币,肯定游戏厂商不会允许这样的情况发生的。
TCP接收方这边会对接收的数据按照序列号来进行去重。
在TCP重复传输\重复接收是无所谓的,但会保证在应用层不会读取到重复的数据。
那么超时重传的时间间隔是多少呢?
这个时间不是一个固定值,是会随着重传次数的增加而变长,
当时间达到一定阈值,就会重置连接。触发一个”复位报文“来进行尝试重连
但是当网络出现严重的故障时,RST也无法触发重置
只能断开连接(通信双方清除对方的数据 例如端口号 IP地址等)
总结一下
通信是如何实现可靠传输的?
在发送方发送数据时,会对字节进行编号,这个编号连续自增,TCP报头中的序号只存储第一个字节的序号,接收方在接收到全部数据后,会按照接收到的序号+1生成确认序列,这里的确认序号有两个作用,一是说明这个序号之前的数据都已经收到,二是接下来应该发送从确认序号开始的数据,并将报头中的ACK标识符的bit位更改1,生成应答报文返回给发送方。通信中丢包怎么办?
TCP拥有超时重传这样的机制,当一段时间后,发送方没有接收到来自于接收端的应答报文时,会重新传输数据报。这里的时间间隔怎么设置?
每次的间隔时间会变长,当到达一定阈值后,就会尝试重置连接重置连接细节
此时会将报头中的RST标志位的bit位改为1,来进行重置连接。
此时 发送方和接收方就会清除互相的信息(端口号、IP地址等)通信中出现重复的数据怎么办?
TCP拥有一个接收缓冲区(类似于带有优先级的阻塞队列),当数据到达时,就会进入缓冲区,当应用程序进行read时,就会将数据从缓冲区删除,那么TCP接收会在缓冲区按照序列号来对数据进行去重。为什么会出现重复的数据?
因为发送方无法分辨是数据丢了,还是ACK丢了,如果是ACK丢了,就会收到重复的数据。
3.连接管理–三次握手 四次挥手
建立连接:三次握手
断开连接:四次挥手
握手:发送一个不含业务数据的数据报,不起到任何业务的作用,只是建立连接。
三次握手
在上述过程中,服务端和客户端各自给对方发送一个SYN,再各自给对方返回一个ACK,实际上是四次交互。
其中,中间的ACK和SYN可以合并成一个数据报(这里是由内核完成的,当收到客户端的ACK同步报文后,立即生成ACK和SYN,这两是同一时刻由内核控制完成的
ACK的bit为1,SYN的bit为1
可以缩减为两次握手吗?
不可以,服务器对于通信双方的接收能力和发送能力的验证没能完成。
那么四次交互不是更直观吗?
这是因为三次的效率比四次要高。
在数据的传输过程中,要经过层层的封装和分用,多一次而带来的消耗就会很大。
三次握手的意义
- 在数据传输前,先确保通信链路是否通畅
- 通过三次握手,来确认通信双方的接收和发送功能都是正常的。
- 还需要协商一些必要的参数。有些参数不是单方面可以确定的,需要双方商量来决定。
三次握手协商
TCP通信时使用的序号,就是通过三次握手协商出来的。
第一次连接和第二次连接,协商出来的起始序号,往往差别很大。
这是为了避免当一个数据报在网络中传输时,可能由于线路规划不明确,导致在网络中一直游转,当通信双方断开连接时生成新的连接时(上一个业务结束,执行下一个业务),此时这个数据报才送到接收端,这时接收端就会根据序号的差异(检查序号时候如果差异过大,就会将这个数据报直接丢弃掉),从而防止上一次业务的逻辑运行到这次的业务中。
四次挥手
客户端和服务端都可以先发起FIN(结束报文)
这里我们假定客户端先发起。
和三次握手是不是有点相似,那为什么三次握手就可以合并两次操作,而四次挥手不可以呢?
四次挥手的FIN和ACK报文可以合并吗?
在特殊情况下是可以的,但一般来说是不可以的。
- 一般情况
当收到FIN报文时,内核直接就返回ACK报文,而FIN报文是由应用程序来控制的(socket.close),不是同一时刻,所有不能合并。
三次握手是因为SYN和ACK都是由内核控制的,所以可以合并。 - 特殊情况
TCP有一个延时应答机制,在回复ACK数据报时,不是立即回复,而是等待一段时间,这时ACK和FIN就可以合并成一个报文了。
在服务器看到大量的CLOSE_WAIT,可能是什么原因?
- close未调用
- close调用不及时
LISTEN - 侦听来自远方TCP端口的连接请求; SYN-SENT - 在发送连接请求后等待匹配的连接请求; SYN-RECEIVED - 在收到和发送一个连接请求后等待对连接请求的确认; ESTABLISHED - 代表一个打开的连接,数据可以传送给用户; FIN-WAIT-1 - 等待远程TCP的连接中断请求,或先前的连接中断请求的确认; FIN-WAIT-2 - 从远程TCP等待连接中断请求; CLOSE-WAIT - 等待从本地用户发来的连接中断请求; CLOSING - 等待远程TCP对连接中断的确认; LAST-ACK - 等待原来发向远程TCP的连接中断请求的确认; TIME-WAIT - 等待足够的时间以确保远程TCP接收到连接中断请求的确认; CLOSED - 没有任何连接状态;
为什么会有TIME_WAI?
当服务器给客户端发送FIN后,客户端不能立即释放TCP连接(因为此时客户端还没有发送ACK给服务器,服务器一段时间没收到ACK就会重传FIN,当服务器重传FIN后,客户端还要将ACK返回给服务器,需要服务器的端口号等信息,所以不能立即释放),在一定时间段内,没有收到服务器重传的FIN,就说明ACK已经被服务器所接收。
4.滑动窗口
在日常的业务背景下,有时会处理大量请求,这时,如果再一条一条发送,一条一条接收的话,效率非常低下。此时就引出华东窗口这样的机制了。
没错,就是算法题中的那个滑动窗口。
窗口大小指的是无需得到ACK应答就能发送数据的最大值,图中的窗口大小是2500个字节(五个段)
当一个段接收到ACK后,窗口就会向后移动,然后发送第六个段的数据
操作系统内核开辟了发送缓冲区来记录当前有哪些数据还没有应答,应答了的数据就会从缓冲区中删除。
窗口越大,传输的数据就越多,网络的吞吐率就越高。
如果发现丢包怎么办?丢的是ACK包: 不影响,因为后续的应答数据报的确认序列会告诉进程,这个序列号之前的数据,都已经接收了。
丢的是TCP数据报: 有影响,
- 当其中的一个数据报丢失了(例如这个窗口是501~2001,其中501-1000的这个数据报丢失了),那么接收端就会一直向发送端发送同一个应答数据报(要求下一个传输的数据报是从501开始,因为接收端没有接收到501-1000的数据报)
- 当发送端连续收到这样的同一个应答数据报,就会重新发送501-1000的数据报
- 当接收端接收到了501-1000的这个数据报,那么就会继续执行,下次执行的就是正在运行的正常的业务的应答数据报。(因为在这个过程中,操作系统内核会有一个接收缓冲区,会将正常的数据报都放在这里面,等处理完异常的情况后,就会根据缓冲区里的数据来继续执行)。
如上这样的处理方式称为快速重传
滑动窗口有什么意义呢?
TCP为了保证可靠性,牺牲了很多的效率,滑动窗口就是为了弥补效率的牺牲,而采取的一种办法。
但是即使采取了措施,效率还是没有UDP快。
5.流量控制
流量控制其实是滑动窗口的一个控制窗口大小的补充。
接收端处理数据的速度是有限的,如果发的太快。接收端还没来得处理,接收端的缓冲区就满了,可能就会发生丢包,引起丢包重传等一系列问题。
因此TCP⽀持根据接收端的处理能⼒, 来决定发送端的发送速度. 这个机制就叫做流量控制(FlowControl);
6.阻塞控制
和流量控制一样,是为了辅助滑动窗口使用的。
数据在网络的传输中,会经过很多网络结点。传输的速度就会受到这些结点的限制。
为了探寻传输数据的的最高效率,引入了阻塞控制
工作流程如下
- 最开始按照小的速度,小的窗口来发送数据
- 如果没有丢包,就加大速度,增大窗口,继续发送
- 增加到一定程度,出现了丢包现象,就立即减小速度,缩小窗口
- 当没有丢包情况出现后,就继续加大速度,增大窗口
- 上述操作持续进行,直到找到一个阈值。
流量控制的窗口和阻塞控制的窗口选择哪个呢?
谁小听谁的
注意
阻塞控制的窗口变化也是有相应的规律的。
- 慢开始: 先以小的窗口传输数据,主要是检测通信路径是否通畅。
- **扩大窗口①:**以指数的形式来扩大窗口。
- **扩大窗口②:**到达某个阈值,就开始线性扩大窗口
- 缩小窗口:
①:窗口直接缩小到0,再重复上述流程,
②:窗口缩小一半,然后线性的增长窗口。
7.延迟应答
在接收方接收到数据后,并不立即返回ACK,而是隔一段时间再返回ACK,这样可以提高传输速率
窗口越大,网络的吞吐量就越大,也就是说网络传输的速度也就越快
当接收方接收到数据后,处理数据需要时间,而延迟应答的这一段时间正好就可以处理数据,处理完数据后,缓冲区的空闲窗口就会变大,从而由流量控制来进行响应给出反馈,从而使得传输速度变快。
说到延迟应答,就又跟四次挥手联系在一起了,延迟应答这种机制就可以将四次挥手给合并成三次挥手哦,不过这只是特殊情况。一般的情况就还是四次挥手。
** 延迟应答的时间的规划**
- 按照一定的时间来应答,一般是200ms
- 按照收到的数据报个数来应答,一般是两个包
8.捎带应答
捎带应答 就是将两个数据报的内容在满足业务逻辑的情况下,将几个数据报合并在一起。
例如在三次挥手中,就将ACK报文和SYN报文合并在一起了。
这样做的意义:
可以调高效率,减轻服务器的压力,因为一次数据的传输,需要经过层层的封装和分用,,将两次操作合并成一次,减少了很多工作量。
9.粘包问题
首先我们要明确,粘包问题粘的是应用层的数据报
为什么会出现这样的情况?
因为TCP协议报头并没有像UDP协议一样报头中有着报文长度这样的属性字段,所以不能确定一个数据报是多长而进行分割
站在传输层的角度来说,TCP是一个一个报文来的,按照序号排放在缓冲区中.
站在应用层的角度来说,看到的只是一串连续的字节数据,
应用程序看到了这么一连串的字节数据,就不知道从哪个部分开始到哪个部分结束是一个完整的数据包。
说了这么多,那么该如何让避免呢?
- 使用分割符: 定义任意字符,来作为分隔符。例如用空格作为分割符来将包与直接分割开。
- 约定包的长度: 在发送数据包之前,也将这个数据包的长度一同发送过去,从而就知道了包的结束位置
10.异常情况
- 一个进程崩溃
进程终⽌会释放⽂件描述符, 仍然可以发送FIN. 和正常关闭没有什么区别.
操作系统内核都会回收释放对应PCB,可以释放文件描述符表,相当于close。也会正常的进行四次挥手,一定会完成四次挥手。 - 主机正常关机
这时,操作系统先会强制结束所有的进程,正常的进行四次挥手,但不一定会完成四次挥手,但是不影响。因为当发送了FIN后没有接收到ACK或者接收到ACK后,A进程结束,此时B的FIN会重复传送几次,如果重传了几次还是没有收到A的ACK,那么B还是会把A的信息(IP地址,端口号等)删除,此时A已经关机了所以A中B的信息也删除了。 - 主机电源断开
分两种情况来分析:
- 当接收端的电源断开时
发送端接下来发送的数据,都不会有ACK了
那么就会触发超时重传,重传几次之后,就会发送复位报文RST,当复位报文也没有响应时,发送端就会删除接收端的信息。 - 当发送端的电源断开时
接收方会在一段时间没有收到来自发送方的数据时,会触发一个心跳包,
心跳包的特点:有周期,有心跳就说明进程没有关闭
在接收方发送了几次心跳包后,没有收到回复,那么就会认为发送方进程已经结束,就会删除发送端的信息。
- 网络断开
本质也就是第三种。
以上就是本文所有内容,如果对你有帮助的话,点赞收藏支持一下吧!💞💞💞