Android 开发工程师面试必备佳品之TCP与UDP协议

1,349 阅读11分钟

在面试的时候我们经常会被问道有关计算机网络相关的内容,尤其是TCP与UDP,它的登场率可以说是相当高了。这部分内容是基础并且很重要。所以我们有必要好好的学习一下它。即使不为了面试这也是值得去学习的知识点,因为说不定哪天你就用上它了哈。

一、TCP和UDP的区别

一般我们都会被问到这两个协议的区别,大部分人会回答,TCP 是面向连接的,UDP 是面向无连接的。

那么什么叫面向连接,什么叫无连接呢?

在互通之前,面向连接的协议会先建立连接。所谓的建立连接,是为了在客户端和服务端维护连接,而建立一定的数据结构来维护双方交互的状态,用这样的数据结构来保证所谓的面向连接的特性。 比如我们的TCP协议,它会通过三次握手建立连接。而我们的UDP就不需要建立连接。正因为这样的不同,才有了两者各自不同的特点:

TCP和UDP的特点

1.TCP提供可靠交付、通过TCP连接传输的数据,无差错不丢失、不重复、并且按序到达。但是UDP继承了IP包的特性,不保证不丢失和按顺序到达。

2.TCP是面向字节流的,发送的时候是个流,没有头和尾。但是UDP继承了IP包的特性,面向数据报,一个一个的发送,一个一个的收。

3.TCP还可以进行拥塞控制,可以根据情况调控发送的速度。但是UDP不会,应用让它发就发,发出去就不管它的事了。

简单总结一下就是,TCP有脑子的,它会记录发送的情况,接收的情况,还可以控制发送过程,无疑这是很可靠。不像UDP一样,没有脑子,发出去就发出去了,之后就什么也不管了。当然这也不能怪它,毕竟它是继承了它的父亲IP的基因,几乎没有自己的思想。

这样说的好像UDP一无是处一样,其实我们都知道一个缺点换个角度看它很有可能就会变成优点。所以下面我们先详细认识一下这个UDP。

二、UDP

1.先看一下UDP的包头

image

可以看出UDP的包头很简单,为什么这么说它简单呢?因为和TCP的包头比较一下就会发现,它是真的超级简单【没有对比就没有伤害,滑稽笑】

发送UDP数据包,发送者是知道的,但是接收者是怎么知道是UDP包的呢?这就需要靠IP头里的8位协议,它里面会存放表示是TCP或者是UDP的信息。这里当然就是UDP了。

之后接收者会将它解析出来,然后把数据发送给应用程序正在监听的端口。那发送给哪个端口呢?

这时候UDP包头的作用就开始凸显了,由于是两端通信,所以它携带了源端口号和目的端口号,通过目的端口号,就可以把包准确的发送给应用程序了。

2.UDP的特性

从这个过程可以看出来,UDP的传输很简单

1.它不会建立连接,只是通过端口号来发送和传输数据,谁都可以传给他,它也可以传输给任何人甚至同时传输给一群人。 2.它相信包不会在传输的过程中丢失,也不会根据网络情况进行拥塞控制。 3.UDP简单、处理速度快,不像TCP一样需要操很多心,考虑各种重传、保证顺序、前面的收不到等等。

3.UDP适用场景

正是因为这些特点所以它适合在这样一些场景

(1)需要资源少,对丢包不敏感、网络比较好的应用。 DHCP 就是基于 UDP 协议的。一般的获取 IP 地址都都是内网请求,而且一次获取不到 IP 又没事,过一会还有机会。

(2)不需要一对一连接和沟通,可以广播的应用。 DHCP就是一种广播的方式。

(3)需要处理速度快、可以容忍少数丢包,但是要求即便网络堵塞也毫不退缩,勇往直前的时候。

这个时候TCP就不合适了。因为TCP在网络不好出现丢包的时候,拥塞控制策略会主动的退缩,降低发送速度,这就相当于本来环境就差,我还降低速度,那岂不是会卡到吐血,不能忍。

譬如说

现在的很火的直播应用,很多都是基于 UDP 实现了自己的视频传输协议。因为用户要看的是最新的内容,宁愿看丢失几帧的视频也不会愿意看卡出翔的视频。但是TCP的严格顺序传输要保证前一个收到了,下一个才能确认,如果前一个收不到,下一个就算包已经收到了,它也会被放到缓存里面,一直等着。对于直播来讲,这显然是不合适的,因为老的视频帧丢了其实也就丢了,就算再传过来用户也不在意了,他们要看新的内容。即使过程会丢包,丢了几帧那影响也不大,看视频的人不会有很明显的感知。

三、TCP

前面说了TCP的包头没有UDP那么简单了,那它是什么样子的呢?

image

1.认识包头

(1)我们可以看到最上层和UDP的一样,都是源端口号和目的端口号。如果没有这两个端口号。数据就不知道应该发给哪个应用。所以这是必不可少的。

(2)接下来就是包的序号。为什么要给包编号呢?这是为了解决后面的乱序问题,不编号好号接收方怎么确认哪个应该先来哪个后到呢。可能这里不能很好的理解。看完后面的内容就会清楚了。

(3)之后就是确认序号。发出去的包要有确认,要不然我不知道对方有没有接收到的,可能以为丢包了,就会不断重发这个包,所以需要有个确认序号。

(4)接下来就是一些状态位:SYN表示发起一个连接,ACK是回复,RST是重新连接,FIN是结束连接。

TCP 是面向连接的,因而双方要维护连接的状态,这些带状态位的包的发送,会引起双方的状态变更。这些状态位要记住,这对我们后面要说的面试常问问题《TCP的三次握手和四次挥手》很重要。

(5)最后还有一个重要的是窗口大小,TCP要做到流量控制和拥塞控制,通信的双方就会声明一个窗口,表明自己的处理数据能力。通过这个窗口大小就可以控制发送的速度了。

通过对包头的认识,可能总结以下TCP的特点:

可以维护连接、保证发送包的顺序、保证结果不会丢包(过程可能会丢包,但会通过重传机制保证结果不会丢包),还可以进行流量控制、拥塞控制

四、面试问题之三次握手

TCP为什么要三次握手,而不是两次握手,四次握手?

1.A和B的连接过程,三次握手最主要是防止已过期的连接再次传到被连接的主机。 如果采用两次的话,可能会出现下面这种情况。 比如是A要连接到B,结果发送的连接信息由于某种原因没有到达B; 于是,A又发了一次,结果这次B收到了,于是就发信息回来,两机就连接。当它们通信完断开后。这时候,原先没有到达的连接信息突然又传到了B,于是B发确认连接给A,然后B机就以为和A连上了,这个时候B就开始等待A传东西过去。

其实四次握手、四百次握手也是可以的,但只需要三次握手就可以实现A和B的连接,四次握手和三次握手的效果是一样的,所以不需要进行多余的握手操作。

2.在建立连接的时候要考虑一个问题,就是TCP包的序号

A要告诉B,我这面发起的包的序号起始是从哪个号开始的, B同样也要告诉A, B发起的包的序号起始是从哪个号开始的。为什么序号不能都从1开始呢?因为这样往往会出现冲突。 例如,A连上B之后,发送了1、2、3三个包,但是发送3的时候,中间丢了或者绕路了,于是重新发送,后来A掉线了,重新连上B后,序号又从1开始,然后发送2,但是压根没想发送3,但是上次绕路的那个3又回来了,发给了B, B自然认为这就是下一个包,于是发生了错误。因而,每个连接都要有不同的序号。

建立连接后,为了维护这样一个连接,需要双方维护一个状态机。在连接建立的过程中,双方的状态变化时序图如下:

image

【状态位:SYN表示发起一个连接,ACK是回复,RST是重新连接,FIN是结束连接】

(1)一开始,服务端处于监听某个端口的状态,然后客户端发起主动连接SYN,之后处于SYN-SENT状态。

(2)服务端收到发起的连接,返回SYN,并且ACK客户端的SYN,之后处于SYN-RCVD状态。

(3)客户端收到服务端发送的SYN和ACK之后,发送ACK的ACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了,服务端收到ACK的ACK后,也处于 ESTABLISHED 状态。这样就建立好了双方的连接。

五、面试问题之四次挥手

TCP为什么要进行四次挥手?关闭连接一应一答,两次不就可以了?

A发送给B发消息说我要关闭连接了,但是B能不能在ACK的时候就关闭连接呢?这是不可以的,因为A发完了数据要关闭与B的连接,但是B很有可能没有做完自己的事情,还要发送数据呢,此时称为半关闭状态。

这个时候A可以选择不接收数据,也可以选择最后再接收一段数据,等待B的关闭。这样整个连接就关闭了。

过程类似这样:

  • 小明:小花,我要走了

  • 小花:哦,我知道了(还剩一堆话说要和小明说)balabala

  • 小花:好的,你走吧,白白

  • 小明:好的,白白

上面是比较和平的分手情况。但是有可能会出现这种异常的情况,A发消息"我要走了",因为B还没有结束任务,就不会回复关闭的消息,此时由于A没有收到消息,就会以为我没有把消息成功发过去,就会重新发送消息“我走了”。

还有一种就是A发完消息有一方直接跑路的,A跑路,B即使发起结束也得不到回答,B就不知道怎么办了;或者B跑路,A不知道B是结束了还是目前正在处理自己的事情待会才发起结束,A就不知道怎么办了。所以两次挥手就会有很多问题,就需要四次挥手。

对于这些问题,TCP协议设计了几个状态来处理这些问题,我们看一下关闭连接的状态时序图。

image

(1)开始,客户端先发送关闭连接的消息,然后处于 FIN_WAIT_1 的状态。

(2)服务端接收到消息后,发送收到关闭连接的消息后,就处于CLOSE_WAIT 的状态,此时服务端继续处理未做完的事情。

(3)服务端处理完所有事情后,发送确认关闭的消息。

(4)客户端接收到确认关闭的消息再给予ACK。这样A和B就会成功的关闭连接了。

到这里相信你对这两个协议有了个相对较深的理解,在面试的时候被问到这类问题肯定可以游刃有余的回答,能够得到面试官的会心一笑,嘻嘻。

由于水平有限,有错误的地方在所难免,未免误导他人,欢迎大佬指正!码字不易,感谢大家的点赞关注!

🙏有一起学习的小伙伴可以关注下我的公众号——【❤️程序猿养成中心❤️】每周会定期做关于Android的技术分享。