赞
踩
目录
以下为UDP的特点:
端口:
我们常说的端口号的范围在0~65535(十进制) ,0~1111111111111111(二进制)的原因是因为在传输层协议中源端口号的长度是两个字节。
一个UDP报文(包括报头)是64kb,但是随着网络的发展,单次传输的数据的越来越多,因此我们一般有两种解决方案:
校验和:
作用是检查数据是否出错了。
网络传输的过程中,难免会受到一些干扰,导致传输的数据出错。(毕竟光信号和电信号,在一些特殊环境会收到影响)
而UDP中采取的是CRC算法(循环冗余校验),接收方和发送方都会计算一遍校验和。这样出错的概率会大大降低。
注:如果校验和出错,就会直接丢弃。
需要注意的是:
虽然TCP和UDP都支持全双工,当并不是代表着这两个协议只能使用全双工进行通信,以下这些方式这两个协议都支持。
全双工通信:在全双工通信中,通信的两个端点可以同时发送和接收数据,这意味着数据可以在两个方向上同时传输,而不会发生冲突。全双工通信通常用于需要双向实时通信的场景,例如网络电话或视频聊天。
半双工通信:在半双工通信中,通信的两个端点之间只能进行单向通信,要么发送数据,要么接收数据,但不能同时进行。通信方向在通信会话中可以切换,但不是同时的。半双工通信通常用于需要交替通信的场景,例如对讲机或一些传感器网络。
单工通信:在单工通信中,通信的两个端点之间只有一个方向的通信,通常是单向的。一个端点只能发送数据,而另一个端点只能接收数据。单工通信通常用于不需要双向通信的场景,例如广播电视或一些传感器网络。
TCP全称为 传输控制协议(Transmission Control Protocol),TCP协议被广泛应用,其根本原因就是提供了详尽的可靠性保证,基于TCP的上层应用非常多,比如HTTP、HTTPS、FTP、SSH等,甚至MySQL底层使用的也是TCP。
我们知道,TCP的基本特性为 面向字节流,有连接,支持全双工,可靠传输。虽然只有前面几个特点能在代码中体现(UDP与TCP网络编程),但是可靠性才是TCP中最最核心的特性。
6位标志位:
注:TCP的校验和是不可以关闭的,而UDP是可以关闭的。
校验和是如何运作的?
在TCP和UDP中都有使用校验和机制来检测传输过程中是否发送了错误或损害。
校验和是一种简单的差错检验技术,它是通过数据包中的字节进行书写运算生成一个校验和值,接收方可以使用这个值来验证数据包的完整性。
发送方计算校验和:在发送数据包之前,发送方将数据包中的所有字节相加,然后取其反码(按位取反)。这个反码值就是校验和值,它附加在数据包的头部。
接收方验证校验和:接收方在接收到数据包后,将数据包中的所有字节再次相加,然后取其反码。然后,接收方将这个反码与数据包中附加的校验和值进行比较。如果两者相等,表示数据包没有发生错误或损坏;如果不相等,表示数据包在传输过程中发生了错误。
什么是真正的可靠?
在进行网络通信的过程中,一方发出数据后,它不能保证该数据是否会被对端接受,因为在实际生活中,可能会出现各种各样的问题,因此需要对端回复所谓的“响应信息”后,发送端才能确保对端是接受到自己的信息。这也就是我们所说的可靠。
但是我们会发现一个问题,那就是上述场景中,对端主机如果想知道自己发送的响应信息是否被发送端所接收,那么就需要发送端在发送一条响应信息,这样就会进入一个死循环......
所以:在严格意义上来说,互联网通信的过程中是不存在百分比的可靠传输的,因为总有一条消息得不到所谓的响应。其实实际上并不需要保证每条消息的可靠性,只需要对核心消息做出保证即可,对于非核心信息(比如响应数据),如果其无法发送到对端,发送端可以进行重传操作。(重传后信息再次丢失的概率就极低了)。
确认应答机制是TCP协议保证传输可靠性的最核心机制
上述所描述的场景就说TCP当中的确认应答机制,确认应答的关键就是发送方发送数据给接收方后,接收方会自动返回一个响应表示收到数据了。
而其中的应答报文,也称之为ack(acknowledge)报文,顾名思义,ack报文中标志位ACK位为1.普通报文ACK位为0.
在数据传输的过程中,可能会出现后发先至的情况,这是当下的网络结构操作造成的,无法避免,解决方案就是使用序号和确认序号。
序号(32位):针对请求数据进行的编号
确认序号(32位):只针对ACK报文有效
将上述过程进行编号,就不会产生歧义了:
我们知道,TCP是面向字节流的,实际中传输过程中,是针对每一个字节进行编号,比如传输数据的序号是1,发送了1000个字节的数据,那么接收方收到数据后会返回一个1001,表示1001之前的数据以及全部收到了。
发送的数据:
传输过程:
我们知道确认应答机制,会让接受端在收到消息时返回一个应答报文(ACK),可是这个应答报文在传输的过程中可能会出现丢包的情况。
发生超时重传的两种情况:发送端数据丢失和应答报文丢失(ACK);
特定时间间隔:
这里的时间间隔是由系统内部的配置项决定的,描述的是超时重传需要等待的时间,例如第一次出现丢包,发送方就会在达到超时时间阈值后,进行重传,如果仍无响应,那么就会继续重传,当然,第二次的等待重传时间会比第一次长。
注:这里的重传,并非是无止境的,一般第二次重传就会成功到达(两次都发生丢包的概率太低了),如果第二次重传未成功,可能说明当时网络情况很差,后续可能会进行重置TCP连接(断开重连)的操作,甚至是释放连接(彻底放弃)。
TCP的连接不是物理上的,而是逻辑上的,举个栗子:
如果主机A和主机B建立连接,那么在主机A和主机B的系统内核中,会分别记录了一个数据结构,在其中包含了与其建立连接对象的信息(IP地址,端口号等)。
在正常情况下,TCP要经过三次握手建立连接,四次挥手断开连接
由下图可知;标志位中的SYN为1,说明就是一个同步报文段。
为什么需要建立连接以及建立连接的意义是什么?
因为TCP是全双工通信的,因此建立连接的目的是投石问路——检查当前的网络情况是否是畅通的,需要注意的是三次握手建立的连接并不传输任何业务数据。
但是会传输一些重要数据,如窗口大小,下文会提到。
三次握手同时是可以检查通信双方的 发送能力 以及 接收能力 是否是正常的。
为什么是三次握手?两次握手行不行?
很显然,三次握手才是最优解,因为只有三次握手后,才能验证通信双方可以正常的接收和发送数据(同时三次握手恰好是验证双方通信信道的最小次数)。
另外,三次握手其实还有一个好处,就是可以让连接异常时,异常挂在客户端这里,通过上述描述我们可以得知,服务器和客户端双方各自建立连接的时间点是不同的,
如果客户端发出的第三次握手丢包了,那么服务器就不会与其建立连接,而客户端就要对这个现象进行维护(需要耗费时间成本和空间成本),如果出现丢包,难免会耗费时间和空间成本,但是如果发生在客户端就会好点,因为如果是服务器的话,一旦出现多个失效情况,那么成本可能就会翻倍。
三次握手时的状态:
通信双方断开连接(取消相互之间的认同关系),
通信双方各自向对方申请断开连接,再各自给对方回应。
需要注意的是,这里从服务端发送的ACK和FIN没有像三次握手中的ACK和SYN一样是合并到一个报文中发送的,因为在四次握手中从 服务端发送出去的ACK 是操作系统内核收到来自 客户端的FIN 后立即触发的,而 服务端发送的FIN 则是显示的调用 应用程序中的socket的close 方法触发的。
四次挥手时的状态
因为服务器收到客户端断开连接的请求时,可能还有一些数据没有发完,这时先回复 ACK,表示接收到了断开连接的请求。等到数据发完之后再发 FIN,断开服务器到客户端的数据传送。
以上就是四次挥手的全部状态。
回顾一下刚才的确认应答机制,其内容是发送方对自己每一个发送的数据段,都需要接收端返回一个ACK确认应答后才能继续发送下一条数据。
这种做法虽然在一定程度上保证了数据的可靠性,但是呢,却有一个缺点,就说性能较差,尤其是返回的ACK需要等待很长时间的时候。
设计TCP协议的人当然想到了这一点,所以引入了滑动窗口这一机制来提高发送的效率,但是其实有点属于亡羊补牢,其效率再高也没用UDP协议高。
既然一条一条发送数据效率太低,发送端先发送一波信息,然后等待接收端回应一波ACK,以此循环:
需要注意的是发送方并不需要等待所有的ACK都传回来之后再进行发送,而是传回来一个ACK后,就继续发送下一条数据。
现在对上图进行说明:
为了更好的理解窗口这一概念,请看下图;
此时等待的数据段仍是4条,在视觉上,看起来窗口的大小没变,但是却向右边移动了。因此我们把这种现象叫做 滑动窗口。
Tips:窗口越大TCP传输的效率就越高,反之则越小,假设窗口是无穷大,那么此时的发送方就完全不需要等待接收端传回的ACK,这时TCP的效率就跟UDP一样了,但是假设终究是假设,实际上是不可能完成的。
上述我们讨论的情景都属于正常情况下,网络情况错综复杂,当出现丢包/乱序的情况下应该如何处理呢?
丢包分为两种情况;
针对情况1,ACK丢了,其实完全不需要作任何事,因为就算ACK丢包率达到了百分之50,也无需担心,不用每一个丢失的ACK都进行重传(如果刚好是最后一个ACK丢了,那么正常进行超时重传操作就好),因为2001这个ACK代表的是接收了1~2000字节的数据,已经涵盖了刚刚1001这个ACK所包含的内容,6001这个ACK代表的是接收了1~6000字节的数据,也涵盖了在这之前丢失的ACK。
针对情况2,传输的数据包丢了;
前面丢失的是1001~2000,而2001~7000的数据并未丢失(主机B都受到了),当A重传之后,主机B的7000之前的数据就补齐了,后续主机B只返回了7001这个ACK,并无返回2001,3001这些ACK,这种不拖泥带水的操作称之为快速重传——搭配滑动窗口的超时重传
并不是有了快速重传之后,超时重传就毫无用武之地,如果传输的数据很多,当然是遵守快速重传的方式,如果传输的数据很少(只有一条),那么就需要按照超时重传的方式进行。
例如刚刚传输多条数据的过程中,刚好最后一个数据段丢失了,那么就按照超时重传的方式进行,
可以这样理解:只要出现丢包的情况,就要进行超时重传,快速重传其实只是超时重传的一种特殊情况。
我们知道,窗口越大,发送的速度越快,但是如果窗口太大,导致接收方没有能力接收这些数据,会起到反效果,可能会造成丢包等一系列操作,
TCP是可靠的,就会对这些数据进行重传,这犹如雪上加霜般,对接收端将是很不好的体验,TCP根据接收端的处理能力(由于应用程序从接收端抽取数据的速率不好衡量,于是是用接收方缓冲区的剩余容量来衡量接收方处理能力),来决定发送端的发送速度,也就是说其实流量控制是对发送方的速度进行控制。
主机A第一次向主机B发送数据的时候如何得知窗口大小?
回忆TCP首部中,有一个16位窗口字段,就是存放窗口大小信息的,在TCP三次握手的阶段,除了验证双方通信是否畅通之外,还会进行其他信息的交互:接收端就会在ACK中将窗口大小反馈给发送方,因此当我们第一次发送数据的时候,就不会出现窗口溢出的情况。
tips:窗口的大小根据网络状况的优劣以及其他因素是实时变化的。
当缓冲区满了,主机A会停止发送数据,那后续主机A是如何得知可以继续发送数据呢?
窗口字段是16位大小,是否意味着窗口最大就是65535呢?
当然不是这样,在TCP报文的”选项“字段中,包含了一个窗口大小扩展因子M,而实际的窗口大小是窗口字段的值左移M位得到的。
拥塞控制的存在意义?
为了防止和流量控制混淆,以下做出区分:
当流量控制和拥塞控制下产生的窗口大小不同时,听命于谁?
:听命于窗口小的那一方。
拥塞控制
虽然TCP协议中有滑动窗口这么一个厉害的方法能一次性发送大量数据,但是现实中可能没有那么美好,因为网络环境的错综复杂,时好时坏的表现,可能会让我们出现大量丢包的情况,
为了避免这种情况,TCP引入了 慢启动 这一概念,先发少量的数据,探探路,摸清当前的网络拥堵状态,再决定按照多大的速度传输数据,如果后续再出现丢包的情况,那么窗口就再变小,反复调整。
由图中所示:
总结:
少量的丢包,我们仅仅是触发超时重传;大量的丢包,我们就认为网络拥塞;
当TCP通信开始后,网络吞吐量会逐渐上升;随着网络发生拥堵,吞吐量会立刻下降;
拥塞控制,归根结底是TCP协议想尽可能快的把数据传输给对方,但是又要避免给网络造成太大压力的折中方案。
当接收缓冲区满了,TCP 协议采用一种流量控制的机制来防止数据的丢失和过载。这是通过以下方式之一来处理的:
通知发送方减速(流量控制):
丢弃数据(缓冲区溢出):
拥塞控制:
总体而言,TCP 通过这些机制来确保在网络通信过程中的可靠性和稳定性。接收方可以通知发送方降低发送速率,或者在极端情况下选择丢弃数据以防止缓冲区溢出。这些机制有助于TCP适应网络的变化,维持整个通信系统的稳定性。
概念:这是一种基于 流量控制 而提高效率的机制,简单来说,延迟应答是流量控制的延申,流量控制的目的是为了使发送方不要发送的太快,而延迟应答在此基础上,想让窗口的大小尽量大一些。
延迟应答的出现,就是为了让传输的效率变高,因为窗口越大,对应着网络吞吐量越大,我们的目的就是在保证网络不堵塞的情况下提高传输的效率。
那么所有的包都可以延迟应答么?肯定也不是。
在延迟应答的基础上,我们发现,很多情况下,客户端服务器在应用层也是 "一发一收" 的。意味着客户端给服务器说了 "How are you",服务器也会给客户端回一个 "Fine, thank you";
那么这个时候ACK就可以搭顺风车,和服务器回应的 "Fine,thank you" 一起回给客户端(所以在一定条件下,是可以让四次挥手变成三次挥手的)。
为什么会出现粘包问题?
需要注意的是:
首先要明确,这里面所说的包是指应用层的数据包。
该如何避免这种粘包的问题呢?
归根结底:明确包的长度。
对于UDP协议来说,是否也存在 "粘包问题" 呢?
主要分为以下几种情况,进程崩溃,正常关机,主机掉电,网络断开
进程崩溃
进程崩溃也称进程的异常退出,操作系统会回收进程的资源,其中包括释放文件描述符,这样的操作就相当于调用socket的close方法,执行close就会触发FIN报文,进一步的开始四次挥手。
需要注意的是:这种情况和普通的四次挥手没有什么区别。
正常关机/机器重启(通过 开始菜单 这种方式来关闭主机)
关机的时候或者计算机重启的时候,系统会强制结束所有的用户进程,和上述的那个进程崩溃类似,系统内核会进行文件描述符的释放操作,进一步的进行四次挥手。
主机掉电
分为两种情况,一种为掉电的是接收方,另一种为掉电的是发送方。
当掉电是接收方的时候,发送方是不知道接收方断电了,还会继续发送数据,此时发的数据没有,接收方无法返回ACK了。发送方此时会触发超时重传,重传了几次之后,仍然无法连接。
此时发送方会重置连接(发送复位报文段,RST为1),发现还是无法连接时,发送端就放弃连接了。
当掉电的是发送方的时候,此时接收方并没有干等着,会在一个时间周期后,发送一个“心跳包”。(心跳包是周期性触发的,只是一个简单的不携带任何业务数据的包,该包存在的意义就是确认一下对方是否还在)
tip:这里的心跳包虽然和探测报文的功能很像,但是两者并非同一个东西。
如果对方不反回一个心跳包的话,说明对方就挂了,于是便放弃了连接。
网线断开
该情况与主机掉电是相同的,只不过这时通信的双方分别按照上述(发送方,接收方)进行着。
另外,应用层的某些协议,也有一些这样的检测机制。例如HTTP长连接中,也会定期检测对方的状态。例如QQ,在QQ断线之后,也会定期尝试重新连接。
为什么TCP这么复杂?因为要保证可靠性,同时又尽可能的提高性能。
可靠性:
提高性能:
基于TCP应用层协议
当然,也包括你自己写TCP程序时自定义的应用层协议。
下面来看一道常见面试题:
如何使用UDP来实现可靠传输
其实考察的是TCP,只要引入TCP中的可靠机制即可,如校验和,序列号(按序到达),确认应答等等。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。