TCP、UDP详解

目录

1.区别

[1.1 概括](#1.1 概括)

[1.2 详解](#1.2 详解)

2.TCP

[2.1 内容](#2.1 内容)

[2.2 可靠传输](#2.2 可靠传输)

[2.2.1 确认应答](#2.2.1 确认应答)

[2.2.2 超时重传](#2.2.2 超时重传)

[2.2.3 连接管理](#2.2.3 连接管理)

三次握手

四次挥手

[2.2.4 滑动窗口](#2.2.4 滑动窗口)

[2.2.5 流量控制](#2.2.5 流量控制)

[2.2.6 拥塞控制](#2.2.6 拥塞控制)

[2.2.7 延时应答](#2.2.7 延时应答)

[2.2.8 捎带应答](#2.2.8 捎带应答)

[2.2.9 面向字节流](#2.2.9 面向字节流)

[2.2.10 异常情况的处理](#2.2.10 异常情况的处理)

1.进程崩溃

2.主机关机(正常关机)

3.主机掉电(非正常)

4.网线断开

3.对比


TCP和UDP是传输层的两个重要协议,也是面试中经常会被问到的,属于面试高频点。今天,我们来学习这两个协议。

1.区别

1.1 概括

TCP:有连接,可靠传输,面向字节流,全双工

UDP:无连接,不可靠,面向数据报,全双工

1.2 详解

1.TCP是有连接的,UDP是无连接的

连接是抽象的概念,本质上是建立连接的双方,各自保存对方的信息

两台计算机建立连接,就是彼此保存了对方的关键信息

TCP想要通信,就需要建立连接,做完之后,才能后续通信。至于连接如何建立,不需要代码干预,是系统内核自动负责完成的。

对于应用程序来说,客户端这边,主要是要发起"建立连接"动作;服务器这边,主要是把建立好的连接从内核中拿到应用程序里~

如果有客户端,和服务器建立连接,这个时候服务器的应用程序是不需要做出任何操作(也没有任何感知的),内核直接就完成了,建立连接的过程(三次握手),完成流程之后,就会在内核的队列中(这个队列是每个serverSocket都有一个这样的队列)排队。

应用程序要想和这个客户端进行通信,就需要通过一个accept方法,把内核队列已经建立好的连接对象,拿到应用程序中。

即一个典型的生产者-消费者模型。

UDP想要通信,直接发送数据即可,不需要征得对方同意,UDP自身也不会保存对方的信息

2.TCP是可靠传输,UDP是不可靠传输的

网络上进行通信,A-> B发送一个消息,这个消息是不可能做到100%送到的

TCP就内置了可靠传输机制,UDP就没有内置可靠传输。

A -> B 发消息,消息是不是到达B这一方,A自己能感知到,进一步的,就可以在发送失败的时候采取一定的措施(尝试重传之类的)

可靠传输需要付出代价:a.机制更复杂 b.传输效率会降低

3.TCP是面向字节流的,UDP是面向数据报的

TCP和文件一样,以字节位单位来进行传输

UDP则是按照数据报为单位,来进行传输的(UDP数据报是有严格的格式的)

4.TCP和UDP都是全双工的

一个信道,允许双向通信,就是全双工

一个信道,允许单向通信,就是半双工

2.TCP

2.1 内容

2.2 可靠传输

常见面试题:

TCP是如何保证可靠传输的?

通过确认应答为核心,借助其他机制辅助,最终完成可靠传输

2.2.1 确认应答

发送方,把数据发给接收方之后,接收方收到数据就会给发送方返回一个应答报文

发送方,如果收到这个应答报文了,就知道自己的数据是否发送成功

2.2.2 超时重传

如果网络传输过程中,出现丢包了,发送包,势必就无法收到ACK了

由于丢包是一个"随机"的事件,因此在TCP传输过程中,丢包就存在两种情况:

1.传输的数据丢了

2.返回的ACK丢了

无论出现上述两种情况,发送方都会采取统一的措施,就是"重传"

发送方,何时重传?等待时间

发送方,发出数据之后,会等待一段时间,如果这个时间之内,ack来了,此时就自然视为数据到达

如果达到这个时间之后,数据还没到,就会触发重传机制~

超过等待时间,再重传~

第二次重传后的等待时间会比第一次时间延长,但延长也不是无限制延长,重传若干次后,时间拉长到一定程度,认为数据再重传也没有用了,就放弃tcp连接(准确来说就是会触发tcp的重置连接操作)

TCP会有一个"接收缓冲区"就是一个内存空间,会保存当前已经收到的数据,以及数据的序号

接收方如果发现,当前发送方发来的数据,是已经再接收缓冲区中存在的(收到过的重复数据了),接收方就会直接把这个后来的数据给丢弃掉,确保应用程序进行read的时候,读到的是只有一个数据

2.2.3 连接管理

建立连接 + 断开连接

面试中最经典的问题:三次握手 + 四次挥手

建立连接

三次握手

tcp这里的握手,也就是给对方传输一个简短的,没有业务数据的数据包,通过这个数据包,来唤起对方的注意,从而触发后续的操作(握手这个操作,不是TCP独有的,甚至不是网络通信独有的,计算机中很多的操作,都会涉及到握手)

TCP的三次握手,TCP在建立连接的过程中,需要通信双方进行"打三次招呼"才能够完成连接建立的

问:三次握手是要解决什么问题?

答:三次握手核心作用一:

投石问路,确认当前网络是否是畅通的

三次握手核心作用二:

能够发送方和接受党都能确认自己的接收能力和发送能力均正常

三次握手核心作用三:

让通信双方,在通信过程中,针对一些重要的参数,进行协商

问:四次握手可以吗?两次握手可以吗?

答:四次:可以,但没必要

两次:不可以,不能达到双方都知道信息的过程

断开连接

四次挥手

为什么中间两次不能像建立连接一样合并为一步?

不一定

不能合并的原因:ACK和第二个FIN的触发时机是不同的

ACK是内核响应的,B收到FIN,就会立即返回ACK

第二个FIN是应用程序的代码触发,B这边调用了close方法,才会触发FIN

是否意味着,如果这这边代码close没写/没执行到,是不是第二个FIN就一直发不出去?(有可能的)

如果是正常的四次挥手,正常的流程断开的连接

如果是不正常的挥手(没有挥完四次),异常的流程断开连接(也是存在的)

三次握手ACK和第二个syn都是内核触发的,同一个时机,可以合并

可以合并的情况:TCP还有一个机制,延迟应答,能够拖延ACK的回应时间,一旦ACK滞后了,就有机会和下一个FIN一起合并

哪一方,主动断开连接,哪一方就会进入TIME_WAIT

TIME_WAIT状态的主要存在的意义,就是为了防止最后一个ACK丢失,留下的后手

如果最后一个ACK丢了,站在B的角度,B就会触发超时重传,重新把刚才的FIN给传一遍

如果刚才A没有TIME_WAIT状态,就意味着A这个时候就已经真的释放连接了,此时重传的FIN也就没人能处理,没人能返回ACK了,B也就永远收不到ACK了

A这边使用TIME_WAIT状态进行等待,等待的这个时间,就会为了处理后续B重传的FIN

此时如果有重传的FIN来了,就可以继续返回ACK了,B这边的重传才有意义

TIME_WAIT等待多久呢?

假设网络上两个节点通信消耗的最大等待时间为MSL,此时的TIME_WAIT的时间就是2MSL (已经是上限了,绝大部分的数据包不会达)

2.2.4 滑动窗口

提高效率

TCP的可靠传输,是会影响传输的效率的

滑动窗口,就让可靠传输性能的影响,更小一些

TCP只要引入了可靠传输,传输效率是不可能超过没有可靠性的UDP的

TCP这里的"效率机制"都是为了让影响更小,缩小和UDP的差距

批量传输数据,不等ack回来,直接再发下一个数据

批量传输,也不是"无限的"传输

批量传输也是存在一定的上限的,达到上限之后,再统一等待ack

不等待的情况,批量最所发多少数据,这个数据量,称为"窗口大小"

当前A->B是批量的发了四份数据

此时B也要给A回应四组ACK,此时A已经达到窗口大小,再收到ACK之前,不能继续往下发了

需要等待有ACK回来了之后,才能继续往下发。

可以不需要一次等待四个ACK全部回来之后才能继续发,而是回来一个ack,就立即继续发一个

窗口越大,等待的ack越多,此时传输效率也就越高

情况一:ack丢了

这种情况,不需要任何重传,没事了

确认信号,表示的含义是,当前序号之前的数据,已经确认收到了

下一个你应该从确认序号这里,继续发送

例如:如果1001这个ACK丢了,但是2001ACK到了,则证明2001之前的数据都已经确认传输成功了,涵盖了1001的情况

情况二:数据包丢了

主机A就需要知道是哪个数据丢了,主机B也就得告诉A是哪个数据丢了。

主机A看到了B这边连续的几个ack,都是再索要1001,A就知道了,1001这个数据就是丢了,就重传了1001

1001-2000重传之后,顺利到达B索要的就是7001

上述的重传过程,并没有额外的冗余操作,哪个数据丢了,就重传哪个,没丢的数据就不需要重传,整个过程都是比较快速的

如果通信双方,传输数据的量比较小,也不频繁,就仍然是普通的确认应答和超时重传。

如果通信双方,传输数据量更大,也比较频繁,就会进入到滑动窗口模式,按照快速重传的方式处理。

通过滑动窗口的方式传输数据,效率是会提升的

窗口越大,传输效率就越大(一份时间,等待的ack更多了,总的等待时间更少了)

当然,滑动窗口也不是设置越大越好

如果传输的速度太快,就可能会使接收方,处理不过来了,此时,接收方也会出现丢包,发送方还得重传。

2.2.5 流量控制

站在接收方的角度,反向制约发送方的传输效率

考虑接收方的处理能力

发送方发送的速率,不应该超过接收方的处理能力

可以根据缓冲区剩余空间大小来判断消费者处理速度

把剩余缓冲区返回给发送方,发送方根据改缓冲区大小发送数据

窗口探测包:并不携带具体的业务数据,只是为了触发ack,为了查询当前接收方这边的接收缓冲区剩余空间。

2.2.6 拥塞控制

不仅仅是接收方,还有整个通信的路径

关键问题:接收方的处理能力,很方便进行量化

但是中间节点,结构更复杂,更难以直接的进行量化,因此可以通过"实验"的方式,来找到一个合适的值

让A先按照比较低的速度(小的窗口)来发送数据

如果数据传输的过程非常顺利,没有丢包,再尝试使用更大的窗口,更高的速度进行发送(一点一点变化)

随着窗口大小不停的增大,达到一定的程度,可能中间节点就会出现问题了,此时这个节点就可能出现丢包

发送方发现丢包了,就把窗口大小调整小,此时如果还是继续丢包,就继续缩小,如果不丢包了,就继续尝试变大

在这个过程中,发送发不断调整窗口大小,逐渐达到"动态平衡"

最终时机发送的窗口大小,是取流量控制和拥塞控制中的窗口的较小值

2.2.7 延时应答

A把数据传给B,B就立即返回ack给A(正常)

也有时候,A传输给B,此时B等一会再返回给ack给A(延时应答)

本质上也是为了提升传输效率

延时返回ack,给接收方更多的时间,来读取接收缓冲区的数据

此时接收方读了这个数据之后,缓冲区剩余空间,变大了,返回的窗口大小也就更大了

2.2.8 捎带应答

在延时应答的基础上,进一步的提升效率

网络通信中,往往是这种"一问一答"这样的通信模型

ack也是内核立即返回的,response则是应用程序代码来返回的,这两者的时机是不同的

由于tcp引入了延时应答,上面的ack不一定是立即返回,可能要等一会

在等一会的过程中,B就刚好把response给计算好了,计算好了之后,就会把response返回,与此同时顺便就把刚才要返回的ack也带上了,两个数据就合并成了一个数据,此时就可以得到更高效的效果

2.2.9 面向字节流

这里有一个最重要的问题,粘包问题(不是tcp独有的,而是面向字节流的机制都有类似的情况)

此处"包"应用层数据包,如果同时有多个应用层数据包被传输过去,此时就容易出现粘包问题

目前,接收缓冲区中,这三个应用层数据包的数据,就是以字节的形式紧紧挨在一起的

接收方的应用程序,读取数据的时候,可以一次读一个字节,也可以读两个字节也可以读N个字节......

但是最终的目标是为了得到完整的应用层数据包,B应用程序,就不知道,缓冲区里的数据,从哪里到哪里是一个完整的应用数据包了

相比之下,像UDP这样的面向数据报的通信方式,就没有上述问题

UDP的接收缓冲区中,相当于是一个一个的DatagramPacket对象,应用程序读的时候,就明确知道哪里到哪里是一个完整的数据。

如何解决粘包问题?

核心思想:通过定义好应用层协议,明确应用层数据包之间的边界

1.引入分隔符

例如,可以使用\n作为分隔符

aaa\n

bbb\n

ccc\n

2.引入长度

3aaa

4bbbb

3ccc

自定义应用层协议的格式

xml,json,protobuffer,本身都是明确了包的边界的

2.2.10 异常情况的处理

如果在tcp的使用中出现了意外,会如何处理?

1.进程崩溃

(本质上是进程没了,异常终止了。文件描述符表,也就释放了,相当于调用socket.close(),此时就会触发FIN,对方收到之后,自然也就返回FIN和ACK,这边再进行ACK,即正常的四次挥手断开连接的流程)

TCP的连接,可以独立于进程存在(进程没了,TCP连接不一定没)

2.主机关机(正常关机)

进行关机的时候,就是会触发强制终止进程的操作,相当于1,此时就会触发FIN,对方收到之后就会返回FIN和ACK,此时不仅仅是进程没了,整个系统也可能关闭了,如果在系统关闭之前,对端返回的ACK和FIN到了,此时系统还是可以返回ACK,进行正常的四次挥手。但如果系统已经关闭了,ACK和FIN迟到了,无法进行后续ACK的响应,站在对端的角度,对端以为是自己的FIN丢包了,重传FIN,重传几次都没有响应,自然就会放弃连接(把持有的对端的信息就删了)。

3.主机掉电(非正常)

此时,就是一瞬间的事情,来不及杀进程,也来不及发送FIN,主机就直接停机了,站在对端的角度,对端不一定知道这个事情

如果对端是在发送数据(接收方掉电),发送的数据就一直会等待ACK ,触发超时重传,触发TCP连接重置功能,发起"复位报文段",如果复位报文段发过去之后,也没有效果,此时就会释放连接了

RST复位报文段

如果对端是在接收数据(发送方掉电),对端还在等待数据到达.....等了半天没消息,此时其实是无法区分,是对端没发消息,还是对方挂了

TCP中提供了心跳包机制(形象的比喻),即接收方也会周期性的给发送方发送一个特殊的,不携带业务数据的数据包,并且期望对方返回一个应答,如果对方没有应答,并且重复了多次之后,仍然没有,就视为对方挂了,此时就可以单方面释放连接了。

4.网线断开

和主机掉电非常类似

当前A给B发送数据,一旦网线断开

A就相当于会触发超时重传 -> 连接重置 ->单方面释放连接

B就会触发心跳包 -> 发现对端没响应 -> 单方面释放连接

3.对比

TCP和UDP之间的对比

TCP优势在于可靠传输,更适用绝大部分场景

UDP优势在于高效率,更适用于"可靠性不敏感,性能敏感"场景

局域网内部(同一个机房)的主机之间通信

如果要传输比较大的数据包,TCP更优先(UDP有64kb的限制)

如果要进行"广播传输",优先考虑UDP,UDP天然支持广播,TCP不支持(应用程序额外写代码实现)

有一种特殊的场景,需要把数据发给局域网的所有机器,这个情况就是广播

相关推荐
跨境小宇哥1 小时前
如何通过指纹浏览器使用代理IP?
tcp/ip·ip
小哈龙2 小时前
C# HTTP POST(System.Net.WebException:请求已中止:请求已被取消)
网络·网络协议·http
CooperYXQ2 小时前
这一次终于理解了三次握手四次挥手
tcp/ip·https
LeoLei80603 小时前
深入理解TCP协议格式(WireShark分析)
网络·c++·网络协议·tcp/ip·wireshark
孟里啥都有.3 小时前
web基础和HTTP协议
网络协议·http
settingsun12257 小时前
[Cloud Networking] BGP
网络·网络协议
落子摘星8 小时前
netlink通信——读取路由表获取通信网卡IP
网络·网络协议·tcp/ip·netlink
法号:吃肉12 小时前
求推荐几款http可视化调试工具?
网络·网络协议·http
马叔聊跨境18 小时前
如何使用代理 IP 防止多个 Facebook 帐户关联 - 最佳实践
网络·tcp/ip·安全·ip
网恋褙骗八万18 小时前
网络编程:使用udp实现数据文件的接收java
网络·网络协议·udp