TCP输出 之 tcp_write_xmit

概述

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

源码分析
1 1 static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle, 2 2 int push_one, gfp_t gfp) 3 3 { 4 4 struct tcp_sock *tp = tcp_sk(sk); 5 5 struct sk_buff *skb; 6 6 unsigned int tso_segs, sent_pkts; 7 7 int cwnd_quota; 8 8 int result; 9 9 bool is_cwnd_limited = false, is_rwnd_limited = false; 10 10 u32 max_segs; 11 11 12 12 /* 已发送数据段数量 */ 13 13 sent_pkts = 0; 14 14 15 15 /* 发送多个数据段 */ 16 16 if (!push_one) { 17 17 /* Do MTU probing. */ 18 18 /* 发送路径mtu探测 */ 19 19 result = tcp_mtu_probe(sk); 20 20 /* 失败 */ 21 21 if (!result) { 22 22 return false; 23 23 } 24 24 /* 成功,设置已发送数据段数为1 */ 25 25 else if (result > 0) { 26 26 sent_pkts = 1; 27 27 } 28 28 } 29 29 30 30 /* 获取最大tso分段 */ 31 31 max_segs = tcp_tso_segs(sk, mss_now); 32 32 33 33 /* 有数据段要发送 */ 34 34 while ((skb = tcp_send_head(sk))) { 35 35 unsigned int limit; 36 36 37 37 /* 初始化tso分段相关 */ 38 38 tso_segs = tcp_init_tso_segs(skb, mss_now); 39 39 BUG_ON(!tso_segs); 40 40 41 41 if (unlikely(tp->repair) && tp->repair_queue == TCP_SEND_QUEUE) { 42 42 /* "skb_mstamp" is used as a start point for the retransmit timer */ 43 43 skb_mstamp_get(&skb->skb_mstamp); 44 44 goto repair; /* Skip network transmission */ 45 45 } 46 46 47 47 /* 检测拥塞窗口大小 */ 48 48 cwnd_quota = tcp_cwnd_test(tp, skb); 49 49 /* 为0 */ 50 50 if (!cwnd_quota) { 51 51 /* 尾部丢失探测段,设置为1 */ 52 52 if (push_one == 2) 53 53 /* Force out a loss probe pkt. */ 54 54 cwnd_quota = 1; 55 55 /* 其他情况,跳出 */ 56 56 else 57 57 break; 58 58 } 59 59 60 60 /* 检查tcp的数据段是否在发送窗口之内 */ 61 61 if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now))) { 62 62 /* 不在,标记,跳出 */ 63 63 is_rwnd_limited = true; 64 64 break; 65 65 } 66 66 67 67 /* 不需要tso分段 */ 68 68 if (tso_segs == 1) { 69 69 /* 检查nagle算法是否允许发送数据段 */ 70 70 if (unlikely(!tcp_nagle_test(tp, skb, mss_now, 71 71 (tcp_skb_is_last(sk, skb) ? 72 72 nonagle : TCP_NAGLE_PUSH)))) 73 73 break; 74 74 } 75 75 /* 需要tso分段 */ 76 76 else { 77 77 /* 检查是否可以延迟发送 */ 78 78 if (!push_one && 79 79 tcp_tso_should_defer(sk, skb, &is_cwnd_limited, 80 80 max_segs)) 81 81 break; 82 82 } 83 83 84 84 /* 设置分段长度限制为mss */ 85 85 limit = mss_now; 86 86 87 87 /* 需要分段 && 非紧急模式,重新确定分段长度限制 */ 88 88 if (tso_segs > 1 && !tcp_urg_mode(tp)) 89 89 limit = tcp_mss_split_point(sk, skb, mss_now, 90 90 min_t(unsigned int, 91 91 cwnd_quota, 92 92 max_segs), 93 93 nonagle); 94 94 95 95 /* skb中数据段长度>分段长度限制,则进行分段,会申请新的skb */ 96 96 if (skb->len > limit && 97 97 unlikely(tso_fragment(sk, skb, limit, mss_now, gfp))) 98 98 break; 99 99 100100 if (test_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags)) 101101 clear_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags); 102102 if (tcp_small_queue_check(sk, skb, 0)) 103103 break; 104104 105105 /* 发送分段数据 */ 106106 if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp))) 107107 break; 108108 109109 repair: 110110 /* Advance the send_head. This one is sent out. 111111 * This call will increment packets_out. 112112 */ 113113 /* 进行发送之后的数据更新,包括统计计数和定时器等 */ 114114 tcp_event_new_data_sent(sk, skb); 115115 116116 /* 更新最新发送小包的结束序号 */ 117117 tcp_minshall_update(tp, mss_now, skb); 118118 119119 /* 更新发送数据段数量 */ 120120 sent_pkts += tcp_skb_pcount(skb); 121121 122122 /* 只发送一个段,则跳出 */ 123123 if (push_one) 124124 break; 125125 } 126126 127127 if (is_rwnd_limited) 128128 tcp_chrono_start(sk, TCP_CHRONO_RWND_LIMITED); 129129 else 130130 tcp_chrono_stop(sk, TCP_CHRONO_RWND_LIMITED); 131131 132132 /* 本次有数据发送,拥塞相关数据更新 */ 133133 if (likely(sent_pkts)) { 134134 if (tcp_in_cwnd_reduction(sk)) 135135 tp->prr_out += sent_pkts; 136136 137137 /* Send one loss probe per tail loss episode. */ 138138 /* 每次发送一个尾部丢失探测 */ 139139 if (push_one != 2) 140140 tcp_schedule_loss_probe(sk); 141141 142142 /* 拥塞窗口校验 */ 143143 is_cwnd_limited |= (tcp_packets_in_flight(tp) >= tp->snd_cwnd); 144144 tcp_cwnd_validate(sk, is_cwnd_limited); 145145 return false; 146146 } 147147 148148 149149 /* 本次无数据发送,已发出未确认的数据段不为0或者发送队列为空,认为成功 */ 150150 return !tp->packets_out && tcp_send_head(sk); 151151 }
点赞
收藏

评论区

加载中...

相关推荐

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

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

TCP面试题之滑动窗口原理

TCP滑动窗口作用:1.提供TCP可靠性:对发送的数据进行确认2.流量控制:窗口大小随链路变化一、TCP窗口机制TCP中窗口大小是指tcp协议一次传输多少个数据。因为TCP是一个面向连接的可靠的传输协议,既然是可靠的就需要传输的数据进行确认。TCP窗口机制有两种,一种是