UDP是一个简单的面向数据报的运输层协议:进程的每个输出操作通常会产生一个UDP数据报,并组装成一份待发送的IP数据报。这与面向流的协议不同,如TCP,应用程序产生的数据与真正发送的单个IP数据报并不存在直接的关联。
UDP输入和输出以及轻量级UDP涉及以下文件:
include/net/udplite.h 定义轻量级UDP专用的函数等
include/linux/udp.h 定义UDP传输控制块等
net/ipv4/udp.c UDP协议的实现
net/ipv4/udplite.c 轻量级UDP的实现
net/core/sock.c 实现传输层通用的函数
net/ipv4/datagram.c 实现UDP的connect调用
net/ipv4/af_inet.c 网络层和传输层接口
UDP的输入与输出
UDP函数之间调用关系比TCP简单的多,和TCP一样,sock结构中的sk_receive_queue成员是UDP的接收队列,通常情况下,接收到UDP数据报会缓存到此,等待用户进程的读取。UDP接收到数据报后的处理要比TCP简单的多,通过校验的UDP数据报,根据类型做不同的处理后被添加到接收队列。
UDP传输控制块
- struct udp_sock {
- /* inet_sock has to be the first member */
- struct inet_sock inet;
- int pending; /* Any pending frames ? */
- unsigned int corkflag; /* Cork is required */
- __u16 encap_type; /* Is this an Encapsulation socket? */
- /*
- * Following member retains the information to create a UDP header
- * when the socket is uncorked.
- */
- __u16 len; /* total length of pending frames */
- /*
- * Fields specific to UDP-Lite.
- */
- __u16 pcslen;
- __u16 pcrlen;
- /* indicator bits used by pcflag: */
- #define UDPLITE_BIT 0x1 /* set by udplite proto init function */
- #define UDPLITE_SEND_CC 0x2 /* set via udplite setsockopt */
- #define UDPLITE_RECV_CC 0x4 /* set via udplite setsocktopt */
- __u8 pcflag; /* marks socket as UDP-Lite if > 0 */
- __u8 unused[3];
- /*
- * For encapsulation sockets.
- */
- int (*encap_rcv)(struct sock *sk, struct sk_buff *skb);
- };
struct inet_sock inet
udp_sock由inet_sock结构扩展而来
int pending
发送状态,其值只能是0或AF_INET,0表示数据已经从UDP套接口发送到IP层,可以继续调用sendmsg()发送数据,AF_INET表示UDP正在处理调用sendmsg()的发送数据,不需要处理目的地址、路由等信息,直接处理UDP数据
unsigned int corkflag
0 有数据需要发送时,立即发送出去
非0 将UDP数据组成一个单一64KB的UDP数据报后将其发送出去,因此会有延迟
__u16 len
从UDP套接口发送数据到IP层时,标识待发送数据的长度
__u16pcslen
__u16 pcrlen
轻量级UDP,通过UDPLITE_SEND_CSCOV和UDPLITE_RECV_CSCOV选项设置,用于实现控制发送和接收校验和的执行
0 表示对发送/接收的整个UDP-Lite数据包进行校验
>>=8 表示对发送/接收的UDP-Lite包的前pcslen/pcrlen个字节进行校验
其他值是非法的
UDP的状态
UDP的传输是没有状态的,但事实上,UDP和RAW也借用了TCP的一些值:在一个套接口创建之初,其状态是TCP_CLOSE,当UDP套接口调用connect()后,状态改变为TCP_ESTABLISHED,***,关闭套接口时又置回TCP_CLOSE,RAW也一样。
轻量级UDP
2.6.20版本的Linux支持UDP-Lite。UDP-Lite协议相对较新,与UDP协议类似,但更适应网络差错率较大而应用对轻微差错不敏感的情况,例如实时视频播放等。那么UDP-Lite与传统的UDP有什么不同呢?传统的UDP协议对其负载(Payload)作完整的校验,如果其中的哪怕只有一位发生了变化,那么整个数据包就有可能被丢弃,在某些情况下,丢掉一个这样的包代价是非常大的,尤其当数据包比较大的时候。在UDP-Lite协议中,一个数据包到底需不需对其负载进行校验,或者是校验多少位都是由用户控制的,
Linux对UDP-Lite协议的支持也是通过在原来的UDP协议的基础上添加了一个setsockopt选项来实现控制发送/接收 Checksum Coverage的
int val = 20;
setsockopt(s, SOL_UDPLITE, UDPLITE_SEND_CSCOV, &val, sizeof(int));
int min = 20;
setsockopt(s, SOL_UDPLITE, UDPLITE_RECV_CSCOV, &min, sizeof(int));
创建一个轻量级UDP套接口很简单
s = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDPLITE);