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

简介: 作者:gfree.wind@gmail.com博客:linuxfocus.blog.chinaunix.net现在换一种风格,把一些对代码的解读直接写到代码段。那样看起来可能更好继续ip_append_data,    /* hh_len是hardware header...
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net

现在换一种风格,把一些对代码的解读直接写到代码段。那样看起来可能更好

继续ip_append_data,
  1.     /* hh_len是hardware header的长度,也就是2层帧头长度 */
  2.     hh_len = LL_RESERVED_SPACE(rt->dst.dev);
      
     /*fragheader是3层IP报文头长度(正常报文头+IP选项的长度)*/
  1.     fragheaderlen = sizeof(struct iphdr) (opt ? opt->optlen : 0);
  2.     /* maxfraglen为4层实际数据的最大长度(不含IP报文头),为MTU减去IP头长然后对齐 */
  3.     maxfraglen = ((mtu - fragheaderlen) & ~7) fragheaderlen;
     /* 这里对长度进行检查,因为最大的数据包长为65535,如果不满足条件,则出错返回 */
  1.     if (inet->cork.length+length > 0xFFFF - fragheaderlen) {
  2.         ip_local_error(sk, EMSGSIZE, rt->rt_dst, inet->inet_dport,
  3.              mtu-exthdrlen);
  4.         return -EMSGSIZE;
  5.     }
现在还是不明白inet->cork的用法,以后一定得搞明白。
  1.     /*
  2.     第一个分片(或者是正常的普通IP数据包),长度小于MTU(即不会被分片),输出数据的网卡支持校验和,且没     有其它的报文头(exthdrlen),如IPSec头等,如果条件为真,则标识此报文由硬件做校验和。
  3.     */
  4.     /*
  5.      * transhdrlen > 0 means that this is the first fragment and we wish
  6.      * it won't be fragmented in the future.
  7.      */
  8.     if (transhdrlen &&
  9.      length + fragheaderlen = mtu &&
  10.      rt->dst.dev->features & NETIF_F_V4_CSUM &&
  11.      !exthdrlen)
  12.         csummode = CHECKSUM_PARTIAL
     /* 得到上一个数据buf */
  1.     skb = skb_peek_tail(&sk->sk_write_queue);
  1.     inet->cork.length += length;
  2.     /* 
  3.     如果数据长度超过MTU,且是UDP协议,网卡支持UDP分片,那么就进行UDP分片。
  4.     这里的skb_is_gso(skb)判断,不知道是为了什么,留待以后研究。
  5.     */
  6.     if (((length > mtu) || (skb && skb_is_gso(skb))) &&
  7.      (sk->sk_protocol == IPPROTO_UDP) &&
  8.      (rt->dst.dev->features & NETIF_F_UFO)) {
  9.         /* 进行UDP分片 */
  10.         err = ip_ufo_append_data(sk, getfrag, from, length, hh_len,
  11.                      fragheaderlen, transhdrlen, mtu,
  12.                      flags);
  13.         if (err)
  14.             goto error;
  15.         return 0;
  16.     }

  17.     /* So, what's going on in the loop below?
  18.      *
  19.      * We use calculated fragment length to generate chained skb,
  20.      * each of segments is IP fragment ready for sending to network after
  21.      * adding appropriate IP header.
  22.      */
  23.     /*
  24.     之前没有任何的数据buffer
  25.     */
  26.     if (!skb)
  27.         goto alloc_new_skb;

  28.     while (length > 0) {
  29.         /* Check if the remaining data fits into current packet. */
  30.         /* 得到剩余空间 */
  31.         copy = mtu - skb->len;
  32.         /* 当剩余空间无法容纳当前数据时 */
  33.         if (copy length)
  34.             copy = maxfraglen - skb->len;
  35.         if (copy = 0) {
  36.             /* 数据超过最大分片长度,需要分片 */
  37.             char *data;
  38.             unsigned int datalen;
  39.             unsigned int fraglen;
  40.             unsigned int fraggap;
  41.             unsigned int alloclen;
  42.             struct sk_buff *skb_prev;
  43. alloc_new_skb:
  44.             /* 检查上一个buffer中遗留的数据长度 */
  45.             skb_prev = skb;
  46.             if (skb_prev)
  47.                 fraggap = skb_prev->len - maxfraglen;
  48.             else
  49.                 fraggap = 0;

  50.             /*
  51.              * If remaining data exceeds the mtu,
  52.              * we know we need more fragment(s).
  53.              */
  54.             /* 要发送的数据长度等于本次数据长度加上上次遗留的分片数据长度 */
  55.             datalen = length + fraggap;
  56.             /* 如果超过了最大分片数据长度,那么要发送的数据长度就为最大分片数据长度-IP头长*/
  57.             if (datalen > mtu - fragheaderlen)
  58.                 datalen = maxfraglen - fragheaderlen;
  59.             /* 这里的datalen就是真正的3层IP数据, 那么fraglen就是2层数据帧的数据长度 */
  60.             fraglen = datalen + fragheaderlen;
     
             /* 
             计算要申请的内存长度:
             flags被置上MSG_MORE位,表示还有其它分片,且网卡不支持Scatter/gather IO,那么申请内存长              度就为MTU,如果条件为假,申请长度就是真实的2层数据帧的数据长度。
             
     Scatter/gather IO特性说明如下:表示IO设备可以对物理上不连续的内存进行操纵,就不用进行不必              要的拷贝使之成为连续的内存块了。            
             */
  1.             if ((flags & MSG_MORE) &&
  2.              !(rt->dst.dev->features&NETIF_F_SG))
  3.                 alloclen = mtu;
  4.             else
  5.                 alloclen = datalen + fragheaderlen;

  6.             /* The last fragment gets additional space at tail.
  7.              * Note, with MSG_MORE we overallocate on fragments,
  8.              * because we have no idea what fragment will be
  9.              * the last.
  10.              */
  11.             if (datalen == length + fraggap)
  12.                 alloclen += rt->dst.trailer_len;
             /* 申请sk_buff */
  1.             if (transhdrlen) {
  2.                 skb = sock_alloc_send_skb(sk,
  3.                         alloclen + hh_len + 15,
  4.                         (flags & MSG_DONTWAIT), &err);
  5.             } else {
  6.                 skb = NULL;
  7.                 if (atomic_read(&sk->sk_wmem_alloc) =
  8.                  2 * sk->sk_sndbuf)
  9.                     skb = sock_wmalloc(sk,
  10.                              alloclen + hh_len + 15, 1,
  11.                              sk->sk_allocation);
  12.                 if (unlikely(skb == NULL))
  13.                     err = -ENOBUFS;
  14.                 else
  15.                     /* only the initial fragment is
  16.                      time stamped */
  17.                     ipc->shtx.flags = 0;
  18.             }
  19.             if (skb == NULL)
  20.                 goto error;

  21.             /*
  22.              *    Fill in the control structures
  23.              */
  24.             skb->ip_summed = csummode;
  25.             skb->csum = 0;
  26.             skb_reserve(skb, hh_len);
  27.             *skb_tx(skb) = ipc->shtx;

  28.             /*
  29.              *    Find where to start putting bytes.
  30.              */
  31.             /* 得到可以填充数据的起始位置 */
  32.             data = skb_put(skb, fraglen);
  33.             /* 设置IP头的位置 */
  34.             skb_set_network_header(skb, exthdrlen);
  35.             /* 得到传输层报文头的位置 */
  36.             skb->transport_header = (skb->network_header +
  37.                          fragheaderlen);
  38.             data += fragheaderlen;

  39.             if (fraggap) {
  40.                 /* 将上一个分片的数据先复制到buffer中 */
  41.                 skb->csum = skb_copy_and_csum_bits(
  42.                     skb_prev, maxfraglen,
  43.                     data + transhdrlen, fraggap, 0);
  44.                 /* 调整上一个sk_buff的校验和 */
  45.                 skb_prev->csum = csum_sub(skb_prev->csum,
  46.                              skb->csum);
  47.                 data += fraggap;
  48.                 /* 这个函数不知道具体用于什么 */
  49.                 pskb_trim_unique(skb_prev, maxfraglen);
  50.             }
             /* 计算要拷贝的长度 */
  1.             copy = datalen - transhdrlen - fraggap;
  2.             /* 拷贝数据 */
  3.             if (copy > 0 && getfrag(from, data + transhdrlen, offset, copy, fraggap, skb) 0) {
  4.                 err = -EFAULT;
  5.                 kfree_skb(skb);
  6.                 goto error;
  7.             }

  8.             offset += copy;
  9.             length -= datalen - fraggap;
  10.             transhdrlen = 0;
  11.             exthdrlen = 0;
  12.             csummode = CHECKSUM_NONE;

  13.             /*
  14.              * Put the packet on the pending queue.
  15.              */
  16.             __skb_queue_tail(&sk->sk_write_queue, skb);
  17.             continue;
  18.         }
  19.         /* 未完待续 */
发现到了这个阶段,代码不太容易看懂了,因为有一些机制和概念仍然没有搞清楚,比如cork,还有buffer的组织。这估计要等到把UDP的这些代码看完,然后好好总结一下,才能明白




相关文章
|
6月前
|
存储 Python
Python网络编程基础(Socket编程) UDP 发送和接收数据
【4月更文挑战第10天】对于UDP客户端而言,发送数据是一个相对简单的过程。首先,你需要构建一个要发送的数据报,这通常是一个字节串(bytes)。然后,你可以调用socket对象的`sendto`方法,将数据报发送到指定的服务器地址和端口。
|
6月前
|
网络协议 网络性能优化 开发者
Python网络编程基础(Socket编程)UDP Socket编程
【4月更文挑战第8天】Python网络编程中,UDP与TCP协议各有特点。TCP提供可靠连接,确保数据顺序与完整性,适合文件传输等;UDP则无连接,速度快,常用于实时音视频,牺牲了数据可靠性。Python的socket库支持两者,开发者可根据需求选择。
|
6月前
|
存储 Python
Python网络编程基础(Socket编程)UDP客户端编程
【4月更文挑战第9天】在UDP通信中,客户端负责发送数据到服务器,并接收来自服务器的响应。与服务器不同,客户端通常不需要绑定到特定的地址和端口,因为它可以临时使用任何可用的端口来发送数据。下面,我们将详细讲解UDP客户端编程的基本步骤。
|
6月前
|
网络协议 Python
Python网络编程基础(Socket编程)创建UDP socket对象
【4月更文挑战第8天】在Python中创建UDP服务器涉及使用`socket`模块创建socket对象,如`udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)`,然后绑定到特定IP地址和端口,如`udp_socket.bind(('localhost', 12345))`。服务器通过`recvfrom`在无限循环中监听和接收数据报。这只是基础,实际应用还需处理接收、解析、响应及错误处理等。接下来可学习如何利用socket对象进行数据交互以构建完整服务器。
|
安全 网络协议 Java
Thread类的用法 && 线程安全 && 多线程代码案例 && 文件操作和 IO && 网络原理初识 &&UDP socket
Thread类的用法 && 线程安全 && 多线程代码案例 && 文件操作和 IO && 网络原理初识 &&UDP socket
70 0
|
1月前
|
网络协议 Linux 网络性能优化
Linux基础-socket详解、TCP/UDP
综上所述,Linux下的Socket编程是网络通信的重要组成部分,通过灵活运用TCP和UDP协议,开发者能够构建出满足不同需求的网络应用程序。掌握这些基础知识,是进行更复杂网络编程任务的基石。
102 1
|
6月前
|
网络协议 Java
Java的Socket编程:TCP/IP与UDP深入探索
Java的Socket编程:TCP/IP与UDP深入探索
98 0
|
2月前
|
网络协议 Linux
TCP 和 UDP 的 Socket 调用
【9月更文挑战第6天】
|
4月前
|
Java 数据格式
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
83 0
|
5月前
|
存储 网络协议 数据处理
【Socket】解决UDP丢包问题
UDP(用户数据报协议)是一种无连接的传输层协议,因其不保证数据包的顺序到达和不具备内置重传机制,导致在网络拥塞、接收缓冲区溢出或发送频率过快等情况下容易出现丢包现象。为应对这些问题,可以在应用层实现重传机制、使用前向纠错码等方法。这些方法在一定程度上可以缓解UDP通信中的丢包问题,提高数据传输的可靠性和效率。