UDP和TCP特征的详解
1.再次回顾UDP和TCP的区别
UDP:无连接,不可靠传输,面向数字报,无发送缓存区,有接收缓存区,大小受限制64kb,全双工。
TCP:有连接,可靠传输, 面向字节流,有发送缓存区,有接收缓存区,大小不受限, 全双工。
我们接下来针对他们的特性细细解答
UDP
1.UDP的定义格式
无连接:UDP只要知道对方的IP地址和端口号,无需连接就可以发送信息。类似于发短信。
不可靠传输:UDP没有确认机制也没有重传机制,发送出去的信息如果因为网络故障或者其他原因导致,消息没有成功传达,UDP不会给出任何错误的应答。
面向数据报:UDP不会数据大小的控制,应用层给传输层多少数据(不超过64kb),UDP都会原封不动的一次性发送给接收方,比如有一百个字符,UDP只会一次性发送完,不会拆分为十个字符十个字符的形式循环发送。如果数据超过了64kb,发送就要手动分包,接收方就要手动的拼接的包。
2.TCP
TCP的特性体现主要是因为有一下机制:
应答机制:
因为TCP是字节流传输形式,所以可以把数据拆分为一小段一小段循环传输,TCP在每段数据都进行了编号,也就是序列号
主机A想主机B会根据TCP拆分的数据块,一段一段发送,每发送一小段,主机B接收到,就会向主机A发送一个ACK确认应答一下,这个AUK也携带着下一次主机要发送的数据块的序列号,告诉主机A我已经接收了那些数据,下一次应该从哪里发送。
总结:应答机制:TCP会给每段字节流数据进行编号,也就是序列号,发送发向接收方发送一段字节流数据后,接收方如果接收到字节流数据,就会给发送发发送一个ACK应答,这个ACK的应答的作用就是告诉发送发,我已经接收了那些数据,下次你发数据时候,应该从哪里发送。
补充:这里注意一下,应答和响应的区别。响应是只针对请求的内容,做出了一系列请求的反馈,应答相当于一个标记,并不是真实的结果。
超时重传机制
超时重传分为两种情况:
第一种:主机A发送的数据并没有到达主句B,也就是我们常说的丢包情况。
第二种:主机A发送的数据主机B接收到了,但是主机B发送的ACK应答主机A并没有接收到,这就是ACK丢失。
第一种情况,丢包的超时重传机制的处理:
主机A在发送数据给B之后,可能因网络原因或则其他原因造成数据没有被主机B接收到。主机A会在一个特定的时间等待主机B的ACK应答,如果超过这个特定的时间,主机A会重新把数据再发送给主机B一次。
第二种情况:丢失ACK的超时重传机制处理:
主机A在发送数据给主机B后,主机B接收到了主机A的数据,也发送ACK应答,但是ACK应答未到达主机A。主机A在特定时间没有接收到ACK应答就会重新发送一份数据给主机B,主机B发现这次接收的数据序列号和上一次的一样,是同一份数据,就会把上次的数据丢失掉,接收这次的数据,然后重新发送ACK。
提问:我们如何确定超时时长,如果一直超时重传怎莫办呢?
理想情况下,是找到一个最短的时间,保证应答在这个时间内一定返回。但是这个时间会因为网络的波动,产生差异。如果超时时间过长就会影响整体传输效率,如果超时时间太短,会频繁发送重复的包,但是TCP在任何情况下都能保证高性能的通信,因为会动态计算这个最大的超时时间。比如在Linux中,会以超时500ms为一个单位,每次判定超时重传的时间都是500ms的整数倍。第一次是500ms第二次是1000ms,第三次是3*500等等…也不必担心一直超时重传,因为会由重传次数,如果超过了个次数就会强制关闭连接。
总结:超时重传机制,超时重传机制会面对两种情况:一种是丢失数据包,一种是丢失ACK应答包,对于前一种,主机A会在超时之后,重新给主机B发送一份数据。对于后一种情况,主机A会在超时之后,重新给主机B发送一份数据,主句B会根据序列号,判断这份数据是否重复,然后丢掉重复的数据,并返回ACK应答,对于超时时间的定义,是动态计算出来的。也不用担心会一直超时重传,重传一定次数后,就会强制断开连接。
连接管理(可靠性机制)三次握手四次挥手
三次握手:初次建立连接时候
作用:确保了全双工正常,也就是发送端和接收端的发送和接收功能都正常。
过程:第一次握手:发送端发送一个SYN,发送端的状态变为了SYN_SENT状态。第二次握手,接收端接收到了发送端的SYN后,向发送端发送一个SYN和ACK,接收端的状态变为了SYN_RCVD,第三次握手:发送端接收到了接收发送个的SYN和ACK后,向接收端发送一个ACK,发送端变为了ESTABLISHED,接收端接收到发送端发送的ACK后也变为零ESTABLISHED,就完成了建立
思考:可以变成四次握手或者两次握手吗?
两次是不可以的缺少了验证接收端的发送能力和发送方的应答能力。
四次是可以的。
四次挥手:确保客户端和服务器的断开。
过程:第一次挥手,客户端向服务端发送一个FIN,客户端的状态变为FIN_WAIT_1,第二次挥手,服务端接收到了客户端的FIN后,向服务端发送一个ACK,服务端状态变为了ClOSE_WAIT,客户端接收到ACK后状态变为了FIN_WAIT_2,第三次挥手,服务端向客户端发送一个FIN,服务端状态变为了LAST_ACK,第四次挥手客户端接收到了服务端发送的FIN后,状态变为了TIME_WAIT,向服务端发送了ACK,服务端接收到ACK之后,状态变味了CLOSED,客户端等待一段时间后,状态变味了CLOSED
为什么不是三次挥手呢?
因为FIN和ACK发起的时机不同,ACK应答比较及时,FIN要回收一些资源后才能触发。一个是操作系统层面的,一个是应用程序层面的。
TIME_WAIT
想⼀想, 为什么是TIME_WAIT的时间是2MSL
MSL是报文存在的最大时间,因此TIME_WAIT持续窜存在2MSL的话,保证了两个传输方向的未被接收的和迟到的报文已经消失,假设没有TIME_WAIT,A客户端发送了一个数据,立马结束,服务器立即启动后,就可能接收到这个数据,但是并不知道这个数据是谁发送的,也没办法做出响应。
同理:同时也是在理论上保证最后⼀个报⽂可靠到达(假设最后⼀个ACK丢失, 那么服务器会再重发⼀个FIN. 这时虽然客⼾端的进程不在了, 但是TCP连接还在, 仍然可以重发最后一个ACK保证正常的断开连接。
CLOSE_WAIT
⼀般⽽⾔,对于服务器上出现⼤量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭 socket, 导致
四次挥⼿没有正确完成. 这是⼀个 BUG. 只需要加上对应的 close 即可解决问题.
滑动窗口
对比一下:前面的应答机制,是一对一模式下的,客户端发送一个数据后,必须接收到服务端发送的ACK确认应答后,才能发送下一条,这样做的最大缺点就是,性能比较差,返回ACK这段时间大幅度浪费,极端情况一点,如果我们1001到2000的数据因为网络波动好几次发送不过去,就会导致后面的数据连发送的机会都没有,导致数据的阻塞。
为了提高性能,我们一次性发送多条数据,把几条数据的ACK确认应答时间重叠到一块
滑动窗口的过程。
既然我们一发一收的效率太低,发送方一次性发四条数据,当接收方收到这四条数据后会返回对应的ACK,发送方接收到第一个ACK后应答,就把滑动窗口往后移动,继续发送第五个段的数据,这里操作系统为例维护滑动窗口,还专门开辟了一个发送缓冲区,来记录那些数据没有被应答,只有确认应答的过的数据,才会从缓冲区种删除。
缓存区:
那如果出现丢包现象怎么办?
情况1:数据包已经被接收,丢掉了ACK。
这种情况,ack丢失并不要紧,因为可以通过后续的ACK确认。
情况2:数据包丢了
如果中间那段数据丢失,接收端会发送三次重复确认应答,让发送端重新发一份丢失的数据。接收方接收到丢失那段数据后,再次返回的ACK就是7001了,因为2001-7000这段数据已经被放入了接收端的操作系统的接收缓存里面了。
流量控制
为什么要流量控制?
接收方接收数据的速度是有限的,如果发送方速度太快,就会把接收方的缓存打满,这时候如果发送发还发送数据,就会出现丢包的现象。因为TCP让接收方来处理发送方发送的速度。这就是流量控制
接收端会根据自己缓存的小可向发送方反馈一个窗口大小信息,把这个信息放进ACK应答中,通过ACK应答告诉发送端,下一次发送送端会根据这个窗口大小发送数据,如果接收端通知给发送端窗口大小暂时为0的话,这时候发送端会停止发送数据,超过重发时间后,发送端会发一个窗口探测包,获取一下接收方的窗口大小信息,如果不为0会根据窗口大小的来限定发送数据的速度。
拥塞控制
了解拥塞控制之前我们要先再次回顾一下网络的传输机制。
我们刚开始发送数据时候,是不知道网络的状况,如果网络状况不加,我们贸然发送大量数据,就会造成网络堵车,跟春节期间的告诉公路一样,就可能越发送越堵,原本小量数据可以正常传输,但是因为大量的数据直接造成网络瘫痪得不偿失。
所以TCP有着慢启动机制:先发少量数据探探路,看看当前的网络情况。
发送端创建一个拥塞窗口,窗口大小为较小的值.每次接收到ACK时候拥塞窗口的大小都会增加,每次发送数据时候,拥塞窗口都会与接收端主机反馈的窗口大小作比较,取较小的值作为实际发送窗口。当拥塞窗口值等于大于接收端主机反馈的窗口大小时候,就会以接收端反馈的窗口大小为准传输数据,当突然网络产生波动,拥塞窗口会缩小大概一般,然后继续去探路,寻找一个较为合适的数据传输大小避免网络堵塞。
作用:拥塞控制就是为了既能保证数据快速的正常传输,又要避免对网络过大的压力造成网络堵塞。
延迟应答
了解延迟应答之前,我们先了解一下数据读取的过程。
发送端发送的数据并不是直接到用户态的,发送端发送的数据都被放在接收端的缓存区里面,用户态读到那段数据,那段数据才会被在缓存区中删除。
为什么延迟应答。
如果接收端接收到数据后立刻ACK应答,这时候返回的窗口大小较小,因为数据刚放入缓存区,还没有被用户态读入。如果我们延迟一会,等用户态读入一部分数据后,再应答这时候,返回的窗口大小就会大一些,提高了网络的吞吐量,也加快了数据的传输。
所以我们每个包都延迟应吗?当然不是,如果每个包都延迟应答,就会增加应答时间,降低效率,每N个包延迟应答一次是最好的。具体根据操作系统来规定的。
捎带应答
由于TCP是全双工,所以既可以接收也可以发送,如果需要反馈一个ACK,这时候如果也要响应一条数据,两个时间机会是同时,就可以把这个响应交给ACK捎带着一块返回给接收端,类似于三次握手,把SYN和ACK合并到一块。
字节流
TCP是以字节流的形式传输数据的,创建socket时候,内核会创建一个发送缓存区和接收缓存区,接收到的数据都会放到缓存区里面,转化为字节的形式存储着。
粘包问题
为什么会发生粘包问题,对于传输层,接收到缓存区的数据就是一条一条字节数据,但是传输到应用层,看到的是一串连续的字节数据,应用层看到这层数据,并不知道从哪里开始从哪里结束,这就是粘包问题。
对于粘包的解决方案,也很简单,就是明确两个包之间的边界,对于定长的包,保证每次固定读取的大小。对于不定长的包,可以再包头约定一个包总长的长度,根据这个总长长度知道包结束的位置,或者说是,包与包之间使用明确的分隔符(应用层自己定义)
UDP是否存在粘包问题?
对于UDP是存在明确的报文长度的(固定大小),是一个包一个包的交给应用层,使用UDP报文时候,只能是一个完整的包,不会出现半个包这类情况。
异常情况
程序崩溃:系统会回收进程资源,回收相当于socket调用closse(),触发FIN四次挥手
正常关机:处理方式和程序崩溃一样,都是正常断开连接。
主机掉网,掉电,断网:
接收方断网:发送方会超时重传,多次未收到应答后,会重置连接。连接错误就放弃连接。
发送方断网:类似于车载系统,车辆每隔几分钟都会向服务器发送一条信息,表示车辆和服务器正常建立连接,如果长时间服务器没有收到车载信息,就视为断开连接。
总结:
TCP一定比UDP好吗?
其实根据不同场景各自有优势.
TCP适合用于可靠传输情况,如文件传输,重要状态更新的场景。
UDP适合于高速传输和实时性,早期的广播,QQ,视频等