* r( S6 [0 U0 O% ]6 ?& X 一、UDP的特性与应用场景
5 I: u0 g, O z d3 `' L+ | 采用UDP有3个关键点: 网络带宽需求较小,而实时性要求高大部分应用无需维持连接需要低功耗+ e" o% j6 L; a8 l# u( V6 J1 X
应用场景: 网页浏览:新浪微博就已经用了QUIC协议流媒体:WebRTC就是基于UDP的实时游戏:Unity3D采用的RakNet也是基于UDP的协议7 P+ U9 o- U; |' Z
基于UDP协议的QUIC协议$ j, a8 { |2 p$ S- Q' w2 ?' Q5 k
QUIC(Quick UDP Internet Connection)是谷歌制定的一种基于UDP的低时延的互联网传输层协议
, D- W) Q) r/ y* C: ` \ 详情可参阅:https://eng.uber.com/employing-quic-protocol/
8 O5 |, ?+ r$ b) O4 k 8 t6 w% x' \% _9 K+ i
UDP传输时需要注意的问题数据包确认机制数据包重传机制尽量不发送大于路径MTU的数据包处理数据包重排! i" [; W( u. {6 j2 d
二、UDP与MTU7 O$ h( v8 I2 Y2 f. ^
IP分片的概念 在TCP/IP分层中,数据链路层用MTU(Maximum Transmission Unit,最大传输单元)来限制所能传输的数据包大小,MTU是指一次传送的数据最大长度,不包括数据链路层数据帧的帧头,如以太网的MTU为1500字节,实际上数据帧的最大长度为1514字节,其中以太网数据帧的帧头为14字节当发送的IP数据包的大小超过了MTU时,IP层就需要对数据进行分片,否则数据将无法发送成功IP层是没有超时重传机制的,如果IP层对一个数据包进行了分片,只要有一个分片丢失了,只能依赖于传输层进行重传,结果是所有的分片都要重传一遍,这个代价有点大;公网传输,需要经过多个网络设备,IP分片容易造成丢包由此可见,IP分片会大大降低传输层传送数据的成功率,所以我们要避免IP分片+ Q7 a5 A' U& n
UDP与MTU的关系
' Z) m O/ G3 P1 d* d# L* T MTU是指通信协议的链路层上面所能通过的最大数据包大小 ) g2 l* _6 ]; F, r' t3 ~( u" z/ R X
单个UDP传输的最大内容1472字节,但由于不同的网络中转设备设置的MTU值并不相同: Internet环境下:标准MTU值为576字节,UDP的数据长度应该控制在548字节(576-8-20)以内局域网环境下:UDP的数据长度控制在1472个字节以内
_. I+ B+ v# ]# ~* l8 z( d 0 c& _& i$ }" K I1 ?# |* B* f
相关视频推荐 0 W* K3 i9 ]$ ?- I9 \5 }6 Y8 q {
腾讯一面题: UDP如何实现可靠性传输? 9 y/ [( _* [+ l" I' I" J
网络原理tcp/udp,网络编程epoll/reactor,面试中正经“八股文”
) x t/ u8 _8 r0 I 学习地址:C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂
i2 K# }5 t8 b+ y% e$ H 需要C/C++ Linux服务器架构师学习资料加qun812855908获取(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享
2 w3 o' w2 T' ~# d, h" ?: S
! ?+ ~, c" m( R4 K& M0 ]4 Q 三、UDP分包与组包设计% |' O+ e* j+ Z1 V
为什么要对UDP进行分包与组包 通过上面对MTU的介绍我们知道,如果IP数据包的大小超过了其所在环境中MTU的大小,那么就会对IP数据包进行分片当分片只要其中一个片段丢失,那么就需要重传所有的分片数据,因此这种消耗是比较大的/ O4 S" _! y" e4 O* E5 j+ S0 p& [
主要思想 在应用层,我们对UDP数据进行传输时调用的两个接口为sendto()和recvfrom()我们将传输的数据(原始数据,可能很大)分割为一个一个小的分片,使分片的大小不大于MTU的大小,这样我们在进行UDP数据传输的时候,就不会产生上面IP分片的问题了
- N6 J- o' J. B. g % Q# i9 A5 [' r, x6 K' ?
对于每一个分片我们需要设计其格式,例如下面是定义的一种格式。相关字段为如下所示
: g/ `) W; Q7 D: i* O8 s u6 j2 U
( f |0 y# q3 h# ] ?8 F7 ~ 代码
A, `4 F$ s o, A GIthub链接:https://github.com/dongyusheng/csdn-code/tree/master/udp_piece
1 A3 V& `1 o; {. u0 X. Q# M 其中:
2 i' K# a9 W, ]6 X: z circular_buffer.h/.c:环形缓冲区,用来保存数据的
- n, ?' \& K! Z" y* L udp-piece.h/.c:UDP分片与重组(核心代码) 0 N) I8 L# E) V/ `" T+ }
udp-piece-client.c:客户端测试代码,代码内会向服务端发送UDP数据 " V9 r* _4 a( D3 k5 j& i6 O( C
udp-piece-server.c:服务端测试代码,接收客户端的UDP数据 7 a) W1 Z0 C+ i. t4 |
* m. x3 Q1 j- D6 L0 G T" B 编码主要思路 ! N( z: Q2 }7 t& I" r
udp-piece.h:
; ~; |2 G7 ^, M( X% y+ _6 ` 定义了如下的宏和结构,主要用来描述分片节点的 # E0 z" D7 e. O5 q) N! U$ z
其中比较重要的一个字段为PIECE_FIX_SIZE,其代表我们分片中实际数据的长度,因为Internet中MTU的大小通常为576,所以我们的UDP数据包最好不要超过576-8-20大小(8为UDP头大小,20位IP报文大小),另外还要减去12(因为我们分片也有头,为12字节) ) } B5 W5 Q j3 _/ ^
4 k: b1 N* K& a0 d
udp-piece-client.c:其向服务端发送一长串字符串,在发送之前先调用udp_piece_cut()对整个UDP数据包进行分片,然后逐个发送出去
: }; g; @8 h& u0 D) q; ~1 Y 9 }0 k/ h, L9 h) I r
udp-piece-server.c:其从客户端接收UDP数据,将接收的数据放到环形缓冲中,然后进行重组
) ~) S$ p' t5 D% P& U2 _7 n& D: p
0 `9 R8 O @2 ~* q Q, i 小结
/ o& {- D% w( P9 B! t% G4 ` 本文只介绍了“UDP的分包与组包”,并没有涉及到UDP数据包确认、重传等机制,并且代码也只做到了分包与组包 9 Q, w9 i, t8 g3 v# P! @& e
+ O' h5 r4 X2 o3 U
7 c7 P ^5 b# U( o E |