前言:
上文我们讲到了应用层协议:HTTP【Linux 网络】理解并应用应用层协议:HTTP(附简单HTTP服务器C++代码)-CSDN博客
本文了我们来讲讲传输层协议:UDP
传输层
传输层是用于端到端的数据传输的!

为了更好的理解传输层,我们先梳理一下网络数据发送的大致流程:
一. 应用层:由用户给出要发送的数据
二. 传输层:加上对应协议的报头、以及源端口号与目的端口号
三. 网络层:加上对应的协议报头、以及源IP地址与目的IP地址
四. 数据链路层:加上对应协议报头、以及源MAC地址与目的MAC地址
五. 物理层:将数据转化为光信息与电信息发送出去
所以我们说传输层是端到端的数据传输,因为传输层标记端口号。
参考下图(TCP),UDP协议是没有发送缓冲区的

数据发送到主机B后,一层层向上交付,到传输层解析报头,得到对应端口。再将数据放在端口对应软件的缓冲区中。
UDP协议
UDP协议格式如下,分为两部分:
报头:前8字节既报头!包含源端口号、目的端口号、UDP长度、UDP检验和
有效载荷:既引用层传来的数据

UDP长度: 指整个报文的长度(报头 + 有效载荷)
UDP校验和: 如果检验和出错就会直接丢弃。
有效载荷长度 = UDP长度 - 报头长度8字节。
UDP特点
UDP的传输类似于寄信
无连接:UDP知道了IP与端口就直接发送数据了,不需要像TCP一个还行先建立连接才行
不可靠:UDP不保证数据的可靠性,如果因为网络波动等等原因导致数据丢失,那就直接丢失了,没有补救措施
面向数据报:UDP送发的数据一定是完整的报文,不像TCP一样可以控制发送数据的大小
面向数据报
交给UDP多少数据,就发送多少数据,UDP不会进程融合或拆分。 不像TCP,当TCP的发送缓冲区有多个报文时,TCP会将这多个报文"融合"一起发送,让对方一次性全部接收。而UDP是你发送多少保存,对方就必须接收多少次。
UDP缓冲区
UDP是没有发送缓冲区的,但是有接收缓冲区!
接收缓冲区用于当对方应用层忙不过来的时候,暂时保存数据。但是当缓冲区满了的时候,发送过来的数据会被直接丢弃!这又体验了UDP不可靠的特点!
理解报文
在计算机联网互通的时候,一定会进行大量的数据交换:既数据的发送、接收。此次计算机中一定存在大量的报文!!!
对于计算机来说,面对如此大量的报文,是否需要进行管理呢?需要!
如何管理?先描述,再组织!!!
描述:OS中存在一个名为:struct sk_buff的结构体,用于描述报文属性。
管理:OS中再用链表对报文进程组织!
cpp
struct sk_buff {
/* These two members must be first. */
struct sk_buff *next;
struct sk_buff *prev;
struct sk_buff_head *list;
struct sock *sk;
struct timeval stamp;
struct net_device *dev;
struct net_device *input_dev;
struct net_device *real_dev;
union {
struct tcphdr *th;
struct udphdr *uh;
struct icmphdr *icmph;
struct igmphdr *igmph;
struct iphdr *ipiph;
struct ipv6hdr *ipv6h;
unsigned char *raw;
} h;
union {
struct iphdr *iph;
struct ipv6hdr *ipv6h;
struct arphdr *arph;
unsigned char *raw;
} nh;
union {
unsigned char *raw;
} mac;
struct dst_entry *dst;
struct sec_path *sp;
/*
* This is the control buffer. It is free to use for every
* layer. Please put your private variables there. If you
* want to keep them across layers you have to do a skb_clone()
* first. This is owned by whoever has the skb queued ATM.
*/
char cb[40];
unsigned int len,
data_len,
mac_len,
csum;
unsigned char local_df,
cloned,
pkt_type,
ip_summed;
__u32 priority;
unsigned short protocol,
security;
void (*destructor)(struct sk_buff *skb);
#ifdef CONFIG_NETFILTER
unsigned long nfmark;
__u32 nfcache;
__u32 nfctinfo;
struct nf_conntrack *nfct;
#ifdef CONFIG_NETFILTER_DEBUG
unsigned int nf_debug;
#endif
#ifdef CONFIG_BRIDGE_NETFILTER
struct nf_bridge_info *nf_bridge;
#endif
#endif /* CONFIG_NETFILTER */
#if defined(CONFIG_HIPPI)
union {
__u32 ifield;
} private;
#endif
#ifdef CONFIG_NET_SCHED
__u32 tc_index; /* traffic control index */
#ifdef CONFIG_NET_CLS_ACT
__u32 tc_verd; /* traffic control verdict */
__u32 tc_classid; /* traffic control classid */
#endif
#endif
/* These elements must be at the end, see alloc_skb() for details. */
unsigned int truesize;
atomic_t users;
unsigned char *head,
*data,
*tail,
*end;
};
在struct sk_buff中存在四个指针,指向报文:
cpp
unsigned char *head,
*data,
*tail,
*end;

head与end分别指向报文空间的起始、结束位置
data指向报文的开始位置
tail指向报文的结束位置
当报文向下传递添加报头时,只需要date指针上移,其他指针不动!
其实到这里我们也明白了,在给报文添加报头 or 解析报头时是不需要移动报文本身,只需要向下or向上交付sk_buff对象即可!通过指针访问修改报文!!!
UDP使用注意事项
我们注意到,UDP协议⾸部中有一个16位的最大长度(含UDP⾸部)。
然而64K在当今的互联网环境下,是⼀个非常小的数字。如果我们需要传输的数据超过64K, 就需要在应用层手动的分包, 多次发送, 并在接收端手动拼装。