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的大致流程和用途。当整个框架建立好以后,再慢慢补充细节。




目录
相关文章
|
2月前
|
存储 Python
Python网络编程基础(Socket编程) UDP 发送和接收数据
【4月更文挑战第10天】对于UDP客户端而言,发送数据是一个相对简单的过程。首先,你需要构建一个要发送的数据报,这通常是一个字节串(bytes)。然后,你可以调用socket对象的`sendto`方法,将数据报发送到指定的服务器地址和端口。
|
2月前
|
存储 Python
Python网络编程基础(Socket编程)UDP客户端编程
【4月更文挑战第9天】在UDP通信中,客户端负责发送数据到服务器,并接收来自服务器的响应。与服务器不同,客户端通常不需要绑定到特定的地址和端口,因为它可以临时使用任何可用的端口来发送数据。下面,我们将详细讲解UDP客户端编程的基本步骤。
|
2月前
|
网络协议 网络性能优化 开发者
Python网络编程基础(Socket编程)UDP Socket编程
【4月更文挑战第8天】Python网络编程中,UDP与TCP协议各有特点。TCP提供可靠连接,确保数据顺序与完整性,适合文件传输等;UDP则无连接,速度快,常用于实时音视频,牺牲了数据可靠性。Python的socket库支持两者,开发者可根据需求选择。
|
2月前
|
网络协议 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对象进行数据交互以构建完整服务器。
|
2月前
|
网络协议 Java
Java的Socket编程:TCP/IP与UDP深入探索
Java的Socket编程:TCP/IP与UDP深入探索
26 0
|
1月前
|
存储 网络协议 数据处理
【Socket】解决UDP丢包问题
UDP(用户数据报协议)是一种无连接的传输层协议,因其不保证数据包的顺序到达和不具备内置重传机制,导致在网络拥塞、接收缓冲区溢出或发送频率过快等情况下容易出现丢包现象。为应对这些问题,可以在应用层实现重传机制、使用前向纠错码等方法。这些方法在一定程度上可以缓解UDP通信中的丢包问题,提高数据传输的可靠性和效率。
|
1月前
|
网络协议 应用服务中间件 网络性能优化
解析TCP /UDP协议的 socket 调用的过程
【6月更文挑战第2天】该文介绍了传输层的两种主要协议TCP和UDP的区别。TCP是面向连接、可靠的,提供顺序无错的数据传输,而UDP则是无连接、不可靠的,不保证数据顺序或不丢失。
|
2月前
|
网络协议 Linux
TCP 和 UDP 的 Socket 调用
【2月更文挑战第19天】
TCP 和 UDP 的 Socket 调用
|
2月前
|
Python
Python网络编程基础(Socket编程)UDP服务器编程
【4月更文挑战第8天】Python UDP服务器编程使用socket库创建UDP套接字,绑定到特定地址(如localhost:8000),通过`recvfrom`接收客户端数据报,显示数据长度、地址和内容。无连接的UDP协议使得服务器无法主动发送数据,通常需应用层实现请求-响应机制。当完成时,用`close`关闭套接字。
|
2月前
|
存储 网络协议 关系型数据库
Python从入门到精通:2.3.2数据库操作与网络编程——学习socket编程,实现简单的TCP/UDP通信
Python从入门到精通:2.3.2数据库操作与网络编程——学习socket编程,实现简单的TCP/UDP通信