TCP协议UDP协议(传输层重点协议)
Posted AJIUZ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TCP协议UDP协议(传输层重点协议)相关的知识,希望对你有一定的参考价值。
目录
一、UDP 协议
1、UDP 协议段格式
实际的格式是一排连着的:
所谓的把 应用层数据 封装成 UDP 数据段,本质上就是在 应用层数据 的基础上,添加了 8 个字节的段头。
① 源端口、目的端口:我们在写代码进行网络通信时,写的端口号就会在被打包到这样的UDP数据报中。表明了数据从哪个进程来,到哪个进程去。
② UDP报文长度:整个UDP数据报的长度,单位是 字节 。(UDP报文长度 - 8个字节 = 应用层数据长度)
这里要注意:
此处的报文长度是 2个字节,范围是 0~65535。(UDP缺陷:无法表示一个较大的数据段)
当我们要传输的数据长度大于2个字节的长度时:
① 方法一:我们可以在 应用层 进行分包(拆成多个部分),然后再通过 多个UDP 数据段 分别发送。接收方收到后,再把几个包重新拼接成完整的数据。(下策,实现太麻烦了)
② 方法二:不适用 UDP协议了,改成 TCP协议。因为 TCP 中没有这样的长度限制。
③ UDP检验和(校验和):用来验证网络传输的这个数据是否是正确的。(生成校验和的算法:crc、md5、sha1.....都是基于内容来生成的校验和)
网络上传输的数据本质是 光信号 和 电信号。在现实生活中,如果有一些外界干扰(磁场之类的)就可能会导致一些传递的数据信息发生了变化(bit翻转)。 因此我们要尽可能是识别是数据是不是错了,所以就需要有校验和。
校验和正确并不能保证数据百分之百正确,但是校验和不正确,数据一定有问题。当数据有问题时,就直接丢弃了。
2、UDP 协议 的特点
- 无连接
- 不可靠
- 面向数据报
- 大小受限
二、TCP协议(TCP UDP 常考面试题还没整理别忘啦)
1、TCP协议段格式
① 源端口号 和 目的端口号:和 UDP 一样,需要能够记录 端口号。
② 首部长度:4 个 bit位,表示 TCP段头(报头)的长度(单位是 4 个字节)。
因为 TCP报头 是变长的,不像UDP固定8个字节。主要就是因为 “选项”,这个部分可以有,也可以没有;可以有多个,也可以有一个。
例如:1111 0xF 此时的意思是报头的长度就是 15 * 4(单位) = 60
③ 保留位:保留位的意思是 现在还不用,但是以后还要用。(为了未来的升级,留点空间)
④ 校验和:和 UDP段中的一样。
以下的字段要根据 TCP 的原理来理解。
④ 序号 和 确认序号:确认应答机制中。
⑤ URG、ACK、PSH、RST、SYN、FIN:TCP 报文的核心字段,六个标志位。
URG | 紧急指针是否有效 |
ACK | 确认号是否有效 应答报文 / 确认报文段 |
PSH | 提示接收端应用程序 立刻从 TCP 缓冲区中 把数据读走 |
RST | 对方要求重新建立连接 我们把携带 RST 标识的称为 复位报文段 |
SYN | 请求建立连接 我们把携带 SYN 标识的称为 同步报文段 |
FIN | 通知对方,本端要关闭了 我们把携带 FIN 标识的称为 结束报文段 |
⑥ 窗口大小:流量控制机制中。
⑧ 紧急指针:标识哪部分是紧急数据。
⑨ 选项:可以暂时忽略。
2、确认应答(安全机制)
网络传输中,数据报接收的顺序不一定和发送的顺序完全一致,存在后发先至的情况。这是一种很常见的现象。因为现实中网络环境十分复杂,连续发的两个包,不一定就是走的统一条路。
为此 TCP协议 中设计了确认应答机制:将每个字节都分别进行了编号,该编号即为序列号。
大概过程如下图:
每一个 ACK(应答报文)都带有一个 确认序列号,告诉发送者,我已经收到了哪些数据,下一个你从哪里开始发。
3、超时重传(安全机制)
数据传输过程中,可能会出现数据包丢失的情况,即丢包。
丢包主要有两种情况:A给 B 发送的数据包丢失;B给A发送的应答报文ACK丢失。
① A 给 B 发送的数据包丢失:
此时 A 迟迟没有收到 B 的 ACK ,就会在 特定时间间隔 后,进行重发。
② B 发送给 A 的 ACK 丢失:
此时虽然 B 收到了数据,但是 A 并不知道。因此 A 就会往坏的想,认为 B 没有收到数据,因此也会在 特定的时间间隔 后,重发。
但是重发的话,就导致了 B 会接收到 重复的数据。
问题一:为了解决 B 会收到重复的数据,TCP内部 就有了一个 去重 的操作。
接收方(B) 收到的数据会先放到 操作系统内核 的 “接收缓冲区” 中。接收缓冲区 可以视为是一个内存空间,并且也可以视为是一个阻塞队列(实际不仅仅如此)。收到了 新的数据,TCP 就会根据 序号 来查看这个数据是不是在这个 接收缓冲区 中已经存在了。如果不存在,就放进去;如果存在,就直接丢弃。保证 应用程序 调用 socket api(套接字,用于网络通信) 拿到的数据一定是不重复的。
问题二: 对应的,有 接收缓冲区 就有 发送缓冲区 。
在 发送方(A) 重新发送数据的时候,会进行重新封装。
发送方 发送 数据,这是 TCP 负责发送的。 调用一个 socket 中的 write 操作,本质上就是把这个数据写到 TCP 的 “发送缓冲区” 中(发送缓冲区 也可以视为一个阻塞队列),由 内核 从 发送缓冲区 取数据,通过网卡传输(也就是封装的过程)。
当触发了超时重传的时候,内核 再把刚才 发送缓冲区 的内容,重新再进行封装,重新传输。
如果顺利收到了 ACK,就会把这个数据从 发送缓冲区 中删掉。
问题三:重传如果失败,可能会再次尝试,但不会无休止的尝试。
当 连续多次重传 都失败(在现实生活中是小概率事件),就会认为这个网络可能遇到了严重的情况(网线断了),此时再怎么重传都没有用,就只能放弃重传了(自动断开 TCP 连接)。
重传有一个时间间隔,即最大超时时间,这个时间间隔会逐渐变大,即重传的频率会逐渐降低。积累到一定重传次数后,TCP 就认为网络或对端主机出现异常,强制关闭连接。
最大超时时间并不是规定的一个时间间隔,而是TCP动态计算的,但是也可以人为的设置。
4、连接管理(安全机制)
连接管理机制主要是设定了TCP:如何建立连接;如何断开连接。
- 建立连接:三次握手
- 断开连接:四次挥手
① 三次握手
假设现在有一个客户端(主动发起连接请求的一方),一个服务端。
三次握手的过程(双向奔赴):
客户端先发送一个 FYN 同步报文段 给服务器,如果 FYN 为 1,表示当前的报文段是一个 同步报文段,说明客户端要和服务端建立连接。
此时 服务端 就会返回一个 ACK 确认报文段 给 客户端,如果 ACK 为1,表示当前的报文段是一个 确认报文段。而同时 服务端 也会发送一个 FYN 同步报文段 给客户端,表示要和 客户端建立连接。注意:这两次一定会合二为一,即合成为一个报文段发送给 客户端。
合二为一:因为每次的传输数据,都要经过一系列的封装和分用才能完成传输。封装一次就进行传输肯定要比 封装两次 传输 更高效。
当客户端收到该报文段后,同样会给 服务端 发送一个 ACK 确认报文段。
图上还有几个 TCP状态:
① LISTEN:表示 服务器 启动成功,端口绑定成功,随时可以有客户端来建立连接。(类似手机信号良好,随时可以接听别人的来电)
② ESTABLISHED:表示 客户端 已经连接成功,随时可以进行通信。(电话已经接通,我随时可以说话,并且对方也能听到了)
三次握手的意义:
三次握手,相当于 投石问路 ,检查一下当前这个网络的情况是否满足可靠传输的基本条件(如果没有三次握手,当网络非常差时,强行进行 TCP 传输,就会涉及到 大量丢包问题)。更具体的来说,三次握手 也是在检测 通信双方 的 发送能力 和 接收能力 是否正常。
让双方能够协商一些必要的信息。
例如:生活中的网课中老师提问学生:
当 我的麦克风 有问题时,我问“老师你能听见我说话吗”就等不到回应了,如果又说了“老师能听到吗”后,还是没有回应,我就知道了这次通信失败了(不具备可靠传输的条件了),于是我就放弃了。
② 四次挥手:
还是以 客户端 和 服务端 为例。
四次挥手过程:
双方 各自向对方发送 FIN(结束报文段) 请求,FIN 为 1 表示这是一个 结束报文段,并且各自 给对方回 一个 ACK 确认报文。
注意:
- 三次握手:一定是客户端主动发起请求。
- 四次挥手:可能是客户端,可能是服务端。
关于中间两次:
- 三次握手:中间两次合并。
- 四次挥手:中间两次有时候可合并,有时候不能合并。
不能合并因为 时机不同 :
以上图为例,服务端发送的 FIN 和发送的 ACK 的时机是不同的。
三次握手中,服务端发送的 SYN 和 ACK 是同一时机,并且都是由 操作系统内核 负责进行的。
四次挥手中,服务端发送的 ACK 是 操作系统内核 负责的,而 FIN 是 用户代码 负责的。(服务端B 的代码中调用了 socket.close() 方法,才会触发 FIN)。
那为什么又有时候可以合并:
四次挥手中,服务端 收到 FIN,就立即由 内核 返回 ACK。而 FIN 是执行到代码中的 close 时才会触发,这就取决于 用户代码 是怎么写的了。
如果这两个操作之间的时间差比较大,就不能合并了。相反,如果时间差比较小,就有可能会合并(延时应答 和 捎带应答)。
图上还有两个重要的状态:
① CLOSE_WAIT:四次挥手了两次后出现的状态,这个状态就是在等待代码中调用 socket.close() 方法,来进行后续的挥手过程。正常情况下,一个服务器上不应该存在大量的 CLOSE_WAIT ,如果存在,说明大概率代码有 bug,close方法没有被执行到。
② TIME_WAIT:谁主动发起的 FIN,谁就进入 TIME_WAIT。表示给最后一次 ACK 重传的机会。
表面上看起来,A 发送完 ACK 后,就没有 A 什么事情了。按理来说,A 就该 销毁连接释放资源了。但实际上并没有直接释放,而是会进入 TIME_WAIT 状态等待一段时间,这段时间过后,才会释放连接。
等这段时间,就是为了防止 最后一次 ACK 丢包。如果这个 ACK 丢包了,B 就收不到 ACK 了,但是 B 没有办法区分是 自己传的 FIN 丢包了,还是 ACK 丢包了,于是 B 就假设是 FIN 丢了,就会重传 FIN(超时重传)。那如果 A 发完 ACK 就直接释放连接了,当重传的 FIN 到达 A 后,就没有人处理这个 FIN 了,也就没有人对这个 FIN 进行应答了。
因此 A 的连接不应该太早的释放,而应该等待一段时间,确保 B 不会重传 FIN 了之后,在真正的销毁连接。
TIME_WAIT 应该持续多久:
设定的时间是:2 * MSL(TCP报文的最大生存时间) 。
MSL :网络上任意两点之间,传输需要的最大时间。 这个时间也是系统上可以配置的参数,一个典型的设置就是 60s(经验值)。
5、滑动窗口(效率机制)
以上的保证可靠传输的安全机制,有一个缺点,就是性能太差。尤其是数据往返时间较长的时候。既然一条一条的发送效率低,我们就可以一次性发送多条数据,这样就可以大大提高性能了。
滑动窗口存在的意义就是,在保证可靠性的前提下,尽量提高效率。
滑动窗口的意思:
窗口大小:如果一次批量发送的数据 为 N,统一等待,此时这里的 N 就称为 “窗口大小”。
滑动:滑动的意思是,并不用把 N 组数据的 ACK 都等到了,才继续往下接着发送数据。而是收到一个 ACK ,就继续往下发送一组。(类似滑动的效果)。
在上面的例子中,当前是在等待 1001、2001、3001、4001 这四组的 ACK,不需要等到 4001 的 ACK 到了,才继续往下发。
只要 1001 到了,就可以往下多发一组(4001 ~ 5000),此时等待 ACK 的范围就是 2001、3001、4001、5001 了。
如果 2001 到了,就又继续往下多发一组(5001 ~ 6000),此时等待的 ACK 的范围就是 3001、4001、5001 、6001。
滑动的效果:
当这个窗口越大时,可以认为传输速度就越快。窗口大了,同一份时间内等待的 ACK 就更多,总的等待 ACK 的时间就少了。
如果出现丢包:
丢包分为两种:ACK 丢了;数据 丢了。
ACK 丢了:
ACK 丢包并不要紧。
在上图中,A 发送完一组数据后,发现收到了 2001 的 ACK 和 4001 的 ACK.......
2001 ACK 的意思:2001 之前的数据都已经收到了。因此 A 看到 2001 的 ACK 后,就知道 2001 以前的数据肯定发送过去了,因此 1001 的 ACK 就无所谓了。
ACK 确认序号的特定含义,就是可以保证后一条的 ACK 能覆盖前一条。
所以同理,当 A 收到了 5001 的 ACK 时,意味着 1 ~ 5000 的数据 B 都收到了,3001 的 ACK 丢包也毫无影响。
数据丢了:
在 A 重传 1001~2000 之前,B 的接收缓冲区会一直有一个缺口。
当 A 重传了 1001 ~ 2000 之后,B 的接收缓冲区就把缺口补上了。而后面的数据(2001 ~ 7000) B 已经收到了,所以就会直接向 A 索要 7001 开始的数据。(发送 7001 的 ACK)
快重传:
这里的重传只是需要把丢了的一部分数据重传了即可。其他的数据都已经到了,就不用再重传了。整体的重传效率还是比较高的。称为 快重传。
6、流量控制(安全机制)
流量控制 是 滑动窗口 的延伸,目的是为了保证可靠性。
在滑动窗口中,窗口越大,传输效率就高。但传输过程中,不但要考虑发送方,还要考虑接收方。如果 发送方 发送的特别快,接收方已经处理不过来了(缓冲区已经满了),那么接收方就会把收到的新的包丢弃,这样发送方还是需要重传,高效操作就没什么用了。
因此 TCP 支持了 根据接收端的处理能力(接收缓冲区的剩余空间大小),来决定 发送端 的发送速度。这个机制就叫做 流量控制。
如果 接收缓冲区 的 剩余空间比较大,就可以让 发送端发快点。如果剩余空间比较小,就让 发送端 发慢一点。
发送过程:
接收端 将自己可以接收的 缓冲区大小 放入 TCP报头中的 “窗口大小” 字段,通过 ACK段 通知发送端。
窗口大小 的 字段越大,说明网络的吞吐量越高。
接收端一旦发现自己接收缓冲区快满了,就会将 窗口大小 设置成一个 更小的值 给发送端。发送端收到了这个窗口后,就会减慢自己的发送速度。
如果接收端的缓冲区满了,就会将窗口设置为 0 ,这时发送方不再发送数据,但是需要定期发送一个 窗口探测数据段,使接收端把此时的窗口大小告诉发送端。
注意:TCP报头中的 16位窗口大小,并不是 TCP 窗口最大就是 65535 字节。在 TCP报文 的 选项 中还包含了一个窗口扩大因子 M,实际的窗口大小是 窗口字段 的值 左移 M 位。
7、拥塞控制(安全机制)
是 滑动窗口 的延伸,也是限制 滑动窗口的发送速率。
网络上有很多的计算机,因此当前的网络状态可能会比较拥堵。如果这是还是贸然发送大量数据的话,可能会让网络更加拥堵,并且数据也会有很大的可能丢失。因此就有了拥塞控制机制。(类似运动员的负重训练)
拥塞控制衡量的是,发送方到接收方,整个链路之前的拥堵情况。通过 “实验” 的方式,逐渐调整发送速度,然后找到一个比较合适的值(范围)。TCP 就引入了 慢启动 机制,先发少量的数据,先探探路,摸清当前的网络拥堵状态,然后再决定按照多大的速度传输数据。
发送过程:
假设 A B 两台主机进行通信。
A 开始的时候,以一个较小的窗口来发送数据,如果发现数据的传输很流畅,A 就会逐渐增大窗口大小然后进行传输。
当窗口大小增大到一定的程度的时候,出现了丢包的现象,就说明通信链路已经出现拥堵了,此时就会减小窗口大小,然后再进行发送。
通过 反复 的 增大/减小 的过程,就逐渐找到了一个合适的范围。拥塞窗口就会在这个范围中不断的变化,达到一种动态的平衡。
类似于运动员负重训练,一开始运动员也不知道负重多少合适,就一点一点慢慢增加重量,当过重了就开始一点点减轻重量,最后找到了一个合适的重量范围 。
拥塞窗口变化的机制:
“慢启动” 指的是开始的窗口很小,但是增长的速度很快。为了不增长的那么快,就引入了 慢启动 的阈值,当拥塞窗口超过这个阈值的时候,不再按照指数方式增长,而是按照线性方式增长。
当 TCP 开始启动的时候,慢启动阈值 = 窗口最大值;每次超时重传的时候,慢启动阈值就会变成 原来的一半,同时拥塞窗口置回 1 。
少量的丢包,触发超时重传;大量的丢包,就认为是网络拥塞了。
当 TCP 通信开始后,网络吞吐量就会之间上升,随着网络发生拥堵,吞吐量就会立刻下降;而拥塞控制,就是 TCP协议 想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的这种方案。
8、延时应答(效率机制)
相当于流量控制的延伸。流量控制相当于踩了一下刹车,让发送方不要发的太快;延时应答就是在这个基础上,能够尽可能的让窗口再更大一些。
延时应答就是 不那么快的进行应答。
假设先在接收端缓冲区为 1M。此时收到了 500K 的数据,如果立刻应答,返回的窗口就是 500K,但是处理端处理接收端缓冲区的速度很快,一下子就把 500K 的数据从缓冲区中取走处理了,此时接收端缓冲区的剩余大小变成了 1M,说明可以接收的数据就不止 500K 了。因此 接收端就会等一会再进行应答,此时返回的窗口就是 1M 了。(但也不是每个包都延时应答的)
注意:窗口越大,网络吞吐量就越大,传输效率就越高。我们这些机制的目的就是保证网络不拥塞的情况下,尽可能的提高传输效率。
9、捎带应答(效率机制)
捎带应答是延时应答的延伸。即合并问题。
但是又因为有了延时应答机制的存在 ,所以导致 ACK 不一定会立即返回。
如果因为延时应答导致,ACK 返回的时间 和 应用程序中相关代码执行 的时间重合了,就可以把这个 ACK 和 数据 合二为一。
10、面向字节流
TCP 与 UDP 的区别之一就是 TCP 是面向字节流的,而 UDP 是面向数据报的。面向字节流意思是传输数据的时候使用字节就可以(字节流)。
正因为这个特性,就会导致 粘包 的问题。
(1)粘包问题
粘包问题中的 “包” 指的是应用层的数据包。在 TCP 缓冲区中,若干个应用层数据包混在一起了,分不出来谁是谁了。
因为在 TCP报头 中,没有像 UDP 报头一样的 “报文长度” 字段。而虽然有 序号 字段,但是由于有 分用 这样的操作,也没有什么用。
站在传输层的角度,TCP是一个一个报文传过来的,在缓冲区中也是按照 序号 排列存放的。
站在应用层的角度,看到的只是一串连续的 字节数据 而已。因此就不知道从哪个部分开始到哪个部分是一个完整的应用层数据包了,即粘包问题。
如何避免:
在 应用层协议 这里,加入包之间的边界。(粘包问题,虽然在TCP中,但实际上是这个应用层的问题)
- 对于定长的包,保证每次都按固定大小读取;
- 对于变长的包,可以在包头位置,约定一个包总长度的字段,从而就知道了包的结束位置;还可以在包和包之间使用明确的分隔符.........(应用层协议是程序员自己约定的,所以方法很多,只要保证分隔符和正文之间没有冲突即可)
11、 TCP 的异常处理
(1)进程终止
在进程毫无防备的情况下,突然结束进程。进程终止就会释放文件描述符,仍然可以发送 FIN。和正常关闭没有什么区别。
TCP 连接是通过 socket 来建立的, socket 本质上是进程打开的一个文件,这个文件其实就存在于 进程的PCB 里的 文件描述符中。每次打开一个文件(包括 socket),都会在 文件描述符表 中增加一项。每次关闭一个文件,都会在 文件描述符表 中 删除一项。
如果直接终止进程(类似杀死进程),进程的PCB 也就没了,里面的 文件描述符表 也就没了,此处的文件相当于 “自动关闭” 了。这个过程其实 和 手动调用 socket.colse() 方法一样,都会触发 4次挥手 。
(2)机器关机
按照正常的 操作系统约定的的 流程关机,和进程终止的情况相同(正常关闭)。
正常流程的关机,会让操作系统 杀死 所有进程,然后再关机。就和进程终止一样了。
(3)机器掉电/网线断开
例如台式机直接拔掉电源。操作系统不会有任何的反应时间,更不会有任何的处理措施。接收端认为连接还在,一旦接收端有写入操作,就能发现连接已经不在了,就会进行 reset。如果没有写入操作,TCP 自己也内置了一个 探测报文(心跳包),会定期询问对方是否还在,如果对方不再,也会把连接释放。
通过探测报文,发现对方不返回ACK了,就认为出了问题,就释放连接。
12、TCP协议的特点
- 有连接
- 可靠传输(核心机制,可以说引入 TCP 的关键原因就是为了 保证可靠传输 )
- 面向字节流
- 全双工
13、TCP小结
TCP 是在可靠的基础上,尽可能高的提高性能效率。
三、TCP vs UDP 对比
1、对比
TCP | UDP | |
特点 |
|
|
应用场景 | 可靠传输 (文件传输、重要状态更新...) | 对高速传输和实时性要求较高的领域 (早期QQ、视频传输) UDP可用于 广播 |
2、* 常见问题 *
(1)基于 UDP 如何实现可靠传输?
(2)基于UDP协议,传输超过 64K 的数据,应该如何设计?
这两个问题都可以参考 TCP机制 来进行设计。
(3)如何基于 UDP 实现分包组包?
解决方法就是参照下面的 IP 协议(16 位标识、3 位标志字段、13 位片偏移)。
传输层协议(TCP/UDP)介绍
一,TCP/IP协议族的传输层协议概况:1,TCP:传输控制协议
2,UDP:用户数据报协议
二,TCP/UDP协议详解:
1,TCP
a.TCP是面向连接的,可靠的进程到进程通信的协议 ;
TCP提供全双工服务,即数据可在同一时间双向传输。
b.TCP报文段:
TCP将若干个字节构成一个分组。叫报文段。TCP报文段封装在IP数据报中。
数据段详解:、
序号(32):发送端为每个字节进行编号,便于接收端正常重组。
确认号(32):用于确认发送端的信息。
窗口大小(16):用于说明本地可接收数据段的数目,窗口大小是可变的。
SYN:建立连接的请求标记
FIN:断开连接的请求
ACK:确认连接的请求
RST:重新,重置(失败情况下)
URG:紧急指针位(1:开启 0:关闭)不经过缓存直接提交给对方
PSH: 速接收传递给应用层(1:开启 0:关闭)需要经过缓存,等一批报文段到齐直接发送到应用层。
c.常用TCP端口号及其功能
端口 | 协议 | 说明 |
---|---|---|
21 | F T P | FTP服务器所开放的控制端口 |
23 | TELNET | 用于远程登陆,可以远程控制 |
---|---|---|
25 | SMTP | SMTP服务器开放的端口,用于发送文件 |
8 0 | HTTP | 超文本传输协议 |
---|---|---|
11 0 | P0P3 | 用于邮件的接受 |
扩展:20端口是FTP的数据连接,21端口是FTP的控制连接,22端口SSH协议用于远程密文传输,23是明文,443端口HTTPS是安全传输协议。
d.TCP连接
TCP建立连接的过程称为二次握手
TCP断开连接的四次挥手
2,UDP
a.UDP协议:①无连接,不可靠的传输协议,②花费的开销小
b.UDP报文的首部格式
源端口号(16) | 目标端口号(16) |
---|---|
UDP长度(16) | UDP校验和(16) |
c.UDP常用端口号及其功能
端口 | 协议 | 说明 |
---|---|---|
69 | TFTP | 简单文件传输协议 |
111 | RPC | 远程过程调用 |
---|---|---|
123 | NTP | 网络时间协议 |
以上是关于TCP协议UDP协议(传输层重点协议)的主要内容,如果未能解决你的问题,请参考以下文章