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,仅考虑最简单的情况(往往也是常见情况),如下图:
如果客户端之前已经执行了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,仅考虑最简单的情况(往往也是常见情况),如下图:
相关文章推荐
- linux网络协议栈(六)传输层 (3)UDP协议 4)报文接收 recv/recvfrom/recvmsg
- linux网络协议栈(六)传输层 (2)UDP协议 1)创建和bind
- linux网络协议栈(六)传输层 (2)UDP协议 2)connect
- linux网络协议栈(六)传输层 (3)UDP协议 5)传输层框架小节
- 关于linux下的udp/tcp通信设置发送sendto/接收recvfrom信息超时的参数。解决通道堵塞问题。
- 基于UDP可靠传输协议UDT----剖析之发送和接收缓冲区
- linxu kernel version 1.0 TCP/IP 协议栈源代码分析1, UDP协议发送数据.
- socket编程(三)---- UDP协议与传输数据报文
- 基于UDP可靠传输协议UDT----报文协议详解
- 网络编程—UDP协议传输接受与发送数据
- UDP传输协议的基本应用-发送和接收
- C# udp socket.sendto() 在win2008下无法发送。
- Linux内核构造和发送vlan&udp数据报文
- Java 网络编程(二) 两类传输协议:TCP UDP
- Linux网络协议栈(五) -- 数据包的发送(based in 2.6.32)
- 实验:传输层:UDP协议 学习笔记
- TCP和UDP 协议发送数据包的大小
- UDT协议-基于UDP的可靠数据传输协议
- UDP协议的发送端的基本代码
- UDP可靠传输协议UDX,为什么在高延迟,丢包率较高的环境下,不受延迟及丢包影响原理讨论及深思