概述

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

源码分析
 static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
int push_one, gfp_t gfp)
{
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb;
unsigned int tso_segs, sent_pkts;
int cwnd_quota;
int result;
bool is_cwnd_limited = false, is_rwnd_limited = false;
u32 max_segs; /* 已发送数据段数量 */
sent_pkts = ; /* 发送多个数据段 */
if (!push_one) {
/* Do MTU probing. */
/* 发送路径mtu探测 */
result = tcp_mtu_probe(sk);
/* 失败 */
if (!result) {
return false;
}
/* 成功,设置已发送数据段数为1 */
else if (result > ) {
sent_pkts = ;
}
} /* 获取最大tso分段 */
max_segs = tcp_tso_segs(sk, mss_now); /* 有数据段要发送 */
while ((skb = tcp_send_head(sk))) {
unsigned int limit; /* 初始化tso分段相关 */
tso_segs = tcp_init_tso_segs(skb, mss_now);
BUG_ON(!tso_segs); if (unlikely(tp->repair) && tp->repair_queue == TCP_SEND_QUEUE) {
/* "skb_mstamp" is used as a start point for the retransmit timer */
skb_mstamp_get(&skb->skb_mstamp);
goto repair; /* Skip network transmission */
} /* 检测拥塞窗口大小 */
cwnd_quota = tcp_cwnd_test(tp, skb);
/* 为0 */
if (!cwnd_quota) {
/* 尾部丢失探测段,设置为1 */
if (push_one == )
/* Force out a loss probe pkt. */
cwnd_quota = ;
/* 其他情况,跳出 */
else
break;
} /* 检查tcp的数据段是否在发送窗口之内 */
if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now))) {
/* 不在,标记,跳出 */
is_rwnd_limited = true;
break;
} /* 不需要tso分段 */
if (tso_segs == ) {
/* 检查nagle算法是否允许发送数据段 */
if (unlikely(!tcp_nagle_test(tp, skb, mss_now,
(tcp_skb_is_last(sk, skb) ?
nonagle : TCP_NAGLE_PUSH))))
break;
}
/* 需要tso分段 */
else {
/* 检查是否可以延迟发送 */
if (!push_one &&
tcp_tso_should_defer(sk, skb, &is_cwnd_limited,
max_segs))
break;
} /* 设置分段长度限制为mss */
limit = mss_now; /* 需要分段 && 非紧急模式,重新确定分段长度限制 */
if (tso_segs > && !tcp_urg_mode(tp))
limit = tcp_mss_split_point(sk, skb, mss_now,
min_t(unsigned int,
cwnd_quota,
max_segs),
nonagle); /* skb中数据段长度>分段长度限制,则进行分段,会申请新的skb */
if (skb->len > limit &&
unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
break; if (test_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags))
clear_bit(TCP_TSQ_DEFERRED, &sk->sk_tsq_flags);
if (tcp_small_queue_check(sk, skb, ))
break; /* 发送分段数据 */
if (unlikely(tcp_transmit_skb(sk, skb, , gfp)))
break; repair:
/* Advance the send_head. This one is sent out.
* This call will increment packets_out.
*/
/* 进行发送之后的数据更新,包括统计计数和定时器等 */
tcp_event_new_data_sent(sk, skb); /* 更新最新发送小包的结束序号 */
tcp_minshall_update(tp, mss_now, skb); /* 更新发送数据段数量 */
sent_pkts += tcp_skb_pcount(skb); /* 只发送一个段,则跳出 */
if (push_one)
break;
} if (is_rwnd_limited)
tcp_chrono_start(sk, TCP_CHRONO_RWND_LIMITED);
else
tcp_chrono_stop(sk, TCP_CHRONO_RWND_LIMITED); /* 本次有数据发送,拥塞相关数据更新 */
if (likely(sent_pkts)) {
if (tcp_in_cwnd_reduction(sk))
tp->prr_out += sent_pkts; /* Send one loss probe per tail loss episode. */
/* 每次发送一个尾部丢失探测 */
if (push_one != )
tcp_schedule_loss_probe(sk); /* 拥塞窗口校验 */
is_cwnd_limited |= (tcp_packets_in_flight(tp) >= tp->snd_cwnd);
tcp_cwnd_validate(sk, is_cwnd_limited);
return false;
} /* 本次无数据发送,已发出未确认的数据段不为0或者发送队列为空,认为成功 */
return !tp->packets_out && tcp_send_head(sk);
}

最新文章

  1. linux下的a.out文件
  2. C# 字典 Dictionary 转 JSON 格式遍历
  3. 报表引擎API开发入门—简单程序数据集
  4. 静态函数(面向过程的static关键字)
  5. mysql 商品表的设计思路(面向对象建表:类与对象)
  6. spring 中的 RowMapper
  7. Brackets 配置
  8. 【oracle】初学jobs
  9. HDU OJ 4334 Trouble 2012 Multi-University Training Contest 4
  10. 视频客观质量评价工具:MSU Video Quality Measurement Tool【ssim,psnr】
  11. python获取多线程的返回值
  12. FPGA學習筆記(肆)--- Star Test Bench Template Writer
  13. springboot 通过 hibernate 连接sqlserver 空间数据 位置数据
  14. JAVA-ORM框架整理➣Mybatis操作MySQL
  15. PAT基础6-11
  16. Atom选中多行操作
  17. (总结)Ubuntu apt-get apt-cache命令 使用
  18. vector读入指定行数但不指定列数的数字
  19. MACD判断定背离,底背离
  20. Innosetup的状态页面和向导页面解释

热门文章

  1. git clone ssh 时出现 fatal: Could not read from remote repository
  2. jsonp的跨域原理
  3. 创建json对象
  4. vue-无限滚动
  5. 在javascript中:(函数()()是一个匿名函数
  6. js小功能3:一个简单的计算器功能
  7. 4.ID主键生成策略
  8. beego注解路由的格式
  9. Delphi RS-232C标准
  10. arm交叉编译sudo-1.8.6p7