You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
14 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 第10讲 | UDP协议因性善而简单难免碰到“城会玩”
讲完了IP层以后接下来我们开始讲传输层。传输层里比较重要的两个协议一个是TCP一个是UDP。对于不从事底层开发的人员来讲或者对于开发应用的人来讲最常用的就是这两个协议。由于面试的时候这两个协议经常会被放在一起问因而我在讲的时候也会结合着来讲。
## TCP和UDP有哪些区别
一般面试的时候我问这两个协议的区别大部分人会回答TCP是面向连接的UDP是面向无连接的。
什么叫面向连接什么叫无连接呢在互通之前面向连接的协议会先建立连接。例如TCP会三次握手而UDP不会。为什么要建立连接呢你TCP三次握手我UDP也可以发三个包玩玩有什么区别吗
**所谓的建立连接,是为了在客户端和服务端维护连接,而建立一定的数据结构来维护双方交互的状态,用这样的数据结构来保证所谓的面向连接的特性。**
例如,**TCP提供可靠交付**。通过TCP连接传输的数据无差错、不丢失、不重复、并且按序到达。我们都知道IP包是没有任何可靠性保证的一旦发出去就像西天取经走丢了、被妖怪吃了都只能随它去。但是TCP号称能做到那个连接维护的程序做的事情这个下两节我会详细描述。而**UDP继承了IP包的特性不保证不丢失不保证按顺序到达。**
再如,**TCP是面向字节流的**。发送的时候发的是一个流没头没尾。IP包可不是一个流而是一个个的IP包。之所以变成了流这也是TCP自己的状态维护做的事情。而**UDP继承了IP的特性基于数据报的一个一个地发一个一个地收。**
还有**TCP是可以有拥塞控制的**。它意识到包丢弃了或者网络的环境不好了,就会根据情况调整自己的行为,看看是不是发快了,要不要发慢点。**UDP就不会应用让我发我就发管它洪水滔天。**
因而**TCP其实是一个有状态服务**,通俗地讲就是有脑子的,里面精确地记着发送了没有,接收到没有,发送到哪个了,应该接收哪个了,错一点儿都不行。而**UDP则是无状态服务**。通俗地说是没脑子的,天真无邪的,发出去就发出去了。
我们可以这样比喻如果MAC层定义了本地局域网的传输行为IP层定义了整个网络端到端的传输行为这两层基本定义了这样的基因网络传输是以包为单位的二层叫帧网络层叫包传输层叫段。我们笼统地称为包。包单独传输自行选路在不同的设备封装解封装不保证到达。基于这个基因生下来的孩子UDP完全继承了这些特性几乎没有自己的思想。
## UDP包头是什么样的
我们来看一下UDP包头。
前面章节我已经讲过包的传输过程这里不再赘述。当我发送的UDP包到达目标机器后发现MAC地址匹配于是就取下来将剩下的包传给处理IP层的代码。把IP头取下来发现目标IP匹配接下来呢这里面的数据包是给谁呢
发送的时候我知道我发的是一个UDP的包收到的那台机器咋知道的呢所以在IP头里面有个8位协议这里会存放数据里面到底是TCP还是UDP当然这里是UDP。于是如果我们知道UDP头的格式就能从数据里面将它解析出来。解析出来以后呢数据给谁处理呢
处理完传输层的事情,内核的事情基本就干完了,里面的数据应该交给应用程序自己去处理,可是一台机器上跑着这么多的应用程序,应该给谁呢?
无论应用程序写的使用TCP传数据还是UDP传数据都要监听一个端口。正是这个端口用来区分应用程序要不说端口不能冲突呢。两个应用监听一个端口到时候包给谁呀所以按理说无论是TCP还是UDP包头里面应该有端口号根据端口号将数据交给相应的应用程序。
![](https://static001.geekbang.org/resource/image/2c/84/2c9a109f3be308dea901004a5a3b4c84.jpg)
当我们看到UDP包头的时候发现的确有端口号有源端口号和目标端口号。因为是两端通信嘛这很好理解。但是你还会发现UDP除了端口号再没有其他的了。和下两节要讲的TCP头比起来这个简直简单得一塌糊涂啊
## UDP的三大特点
UDP就像小孩子一样有以下这些特点
第一,**沟通简单**,不需要一肚子花花肠子(大量的数据结构、处理逻辑、包头字段)。前提是它相信网络世界是美好的,秉承性善论,相信网络通路默认就是很容易送达的,不容易被丢弃的。
第二,**轻信他人**。它不会建立连接,虽然有端口号,但是监听在这个地方,谁都可以传给他数据,他也可以传给任何人数据,甚至可以同时传给多个人数据。
第三,**愣头青,做事不懂权变**。不知道什么时候该坚持,什么时候该退让。它不会根据网络的情况进行发包的拥塞控制,无论网络丢包丢成啥样了,它该怎么发还怎么发。
## UDP的三大使用场景
基于UDP这种“小孩子”的特点我们可以考虑在以下的场景中使用。
第一,**需要资源少,在网络情况比较好的内网,或者对于丢包不敏感的应用**。这很好理解,就像如果你是领导,你会让你们组刚毕业的小朋友去做一些没有那么难的项目,打一些没有那么难的客户,或者做一些失败了也能忍受的实验性项目。
我们在第四节讲的DHCP就是基于UDP协议的。一般的获取IP地址都是内网请求而且一次获取不到IP又没事过一会儿还有机会。我们讲过PXE可以在启动的时候自动安装操作系统操作系统镜像的下载使用的TFTP这个也是基于UDP协议的。在还没有操作系统的时候客户端拥有的资源很少不适合维护一个复杂的状态机而且因为是内网一般也没啥问题。
第二,**不需要一对一沟通,建立连接,而是可以广播的应用**。咱们小时候人都很简单,大家在班级里面,谁成绩好,谁写作好,应该表扬谁惩罚谁,谁得几个小红花都是当着全班的面讲的,公平公正公开。长大了人心复杂了,薪水、奖金要背靠背,和员工一对一沟通。
UDP的不面向连接的功能可以使得可以承载广播或者多播的协议。DHCP就是一种广播的形式就是基于UDP协议的而广播包的格式前面说过了。
对于多播我们在讲IP地址的时候讲过一个D类地址也即组播地址使用这个地址可以将包组播给一批机器。当一台机器上的某个进程想监听某个组播地址的时候需要发送IGMP包所在网络的路由器就能收到这个包知道有个机器上有个进程在监听这个组播地址。当路由器收到这个组播地址的时候会将包转发给这台机器这样就实现了跨路由器的组播。
在后面云中网络部分有一个协议VXLAN也是需要用到组播也是基于UDP协议的。
第三,**需要处理速度快,时延低,可以容忍少数丢包,但是要求即便网络拥塞,也毫不退缩,一往无前的时候**。记得曾国藩建立湘军的时候,专门招出生牛犊不怕虎的新兵,而不用那些“老油条”的八旗兵,就是因为八旗兵经历的事情多,遇到敌军不敢舍死忘生。
同理UDP简单、处理速度快不像TCP那样操这么多的心各种重传啊保证顺序啊前面的不收到后面的没法处理啊。不然等这些事情做完了时延早就上去了。而TCP在网络不好出现丢包的时候拥塞控制策略会主动的退缩降低发送速度这就相当于本来环境就差还自断臂膀用户本来就卡这下更卡了。
当前很多应用都是要求低时延的它们可不想用TCP如此复杂的机制而是想根据自己的场景实现自己的可靠和连接保证。例如如果应用自己觉得有的包丢了就丢了没必要重传了就可以算了有的比较重要则应用自己重传而不依赖于TCP。有的前面的包没到后面的包到了那就先给客户展示后面的嘛干嘛非得等到齐了呢如果网络不好丢了包那不能退缩啊要尽快传啊速度不能降下来啊要挤占带宽抢在客户失去耐心之前到达。
由于UDP十分简单基本啥都没做也就给了应用“城会玩”的机会。就像在和平年代每个人应该有独立的思考和行为应该可靠并且礼让但是如果在战争年代往往不太需要过于独立的思考而需要士兵简单服从命令就可以了。
曾国藩说哪支部队需要诱敌牺牲也就牺牲了相当于包丢了就丢了。两军狭路相逢的时候曾国藩说上没有带宽也要上这才给了曾国藩运筹帷幄城会玩的机会。同理如果你实现的应用需要有自己的连接策略可靠保证时延要求使用UDP然后再应用层实现这些是再好不过了。
## 基于UDP的“城会玩”的五个例子
我列举几种“城会玩”的例子。
### “城会玩”一网页或者APP的访问
原来访问网页和手机APP都是基于HTTP协议的。HTTP协议是基于TCP的建立连接都需要多次交互对于时延比较大的目前主流的移动互联网来讲建立一次连接需要的时间会比较长然而既然是移动中TCP可能还会断了重连也是很耗时的。而且目前的HTTP协议往往采取多个数据通道共享一个连接的情况这样本来为了加快传输速度但是TCP的严格顺序策略使得哪怕共享通道前一个不来后一个和前一个即便没关系也要等着时延也会加大。
而**QUIC**(全称**Quick UDP Internet Connections****快速UDP互联网连接**是Google提出的一种基于UDP改进的通信协议其目的是降低网络通信的延迟提供更好的用户互动体验。
QUIC在应用层上会自己实现快速连接建立、减少重传时延自适应拥塞控制是应用层“城会玩”的代表。这一节主要是讲UDPQUIC我们放到应用层去讲。
### “城会玩”二:流媒体的协议
现在直播比较火直播协议多使用RTMP这个协议我们后面的章节也会讲而这个RTMP协议也是基于TCP的。TCP的严格顺序传输要保证前一个收到了下一个才能确认如果前一个收不到下一个就算包已经收到了在缓存里面也需要等着。对于直播来讲这显然是不合适的因为老的视频帧丢了其实也就丢了就算再传过来用户也不在意了他们要看新的了如果老是没来就等着卡顿了新的也看不了那就会丢失客户所以直播实时性比较比较重要宁可丢包也不要卡顿的。
另外,对于丢包,其实对于视频播放来讲,有的包可以丢,有的包不能丢,因为视频的连续帧里面,有的帧重要,有的不重要,如果必须要丢包,隔几个帧丢一个,其实看视频的人不会感知,但是如果连续丢帧,就会感知了,因而在网络不好的情况下,应用希望选择性的丢帧。
还有就是当网络不好的时候TCP协议会主动降低发送速度这对本来当时就卡的看视频来讲是要命的应该应用层马上重传而不是主动让步。因而很多直播应用都基于UDP实现了自己的视频传输协议。
### “城会玩”三:实时游戏
游戏有一个特点,就是实时性比较高。快一秒你干掉别人,慢一秒你被别人爆头,所以很多职业玩家会买非常专业的鼠标和键盘,争分夺秒。
因而实时游戏中客户端和服务端要建立长连接来保证实时传输。但是游戏玩家很多服务器却不多。由于维护TCP连接需要在内核维护一些数据结构因而一台机器能够支撑的TCP连接数目是有限的然后UDP由于是没有连接的在异步IO机制引入之前常常是应对海量客户端连接的策略。
另外还是TCP的强顺序问题对战的游戏对网络的要求很简单玩家通过客户端发送给服务器鼠标和键盘行走的位置服务器会处理每个用户发送过来的所有场景处理完再返回给客户端客户端解析响应渲染最新的场景展示给玩家。
如果出现一个数据包丢失所有事情都需要停下来等待这个数据包重发。客户端会出现等待接收数据然而玩家并不关心过期的数据激战中卡1秒等能动了都已经死了。
游戏对实时要求较为严格的情况下采用自定义的可靠UDP协议自定义重传策略能够把丢包产生的延迟降到最低尽量减少网络问题对游戏性造成的影响。
### “城会玩”四IoT物联网
一方面物联网领域终端资源少很可能只是个内存非常小的嵌入式系统而维护TCP协议代价太大另一方面物联网对实时性要求也很高而TCP还是因为上面的那些原因导致时延大。Google旗下的Nest建立Thread Group推出了物联网通信协议Thread就是基于UDP协议的。
### “城会玩”五:移动通信领域
在4G网络里移动流量上网的数据面对的协议GTP-U是基于UDP的。因为移动网络协议比较复杂而GTP协议本身就包含复杂的手机上线下线的通信协议。如果基于TCPTCP的机制就显得非常多余这部分协议我会在后面的章节单独讲解。
## 小结
好了,这节就到这里了,我们来总结一下:
* 如果将TCP比作成熟的社会人UDP则是头脑简单的小朋友。TCP复杂UDP简单TCP维护连接UDP谁都相信TCP会坚持知进退UDP愣头青一个勇往直前
* UDP虽然简单但它有简单的用法。它可以用在环境简单、需要多播、应用层自己控制传输的地方。例如DHCP、VXLAN、QUIC等。
最后,给你留两个思考题吧。
1. 都说TCP是面向连接的在计算机看来怎么样才算一个连接呢
2. 你知道TCP的连接是如何建立又是如何关闭的吗
欢迎你留言和讨论。趣谈网络协议,我们下期见!