udp/tcp特征
udp:
- 无连接
- 不可靠传输
- 面向数据包
- 全双工
tcp:
- 有连接
- 可靠传输
- 面向字节流
- 全双工
解释:
有连接/无连接:发送消息时,对方是否必须要在线
比如我们聊天程序,我们给对方发送消息,是不管现在是否在线的,这就是无连接
但我们打电话时,必须要对方接通后才能对话,这句话有连接
可靠/不可靠传输:发送消息后,我并不知道对方是否已经接收到了消息
如果我明确知道对方接收到了发送的消息(打电话/已读功能)则是可靠传输,反之则不可靠
面向数据报/字节流:
字节流:数据传输就和流水一样,接踵而至,并不知道完整数据报的头和尾分别在哪
数据报:数据传输是以数据报单位传输的,具有一定的格式
全双工:即可以发送数据,也可以接收数据,一条网络通路是互通的
udp报头结构
udp报头共八字节,四个部分,每个部分2个字节
- 源端口:发生信息的端口号
- 目的端口:接收信息的端口号
- udp报文长度:udp载荷----应用数据包的大小
- 校验和:判断接收到的数据是否和发送的数据是否一样,如有错,直接丢弃
udp报文长度为两个字节(2^16 65535b),如果应用层数据包的大小超过了64kb怎么办?
- 此时我们就需要在应用层的代码逻辑中,手动多次地将一个udp数据报分成多次来发送
- 换用TCP协议,TCP协议并没有应用层数据报的大小限制
tcp报头结构
1.源/目的端口:从哪来,到哪去
2.4位tcp报文长度:这里的单位是字节,故能表示的长度大小位(0----60字节)
3.6位标志位:
- URG:紧急指针是否有效
- ACK:确认序号是否有效
- PSH:提示接收端应用程序立刻从tcp缓冲区把数据读走
- RST:对方要求重新建立连接,我们把携带此标志位的称为复位报文段
- SYN:请求建立连接,我们把携带此标志的称为同步报文段
- FIN:通知对方,本端要断开连接了,我们把携带此标志的称为结束报文段
4.校验和:确认发送与接收数据是否一致 (CRC)
5.十六位紧急指针:表明哪写数据是紧急数据
6.选项前的长度是固定的-----20字节选项长度 = 报文长度 - 20字节
TCP原理:
应答机制
引出:在网络传递信息时,可能会出现后发先置的情况
信息正常顺序送达:
小明---->小红:今天要不要出去玩 小红---->小明:没问题
小明---->小红:借我点钱 小红---->小明:哥稳衮
但网络传输存在后发先置的情况,其接收顺序可能会发送变化,从而变成
虽然消息2是后发的,但是它先于消息1到达,故小红回答哥稳衮,但在小明看来他是先发的消息1,故小明以为小红的回答是:
小明:今天要不要出去玩 小红:哥稳衮
小明:借我点钱 小红:没问题
此时,问题是不是变得和正常的情况完全相反了,不符合我们预期
解决:应答机制
将每条消息进行编号,响应对应序号的消息,此时,即使消息达到的顺序乱了,我们也可以对每个消息进行精准地回复
这个时候就需要用到我们tcp报文结构中的序号了,如何一条报文都是有序号的(包括应答报文),而确认序号只有应答报文才有,当ACK=1时,表示当前报文是应答报文,如果为0则表示是一个普通报文
超时重传
引出:当我们传输数据时不顺利时-------丢包(小概率事件)
- 数据报丢失
- 返回的ACK确认序号丢失
此时发送方是无法区分到底是哪种原因导致传输失败的,他只觉得没有接收到ack确认序号
此时TCP就会引入重传机制,并引入一个时间阈值,当发送方发送数据后等待实际超过时间阈值后还是没有收到ACK,此时就会重传一份和之前一摸一样的数据,而这个时间阈值是可配置的,没有固定
那如果重传一次数据后,又丢包了怎么办?
继续重传,当重传到一定次数后,会认为是网络故障,断开重连后再重传,如果此时还是失败,便断开连接。而具体是重传几次后重连,也是可配置的,不确定
同时,你重传的次数不同,时间阈值也随之不同
重传轮数越大,阈值越长,重传的频率越低
因为你重传的次数越多,代表你获得ACK的成功率越低,即使你重传地快,一样还是得不到ACK,白白浪费资源
故TCP的可靠运输,就是一句应答机制和超时重传机制所实现的
连接管理:
三次握手:
1.连接流程:
A发生SYN向B请求建立连接,B接收到请求后返回给A一个ACK,同时发生一个SYN请求和A建立连接,A接收到B的请求后返回给B一个ACK
2.这里实际上是4个步骤,为什么只有三步呢?
其实就是把B向A返回的ACK和SYN合并到一起了
3.那么为什么要合并呢?
封装分用一次的成本无疑比封装分用两次的成本低
三次挥手的意义
1.确认彼此都是对方交流的对象
2.确认彼此的接收发送功能都是正常的
3.在三次握手的过程中,可以来商讨一些彼此之间的秘密
四次挥手:
挥手流程:
1.A向B发送FIN报文,此时A进入FIN_WAIT_1状态
2.B接收到FIN报文后,返回ACK,进入到CLOSE_WAIT状态
3.A接收到B的ACK应答报文后,进入FIN_WAIT_2状态
4.B处理完数据后,向A发送FIN报文,进入LAST_ACK状态
5.A接收到B发送的FIN报文后,发送ACK报文,进入TIME_WAIT状态
6.B收到ACK报文后,断开连接,此时处于CLOSE状态
7.A经过2MSL时间后,进入CLOSE状态
问题:
1.为什么挥手中间的两次操作不能像握手一样合并成一个
首先我们得知道一个点:
FIN的发送是由我们程序员自己控制的,当我们调用操作系统给的socketAPI中的close方法时,就会发送FIN报文。而ACK是内核自动发送的,当接收到FIN,内核会立即自动返回一个ACK。所以在发送ACK和FIN报文之间,存在着一个很明显的时间差。因为你程序员在调用close方法之前做了什么操作,需要多少时间都是不确定的,故这两步操作无法合并成一步,不处于同一时机。
2.为什么当A收到B的FIN后是进入TIME_WAIT状态,而不是CLOSE状态?
1).由上方所述,当接收到FIN报文后内核会自动返回一个ACK,如果A直接断开连接,处于CLOSE状态。那么如果此时ACK没有顺利到B呢?此时不就造成丢包问题了
2.)充分接收B在网络中还在传输的数据--------由于FIN之前的代码业务代码都是不同的,如果在调用close方法前执行了很多业务,传送了大量数据,此时A接收到FIN后如果直接变为CLOSE状态,那么就有可能没有完整地接收B传输给A的消息
3.2MSL是什么?怎么来的?
MSL:Maximum Segment Lifetime(最大报文存活时间),根据字面意思我们就能理解了,当超过此时间后报文被消灭,连接被断开
那么为什么是2MSL--------一来一回嘛
MSL具体是多少:通常情况下,这个值是60s,但是还是具体情况具体分析,并不是一个固定值,更准确地说MSL更像一个经验值,保证了99%的数据的传输时间都不会超过MSL
三次握手和四次挥手就决定了TCP可连接的熟悉
滑动窗口(效率机制):
如果我们每发一次消息,就需要等待接收方返回ACK后,再发一条消息,此时效率是否有点慢了呢?(可以 但不快)
解决:我们一次多发几条消息,其本质是将等待时间重叠了
- 窗口大小:指的是无需等待返回的ACK即可发送数据的最大值。如上图,此时的窗口大小就是4000,故前四个发送的请求,直接发送,无需等待ACK
- 收到第一个ACK后,滑动窗口就开始往后移动了。(注意:并不是需要等待全部的ACK返回才能继续发送后面的请求,而是只要ACK小于窗口大小对应的消息数时,就往后移动,使需要等待的ACK始终是4条(就上图))
- 操作系统为了维护这一个滑动窗口,会在发送方开辟一个发送缓冲区来记录还没有应答的数据,只有接收到ACK的数据才会被冲刷掉
问题:
1.ACK丢了
没啥事,因为确认序号的定义是:此序号前的序号全都已经确认了
比如你发送的1-1000序号包丢了,但你ACK返回了2001,此时表示1-2000的序号都已经被确认了,所以中间的确认序号丢掉几个无关大雅,只要有后续的ACK到达即可
2.数据丢了
如果此时我们的1-1000序号包丢了,1001-2000的序号包成功发送到B,此时B回给A的ACK是1,此时返回的ACK和你发送的数据包已经没啥关系了,此后B就会一值索要序号1开头的数据包,如果B发现接下来几个仍然不是1开头的数据,那么这时候就需要对1-1000序号包进行重传了-------快速重传
快速重传:在发生数据丢包时尽快重传丢失的数据包,减少网络延迟
过程:
1.当接收方收到三个连续的重传请求后,仍没有收到对应缺少的数据包
2.接收方发送快速重传请求
3.发送方接收到快速重传的请求,进入快速重传的状态模式
4.发送方立即重传最后一次发送的数据包
5.发送方停止新的数据包的发送,等待接收方的确认
流量控制----控制发送方的窗口大小
前面说到,窗口大小决定着无序等待接收方返回ACK即可发送消息的多少,故窗口大小越大,吞吐量也越大,但是窗口大小能无限大下去嘛?
这显然不符实际,窗口大小太大,会大量的消耗资源,且接收端的处理能力也跟不上,发的再快也是白发,故流量控制的工作就是,根据接收方的消息处理能力,来协调发送方的发送速率
当窗口大小为0时,发送方就得停止发送数据,在暂停发送的过程中,会给B定期发送窗口探测报文,来触发查询此时的窗口大小
比如喝瓶子中的水,当水被喝完后,我会时不时地给小卖部老板打电话,问有没有进水来
在前面的TCP首部结构中,提到了一个16位的窗口大小,这表示窗口的大小最大为64kb嘛?
实际上,在选项中存在着一个窗口拓展因子M,真正的窗口大小即为窗口大小左移M位
也就是如果如果此时M为,那么实际的窗口大小为64 << 2 = 256kb
拥塞控制:考虑传输过程中中间节点的处理能力
tcp引入慢启动机制,先发少量的数据,来勘测一下目前网络传输的环境(是否拥挤)
此时引入一个概念:拥塞窗口
1.起始值为1,当收到一个ACK确认序号后,拥塞窗口+1
2.每次发送数据包的时候,将拥塞窗口大小和接收端返回给发送方的窗口大小做比较,取较小值作为实际发送的窗口
虽然叫法叫做慢启动,但其增长速度是极快的------指数级增长,为了防止其增长得过快,为其设置了一个阈值,当拥塞窗口超过此阈值的时候,不再是指数级增长,而是成线性增长
1.每当慢启动开始的时候,慢启动阈值为窗口最大值(16位)
2.当遇到少量丢包(超时重传)后,慢启动阈值会变成原来的一半,拥塞窗口也重新变成1
3.故tcp网络吞吐量是一个循环的过程,一开始逐渐增高,当遇到网络堵塞后吞吐量又变成很低,然后又依照之前的增长顺序循环
故拥塞控制的意义为:尽可能快地将数据传输给接收方,又避免给网络太大的传输压力
延迟应答:
- 如果我们接收方收到消息后就立即返回ACK,假设此时的传输数据只有400k,而接收方的缓冲区有1M,此时的窗口大小400k远没有达到缓冲区的范围大小,使效率降低
- 如果我们延迟一点时间再应答,这时候可能就又有新的数据传过来了,此时大小为1M,这时候接收方缓冲区就被充分利用了,返回的窗口大小也是1M,效率提高
窗口越大,网络吞吐量越大,网络传输效率越高
Nagle算法:解决tcp传输小包,降低传输效率的问题
试想:如果你的应用层数据包只有1字节,但由于tcp报头结构,其选项前首部固定的20字节,那么此时你发送的数据就是21字节,这些小包的发送会使网络出现拥塞
故该算法要求:一个TCP连接中,至多只能有一个未被确认的小分组,在该分组未被确认前不能发送其他的小分组,该算法会收集其他小分组,在某一合适时机发送出去
Nagle算法原理:MMS(最大报文长度)
- 如果有数据要发送,且窗口大小大于MSS,或者包含FIN报文,则立即发送
- 如果有未确认的数据,且新数据可以与未确认的数据进行合并(新数据长度+未确认数据长度 < MMS),则新数据内容可以拼接在未确认数据后面
- 如果新数据不能与未确认数据合并,且未确认数据此时已收到ACK,那么会分别发送新数据及未确认数据
- 如果新数据不能与未确认数据合并,且未确认数据此时还是没有收到ACK,这是会等待直到收到未确认数据的ACK后,新数据才能被发送
那么何时进行延迟应答呢? 每几个包之间进行延迟呢?
- 每隔N个包之间:通常取2
- 超过最大时间限制:通常取200ms
捎带应答:
还记得我们三次握手的合并吗,而这里是有概率的合并
由于我们的延迟应答机制,可能会使我们的ACK(内核调用,立即返还)和我们的业务操作变成同一时机,有概率的合并在一起返回给发送端
故延迟应答机制增加了合并的几率
面向字节流:
缓冲区:
创建tcp连接时,内核会分别创建一个发送方/接收方缓冲区
发送方:
- 当发送数据时(write),会先进入发送方缓冲区
- 当发送数据的长度超过缓冲区,会被拆分成多个tcp数据包发出
- 当发送的数据少于缓冲区时,就会先在里面等待,等待后续的数据继续写入,当缓冲区长度快要满时,再一起发送出去
接收方:
- 数据从网卡驱动到接收方缓冲区
- 接收方通过read将数据读出来
由于缓冲区的存在,读数据和写数据不需要一一匹配
1.比如,有一个100k的数据,我可以一次100k的数据包,这样一次就能发送出去。也可以一次发10k,发10次发送出去
2.当我接收这个100k的数据时,我可以一次读100k数据,一次读完。也可以一次读10k数据,读10次读完
我们知道,tcp传输是面向字节流的,而这就会引入一个问题-------粘包问题
试想:所发送来的数据都是和水一样源源不断连续的,那么我们该如何区分每一部分数据的头和尾呢?或者说,如何看懂一篇没有任何标点符合的文章呢?
解决:约定好应用层协议,明确数据与数据之间的分界线
1.定长:确定每次传输的数据长度都是固定的,按照固定的长度解析即可
2.不定长:确定与数据内容无冲突的分隔符,每当遇到分隔符时即分割字符流数据
那么udp数据报是否会存在粘包问题呢? ------- 不存在
- udp是基于数据报发送的,具有消息保护边界
- 不能合并或拆解,发送接收的数据都是一个一个的,每一个数据报都是完整的
但udp数据报虽然不存在粘包问题,但由于其是无连接的,会存在数据丢失/重复/乱序等问题
TCP异常情况:
1.进程结束:发送FIN终止报文
2.机器重启:发送FIN终止报文
3.网络断开:此时接收端还认为连接存在,但如果接收方执行到写操作,就会立马意识到连接已经不在了,这时候接收端就会向发送端发送RST报文(请求建立连接)
即使接收端没有执行写操作,TCP内部也内置了一个保活定时器,会定期询问对方是否还在线,连接是否还处于正常状态
小结:以上就是TCP传输的主要机制,其主要作用于两个功能:传输可靠:提高性能
传输可靠:
- 校验和
- 序列号/确认序列
- 应答机制
- 超时重发
- 传输管理(三次握手 四次挥手)
提高性能:
- 滑动窗口
- 流量控制
- 拥塞控制
- 延迟应答+Nagle算法
- 捎带应答
欢迎大家继续完善相关内容及补充~