Linux内核分析 - 网络[十二]:UDP模块 - 收发
|
如果没有报文,有两种情况:使用了非阻塞接收,且用户接收时还没有报文到来;使 用阻塞接收,但之前没有报文,且在sk->sk_rcvtimeo时间内都没有报文到来。没有报文,返回错误值。
if (!skb)
goto out;
len是recvfrom()传入buf的大小,ulen是报文内容的长度,如果ulen > len,那么只需要使用buf的 ulen长度就可以了;如果len < ulen,那么buf不够报文填充,只能对报文截断,取前len个字节。 ulen = skb- >len - sizeof(struct udphdr); if (len > ulen) len = ulen; else if (len < ulen) msg->msg_flags |= MSG_TRUNC; 如果报文被截断或使用UDP-Lite,那么需要提前验证校验和, udp_lib_checksum_complete()完成校验和计算,函数在下面具体分析。
if (len < ulen || UDP_SKB_CB(skb)-
>partial_cov) {
if (udp_lib_checksum_complete(skb))
goto csum_copy_err;
}
如果报文不用验证校验和,那么执行if部分,调用skb_copy_datagram_iovec()直接拷贝报文到buf中就可以了;如果 报文需要验证校验和,那么执行else部分,调用skb_copy_and_csum_datagram_iovec()拷贝报文到buf,并在拷贝过程中计算校 验和。这也是为什么在内核收到udp报文时为什么先验证校验和再处理的原因,udp报文可能很大,校验和的计算可能很耗时,将 其放在拷贝过程中可以节约开销,当然它的代价是一些校验和错误的报文也会被添加到socket的接收队列上,直到用户真正接收 时它们才会被丢弃。
if (skb_csum_unnecessary(skb))
err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov, len);
else {
err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov);
if (err == -EINVAL)
goto csum_copy_err;
}
拷贝地址到msg->msg_name中,在sys_recvfrom()中msg->msg_name=&address,然后address会从内核拷贝 给用户空间的addr。
if (sin) {
sin->sin_family = AF_INET;
sin->sin_port = udp_hdr(skb)->source;
sin->sin_addr.s_addr = ip_hdr(skb)->saddr;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
(编辑:宣城站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |


