开发者社区> xumaojun> 正文

内核中的UDP socket流程(9)——ip_append_data

简介: 作者:gfree.wind@gmail.com 原文:http://blog.chinaunix.net/space.php?uid=23629988&do=blog&id=96739 下面开始分析ip_append_data这个函数。
+关注继续查看
作者:gfree.wind@gmail.com

下面开始分析ip_append_data这个函数。可以先看看这个函数都被哪些函数调用,通过搜索,可以发现以下函数都会调用ip_append_data。
1. icmp_push_reply;
2. ip_send_reply;
3. raw_sendmsg;
4. udp_sendmsg;
而ip_send_reply又会被tcp_v4_send_reset和tcp_v4_send_ack调用。可见,ip_append_data基本上只用于udp socket和raw socket。
那为什么正常的TCP发送数据函数不使用这个函数呢。

从ip_append_data的注释上看
  1. /*
  2. * ip_append_data() and ip_append_page() can make one large IP datagram
  3. * from many pieces of data. Each pieces will be holded on the socket
  4. * until ip_push_pending_frames() is called. Each piece can be a page
  5. * or non-page data.
  6. *
  7. * Not only UDP, other transport protocols - e.g. raw sockets - can use
  8. * this interface potentially.
  9. *
  10. * LATER: length must be adjusted by pad at tail, when it is required.
  11. */
这个函数用于将多个数据,合成为一个大的IP数据包发送过去。——这里我有一个疑问。UDP 报文是有边界的。怎么能轻易的把多个UDP数据,合成一个大的IP报文呢?合成一个后,怎样区分不同的UDP数据包呢?
那么对于TCP的普通数据来说,TCP本身就是一个基于字节流,而非数据报的协议,另外TCP有自己的窗口控制发送数据的速度和大小,那么用这个函数来组成一个大的数据包,对于TCP就没有什么意义了。——这只是我自己的判断,毕竟还没有看到TCP的代码。

那么开始学习代码吧
  1. int ip_append_data(struct sock *sk,
  2.              int getfrag(void *from, char *to, int offset, int len,
  3.                       int odd, struct sk_buff *skb),
  4.              void *from, int length, int transhdrlen,
  5.              struct ipcm_cookie *ipc, struct rtable **rtp,
  6.              unsigned int flags)
  7. {
  8.      struct inet_sock *inet = inet_sk(sk);
  9.      struct sk_buff *skb;

  10.      struct ip_options *opt = NULL;
  11.      int hh_len;
  12.      int exthdrlen;
  13.      int mtu;
  14.      int copy;
  15.      int err;
  16.      int offset = 0;
  17.      unsigned int maxfraglen, fragheaderlen;
  18.      int csummode = CHECKSUM_NONE;
  19.      struct rtable *rt;

  20.      if (flags&MSG_PROBE)
  21.           return 0;
从今天开始,在学习代码的步骤中,增加对函数参数的说明。
struct sock *sk,这个很简单,就是linu内部的sock结构;getfrag,函数指针,从目前的代码上看,应该是对分片包的处理,例如计算checksum等;from,数据的起始指针;length,数据的长度;
transhdrlen,传输层报文头的长度;ipc,ip包control信息,以option为主;rtp,路由信息;flags,毫无疑问,标志位。
  1.     if (skb_queue_empty(&sk->sk_write_queue)) {
  2.           /*
  3.           * setup for corking.
  4.           */
  5.           opt = ipc->opt;
  6.           if (opt) {
  7.                if (inet->cork.opt == NULL) {
  8.                     inet->cork.opt = kmalloc(sizeof(struct ip_options) + 40, sk->sk_allocation);
  9.                     if (unlikely(inet->cork.opt == NULL))
  10.                          return -ENOBUFS;
  11.                }
  12.                memcpy(inet->cork.opt, opt, sizeof(struct ip_options)+opt->optlen);
  13.                inet->cork.flags |= IPCORK_OPT;
  14.                inet->cork.addr = ipc->addr;
  15.           }
  16.           rt = *rtp;
  17.           if (unlikely(!rt))
  18.                return -EFAULT;
  19.           /*
  20.           * We steal reference to this route, caller should not release it
  21.           */
  22.           *rtp = NULL;
  23.           inet->cork.fragsize = mtu = inet->pmtudisc == IP_PMTUDISC_PROBE ?
  24.                              rt->dst.dev->mtu :
  25.                              dst_mtu(rt->dst.path);
  26.           inet->cork.dst = &rt->dst;
  27.           inet->cork.length = 0;
  28.           sk->sk_sndmsg_page = NULL;
  29.           sk->sk_sndmsg_off = 0;
  30.           if ((exthdrlen = rt->dst.header_len) != 0) {
  31.                length += exthdrlen;
  32.                transhdrlen += exthdrlen;
  33.           }
  34.      }
如果该socket的发送队列为空,那么就需要设置该socket的cork。如果有option信息的话,首先就要复制option信息到socket的cork中,然后设置cork的其他变量,包括分片大小,下一跳地址等。
  1.      if (skb_queue_empty(&sk->sk_write_queue)) {
  2.          /* skip some codes */
  3.      } else {
  4.           rt = (struct rtable *)inet->cork.dst;
  5.           if (inet->cork.flags & IPCORK_OPT)
  6.                opt = inet->cork.opt;

  7.           transhdrlen = 0;
  8.           exthdrlen = 0;
  9.           mtu = inet->cork.fragsize;
  10.      }
如果socket的发送队列不为空,那么就直接从cork中取得路由,如果设置了cork的标志位IPCORK_OPT,表明cork中有option,那么就直接引用cork中的option。

看到这里我有一个疑问,没有想清楚。这里的ipc->opt是由udp_sendmsg传下来的,而在udp_sendmsg中,ipc->opt是从用户API中的参数获得的。如果在调用sendto时,该socket的发送缓冲不为空,那么这个opt就不会设置上。岂不是此次操作并没有成功。而且ipc->opt的值也没有被保存,那么此次操作的opt岂不是就丢掉了。望内核高手指教,多谢!

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
java TCP/UDP、Socket、URL网络编程详解
java TCP/UDP、Socket、URL网络编程详解
65 0
基于UDP协议的Socket通信
TCP和UDP最大的区别在于是否需要客户端与服务端建立连接后才能进行数据传输,如果你学习前面的TCP,传输前先开服务端,accept,等客户端接入,然后获得客户端socket然后进行IO操作,而UDP则不用,UDP以数据报作为数据的传输载体,在进行传输时首先要把传输的数据定义成数据报(Datagram),在数据报中指明数据要到达的Socket(主机地址和端口号),然后再将数据以数据报的形式发送出去,服务端收不收到我就不知道了,除非服务端收到后又给我回一段确认的数据报。
18 0
一文了解HTTP、HTTPS、TCP、UDP、Websocket(论点:概念、通信流程、异同点、应用领域)
一文了解HTTP、HTTPS、TCP、UDP、Websocket(论点:概念、通信流程、异同点、应用领域)
115 0
udp客户端收发数据流程
1、创建客户端socket开始进行通讯。2、这时服务端应该先启动,并在知道服务端的ip以及端口号的时候才能进行通讯。3、本地不需要绑定ip以及端口号,在用此套接字对象发送消息的时候会自动分配活动端口(1024-65535) 每次重启程序可能每次都不一样。
1201 0
udp服务端收发数据流程
1、创建服务端的socket以便开始通讯。2、绑定ip以及端口号,这样客户端才能找到这个程序。3、因为本地网卡不止一个所以尽量不写死,一般用""空来表示所有本地网卡。4、接下来开始通过绑定的ip以及端口开始监听消息,设置最大接收1024字节消息,以防文件过大,占满网络缓存区。
1220 0
UDP socket流程(14)——ip_local_out及其调用的函数
作者:gfree.wind@gmail.com 博客:linuxfocus.blog.chinaunix.net ip_local_out的代码很短 int ip_local_out(struct sk_buff *skb) {     int err;     /*      调用netfilter的hook检查该包是否可以发送。
985 0
UDP socket流程(13)——ip_push_pending_frames
作者:gfree.wind@gmail.com 博客:linuxfocus.blog.chinaunix.net 今天开始学习新的函数ip_push_pending_frames,这个函数会被icmp_push_reply,ip_send_reply,raw_sendmsg,和udp_push_pending_frames调用。
931 0
UDP socket流程(12)——udp_push_pending_frames
作者:gfree.wind@gmail.com博客:linuxfocus.blog.chinaunix.net 本来按照昨天的想法,是想把ip_append_data以及之前遇到的问题和不解整理思考一下的,但今天还是想把send的流程走完,再总结比较好,那么就开始下一个API——udp_push_pending_frames吧。
1160 0
内核中的UDP socket流程(11)——ip_append_data
作者:gfree.wind@gmail.com博客:linuxfocus.blog.chinaunix.net 继续ip_append_data,         if (copy > length)             copy = length;         if (!(rt->dst.
1235 0
内核中的UDP socket流程(10)——ip_append_data
作者:gfree.wind@gmail.com 博客:linuxfocus.blog.chinaunix.net 现在换一种风格,把一些对代码的解读直接写到代码段。那样看起来可能更好 继续ip_append_data,     /* hh_len是hardware header...
1112 0
+关注
xumaojun
乐于学习与分析
文章
问答
视频
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载