TCP输出 之 tcp_write_xmit

Easter79
• 阅读 805
概述

tcp_write_xmit函数完成对待发送数据的分段发送,过程中会遍历发送队列,进行窗口检查,需要TSO分段则分段,然后调用tcp_transmit_skb发送数据段;

源码分析
  1 static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
  2                int push_one, gfp_t gfp)
  3 {
  4     struct tcp_sock *tp = tcp_sk(sk);
  5     struct sk_buff *skb;
  6     unsigned int tso_segs, sent_pkts;
  7     int cwnd_quota;
  8     int result;
  9     bool is_cwnd_limited = false, is_rwnd_limited = false;
 10     u32 max_segs;
 11 
 12     /* 已发送数据段数量 */
 13     sent_pkts = 0;
 14 
 15     /* 发送多个数据段 */
 16     if (!push_one) {
 17         /* Do MTU probing. */
 18         /* 发送路径mtu探测 */
 19         result = tcp_mtu_probe(sk);
 20         /* 失败 */
 21         if (!result) {
 22             return false;
 23         }
 24         /* 成功,设置已发送数据段数为1 */
 25         else if (result > 0) {
 26             sent_pkts = 1;
 27         }
 28     }
 29 
 30     /* 获取最大tso分段 */
 31     max_segs = tcp_tso_segs(sk, mss_now);
 32 
 33     /* 有数据段要发送 */
 34     while ((skb = tcp_send_head(sk))) {
 35         unsigned int limit;
 36 
 37         /* 初始化tso分段相关 */
 38         tso_segs = tcp_init_tso_segs(skb, mss_now);
 39         BUG_ON(!tso_segs);
 40 
 41         if (unlikely(tp->repair) && tp->repair_queue == TCP_SEND_QUEUE) {
 42             /* "skb_mstamp" is used as a start point for the retransmit timer */
 43             skb_mstamp_get(&skb->skb_mstamp);
 44             goto repair; /* Skip network transmission */
 45         }
 46 
 47         /* 检测拥塞窗口大小 */
 48         cwnd_quota = tcp_cwnd_test(tp, skb);
 49         /* 为0 */
 50         if (!cwnd_quota) {
 51             /* 尾部丢失探测段,设置为1 */
 52             if (push_one == 2)
 53                 /* Force out a loss probe pkt. */
 54                 cwnd_quota = 1;
 55             /* 其他情况,跳出 */
 56             else
 57                 break;
 58         }
 59 
 60         /* 检查tcp的数据段是否在发送窗口之内 */
 61         if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now))) {
 62             /* 不在,标记,跳出 */
 63             is_rwnd_limited = true;
 64             break;
 65         }
 66 
 67         /* 不需要tso分段 */
 68         if (tso_segs == 1) {
 69             /* 检查nagle算法是否允许发送数据段 */
 70             if (unlikely(!tcp_nagle_test(tp, skb, mss_now,
 71                              (tcp_skb_is_last(sk, skb) ?
 72                               nonagle : TCP_NAGLE_PUSH))))
 73                 break;
 74         } 
 75         /* 需要tso分段 */
 76         else {
 77             /* 检查是否可以延迟发送 */
 78             if (!push_one &&
 79                 tcp_tso_should_defer(sk, skb, &is_cwnd_limited,
 80                          max_segs))
 81                 break;
 82         }
 83 
 84         /* 设置分段长度限制为mss */
 85         limit = mss_now;
 86 
 87         /* 需要分段 && 非紧急模式,重新确定分段长度限制 */
 88         if (tso_segs > 1 && !tcp_urg_mode(tp))
 89             limit = tcp_mss_split_point(sk, skb, mss_now,
 90                             min_t(unsigned int,
 91                               cwnd_quota,
 92                               max_segs),
 93                             nonagle);
 94 
 95         /* skb中数据段长度>分段长度限制,则进行分段,会申请新的skb */
 96         if (skb->len > limit &&
 97             unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
 98             break;
 99 
100         if (test_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags))
101             clear_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags);
102         if (tcp_small_queue_check(sk, skb, 0))
103             break;
104 
105         /* 发送分段数据 */
106         if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
107             break;
108 
109 repair:
110         /* Advance the send_head.  This one is sent out.
111          * This call will increment packets_out.
112          */
113         /* 进行发送之后的数据更新,包括统计计数和定时器等 */
114         tcp_event_new_data_sent(sk, skb);
115 
116         /* 更新最新发送小包的结束序号 */
117         tcp_minshall_update(tp, mss_now, skb);
118 
119         /* 更新发送数据段数量 */
120         sent_pkts += tcp_skb_pcount(skb);
121 
122         /* 只发送一个段,则跳出 */
123         if (push_one)
124             break;
125     }
126 
127     if (is_rwnd_limited)
128         tcp_chrono_start(sk, TCP_CHRONO_RWND_LIMITED);
129     else
130         tcp_chrono_stop(sk, TCP_CHRONO_RWND_LIMITED);
131 
132     /* 本次有数据发送,拥塞相关数据更新 */
133     if (likely(sent_pkts)) {
134         if (tcp_in_cwnd_reduction(sk))
135             tp->prr_out += sent_pkts;
136 
137         /* Send one loss probe per tail loss episode. */
138         /* 每次发送一个尾部丢失探测 */
139         if (push_one != 2)
140             tcp_schedule_loss_probe(sk);
141 
142         /* 拥塞窗口校验 */
143         is_cwnd_limited |= (tcp_packets_in_flight(tp) >= tp->snd_cwnd);
144         tcp_cwnd_validate(sk, is_cwnd_limited);
145         return false;
146     }
147 
148     
149     /*  本次无数据发送,已发出未确认的数据段不为0或者发送队列为空,认为成功 */
150     return !tp->packets_out && tcp_send_head(sk);
151 }
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
TCP面试题之滑动窗口原理
TCP滑动窗口作用:1.提供TCP可靠性:对发送的数据进行确认2.流量控制:窗口大小随链路变化一、TCP窗口机制TCP中窗口大小是指tcp协议一次传输多少个数据。因为TCP是一个面向连接的可靠的传输协议,既然是可靠的就需要传输的数据进行确认。TCP窗口机制有两种,一种是
Wesley13 Wesley13
3年前
TCP可靠传输&流量控制&拥塞控制
TCP可靠传输TCP可靠传输的工作原理1.无差错1:无差错:A发送分组M1,发送就暂停发送,等待B的确认,B收到M1就向A发送确认,A收到对M1的确认后再发送下一个分组。(若A收到连续的M1分组的确认信息,则证明M2缺失)2:出现差错:
Stella981 Stella981
3年前
MFC_TCP_Server
!(https://oscimg.oschina.net/oscnet/44fdf01cd2e73dc5aa35a55874419856565.png)1//New_MFC_TCP_SreverDlg.h:头文件2//3defineWM_SOCKETWM_USER1004pragm
Wesley13 Wesley13
3年前
34.TCP取样器
阅读文本大概需要3分钟。1、TCP取样器的作用   TCP取样器作用就是通过TCP/IP协议来连接服务器,然后发送数据和接收数据。2、TCP取样器详解!(https://oscimg.oschina.net/oscnet/32a9b19ba1db00f321d22a0f33bcfb68a0d.png)TCPClien
Stella981 Stella981
3年前
Linux应急响应(二):捕捉短连接
0x00前言​短连接(shortconnnection)是相对于长连接而言的概念,指的是在数据传送过程中,只在需要发送数据时,才去建立一个连接,数据发送完成后,则断开此连接,即每次连接只完成一项业务的发送。在系统维护中,一般很难去察觉,需要借助网络安全设备或者抓包分析,才能够去发现。0x01应急场景​
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k