UDP协议由其报头和其数据部分共同组成,其中报头长度固定为8字节

其中的UDP长度 就是用来记录 UDP首部+UDP数据 的长度的,16位表示其能表示得最大长度(65535字节)
1、报头和有效载荷如何分离
UDP使用固定长度的报头(8字节)
有16位UDP长度,用来表示整个UDP长度,就能知道有效载荷的总长度
根据这两样就可以精准分离报头和数据,
2、有效载荷怎么分用的
报头分离 + 根据报头判断 + 载荷上交
-
砍掉本层报头(剥离)
-
读报头里的"上层协议标识"(比如是谁的数据)
-
把剩下的载荷(payload)原封不动地交给对应的上层模块
3、如何理解UDP协议报头
本质可以当成是一个结构体(接收方和发送方默认执行的)
4、如何理解封装过程,解包过程
报文在内核中的描述结构体叫做 struct sk_buff,sk_buff 里有四个字段,如下图所示

这四个指针,会指向整个报文的开头和结尾,以及有效载荷的开头和结尾
封装和解包的过程,最核心的步骤就是 移动指针
首先是封装:
当应用层想要将报文给传输层时,内核就会创建struct sk_buff ,最开始data和tail指针都指向中间,根据应用层传递的数据大小,tail指针会往后移动留出足够的空间,内核就会负责把数据拷贝到data指向的这块空间(data和tail之间),数据拷贝完毕,就会开始填充UDP报头,填充完,将data指针向前移动,强转类型之后把UDP报头信息完整的拷贝sk_buff内部,再以此类推给到封装IP报头的函数,大致过程如图所示:

然后是解包:
当应用层调用socket创建套接字,内核会就创建一个socket结构体,socket返回的fd指向的struct file内部的private_data指针就会指向这个socket结构体
在socket内部拥有一个struct sock*指针
而这个指针指向的struct sock对象其实是inet_sock结构体内的第一个成员,而inet_sock就是udp_sock结构体内部的第一个成员(假设传输层使用UDP协议),其在内存中的布局大致可以这么理解:

其中struct sock内就维护了报文的接收队列和发送队列(struct sk_buff_head类型结构体)
UDP特点
1、无连接:发送数据之前不需要 像 TCP 那样建立"三次握手"连接。应用层把数据交给 UDP,UDP 立马就封装发出去。没有建立连接的开销,延迟极低。
2、不可靠:不保证数据包能到达对端,也不保证顺序。
3、面向报文:应用层发下来一个 100 字节的数据,UDP 就会原封不动地给它加个 8 字节头部,组成一个 108 字节的数据包发出去。(应用层给传输层多少数据,就多少数据组成一个报文)
4、支持一对多
也是因此,UDP不需要真正意义上的发送缓冲区,缓冲区的目的就是确保数据的可靠性,比如TCP发送数据了,它不能立刻丢掉刚刚发送的数据,假设丢包了,它就得负责重传,就需要发送缓冲区内的内容,而UDP不需要保证数据的可靠性
由于UDP面向报文,所以往往使用UDP协议的应用层需要进行手动分包,避免传输的数据大过MTU(最大传输单元,往往是1500字节)