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调用。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net

今天开始学习新的函数ip_push_pending_frames,这个函数会被icmp_push_reply,ip_send_reply,raw_sendmsg,和udp_push_pending_frames调用。该函数用于将该socket上的所有pending的IP分片,组成一个IP报文发送出去。

下面是函数的具体的代码。
  1. int ip_push_pending_frames(struct sock *sk)
  2. {
  3.     struct sk_buff *skb, *tmp_skb;
  4.     struct sk_buff **tail_skb;
  5.     struct inet_sock *inet = inet_sk(sk);
  6.     struct net *net = sock_net(sk);
  7.     struct ip_options *opt = NULL;
  8.     struct rtable *rt = (struct rtable *)inet->cork.dst;
  9.     struct iphdr *iph;
  10.     __be16 df = 0;
  11.     __u8 ttl;
  12.     int err = 0;
     /* 发送队列可能为空 */
  1.     if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL)
  2.         goto out;
  3.     /* 获得分片链表 */
  4.     tail_skb = &(skb_shinfo(skb)->frag_list);

  5.     /* move skb->data to ip header from ext header */
  6.     /* 调整data指针位置 */
  7.     if (skb->data skb_network_header(skb))
  8.         __skb_pull(skb, skb_network_offset(skb));
     /* 调整所有发送缓冲中的sk_buff的data指针位置,并更新第一个sk_buff的数据长度 */
  1.     while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {
  2.         __skb_pull(tmp_skb, skb_network_header_len(skb));
  3.         *tail_skb = tmp_skb;
  4.         tail_skb = &(tmp_skb->next);
  5.         skb->len = tmp_skb->len;
  6.         skb->data_len = tmp_skb->len;
  7.         skb->truesize = tmp_skb->truesize;
  8.         tmp_skb->destructor = NULL;
  9.         tmp_skb->sk = NULL;
  10.     }

  11.     /* Unless user demanded real pmtu discovery (IP_PMTUDISC_DO), we allow
  12.      * to fragment the frame generated here. No matter, what transforms
  13.      * how transforms change size of the packet, it will come out.
  14.      */
  15.     /* 允许本地分片 */
  16.     if (inet->pmtudisc IP_PMTUDISC_DO)
  17.         skb->local_df = 1;

  18.     /* DF bit is set when we want to see DF on outgoing frames.
  19.      * If local_df is set too, we still allow to fragment this frame
  20.      * locally. */
  21.     /* 不允许分片,或者不需要分片 */
  22.     if (inet->pmtudisc >= IP_PMTUDISC_DO ||
  23.      (skb->len = dst_mtu(&rt->dst) &&
  24.      ip_dont_fragment(sk, &rt->dst)))
  25.         df = htons(IP_DF);
      /* ip option 保存在cork中, 则使用cork中的option */
  1.     if (inet->cork.flags & IPCORK_OPT)
  2.         opt = inet->cork.opt;
      /* 选择合适的TTL值 */
  1.     if (rt->rt_type == RTN_MULTICAST)
  2.         ttl = inet->mc_ttl;
  3.     else
  4.         ttl = ip_select_ttl(inet, &rt->dst);
     /* 得到IP报文头的地址 */ 
  1.     iph = (struct iphdr *)skb->data;
  2.     /* 初始化IP报文头的内容 */
  3.     iph->version = 4;
  4.     iph->ihl = 5;
  5.     if (opt) {
  6.         /*
            填充IP option
  7.         看到这里,可以发现opt只可能从cork中获得
  8.          */
  9.         iph->ihl = opt->optlen>>2;
  10.         ip_options_build(skb, opt, inet->cork.addr, rt, 0);
  11.     }
  12.     iph->tos = inet->tos;
  13.     iph->frag_off = df;
  14.     ip_select_ident(iph, &rt->dst, sk);
  15.     iph->ttl = ttl;
  16.     iph->protocol = sk->sk_protocol;
  17.     iph->saddr = rt->rt_src;
  18.     iph->daddr = rt->rt_dst;

  19.     skb->priority = sk->sk_priority;
  20.     skb->mark = sk->sk_mark;
  21.     /*
  22.      * Steal rt from cork.dst to avoid a pair of atomic_inc/atomic_dec
  23.      * on dst refcount
  24.      */
  25.     inet->cork.dst = NULL;
  26.     skb_dst_set(skb, &rt->dst);

  27.     if (iph->protocol == IPPROTO_ICMP)
  28.         icmp_out_count(net, ((struct icmphdr *)
  29.             skb_transport_header(skb))->type);

  30.     /* Netfilter gets whole the not fragmented skb. */
  31.     /* 发送数据 */
  32.     err = ip_local_out(skb);
  33.     if (err) {
  34.         if (err > 0)
  35.             err = net_xmit_errno(err);
  36.         if (err)
  37.             goto error;
  38.     }

  39. out:
  40.     ip_cork_release(inet);
  41.     return err;

  42. error:
  43.     IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
  44.     goto out;
  45. }

这个函数比较容易看懂——当然不是所有细节都非常清楚了。
第一遍浏览这些API,只要搞懂该API的大致流程和用途。当整个框架建立好以后,再慢慢补充细节。




相关文章
|
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对象进行数据交互以构建完整服务器。
|
1月前
|
存储 网络协议 Java
【网络】UDP回显服务器和客户端的构造,以及连接流程
【网络】UDP回显服务器和客户端的构造,以及连接流程
49 2
|
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月前
|
C语言
C语言 网络编程(七)UDP通信创建流程
本文档详细介绍了使用 UDP 协议进行通信的过程,包括创建套接字、发送与接收消息等关键步骤。首先,通过 `socket()` 函数创建套接字,并设置相应的参数。接着,使用 `sendto()` 函数向指定地址发送数据。为了绑定地址,需要调用 `bind()` 函数。接收端则通过 `recvfrom()` 函数接收数据并获取发送方的地址信息。文档还提供了完整的代码示例,展示了如何实现 UDP 的发送端和服务端功能。
|
2月前
|
网络协议 Linux
TCP 和 UDP 的 Socket 调用
【9月更文挑战第6天】
|
4月前
|
Java 数据格式
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
83 0