您的位置:首页 > 理论基础 > 计算机网络

linux网络协议栈(六)传输层 (3)UDP协议 3)报文发送 send/sendto/sendmsg

2014-01-14 09:10 399 查看
6.2.2.2.4、报文发送:
如果客户端之前已经执行了connect系统调用,那么此时它直接使用send发送报文即可,当然也可以使用sendto显式的指定目的地址,事实上send和sendto在内核中使用同一个hook,只是send提供的显式目的地址为NULL罢了,如下图:



继续关注sendto实现:



1、在socket系统调用中,收发的数据以结构体msghdr描述,它包括了发送目的地址、发送内容、额外控制信息等字段,sendto最后两个参数指定的目的地址就被存在在msghdr的发送目的地址字段中,实际数据存在发送内容字段中,使用send/sendto就不包含额外控制信息,msghdr结构体如下图:



事实上recv/send族系统调用还有一组函数:recvmsg/sendmsg,它们是由应用程序组织msghdr结构体成员发给内核,和recv/recvfrom/send/sendto的区别在于,它们可以加入额外控制信息字段,2.10代码使用过,这里暂不考虑额外控制信息;

2、接下来,如果指定了目的地址,则解析出目的地址并复制到msghdr描述符中,然后调用sock_sendmsg函数,它将最终调用相应套接字类型的ops,对于数据报套接字为函数inet_sendmsg,如下图:



这里可见,客户端即便不调用connect强制进行bind,在发送报文时也要进行bind;

然后,调用对应传输层协议ops的sendmsg方法,对于UDP协议为udp_sendmsg函数,除了发送流程本身外,还涉及传输层报文发送的一个重点,它也是网络编程的一个有用的地方,简单的说就是:对要发送的数据进行整形,如果是大数据包则进行切割,变成多个小于或等于MTU的skb;如果是小数据包,并且应用程序对该UDP套接字启用了小报文聚合功能(cork,UDP专属功能,由setsockopt系统调用单独指定或在sendmsg系统调用中指定标志位MSG_MORE)则将若干个小数据包整合成一个大skb

事实上,上面这个事情可以认为是网络层完成的,但它在传输层发送函数中使用;在UDP的实际使用场合中,往往频繁的发送一些小报文,这会加剧网络层的负担,并且导致网络中存在大量的小报文,上面的cork就是解决这个问题的,援引某篇文章的比喻:可以将UDP底部到IP的部分看成一个漏斗,cork给人的感觉就是将这个漏斗底部给堵住,等在一定时候再拔掉这个塞子,这样就可以把各种小数据汇集成一个大数据

鉴于UDP报文发送的上述特性,所以在每次报文发送时,都判断是否传输通道sock的发送队列中是否还有未发送的报文,如果有,则把报文追加到UDP报文“漏斗中”,由UDP的cork机制决定是立即发送,还是分拆成小报文,还是等待合成一个大报文发送,这些事情由函数ip_append_data完成,它能保证每个UDP报文的数据不和其他UDP报文的数据混杂一起,但在多线程并发的发送时,UDP报文的发送顺序是无法保证的;

函数ip_append_data是一个很复杂的函数,暂不分析,后续补充;

回到函数udp_sendmsg,仅考虑最简单的情况(往往也是常见情况),如下图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: