MPTCP 源码分析(五) 接收端窗口值
来源:互联网 发布:京东聊天软件 编辑:程序博客网 时间:2024/06/05 01:16
简述: View Code
这个阶段为MPTCP的第一条子路径建立情况的三次握手,因此此时创建的socket的属性为master而非slave. View Code
在TCP协议中影响数据发送的三个因素分别为:发送端窗口值、接收端窗口值和拥塞窗口值。
本文主要分析MPTCP中各个子路径对接收端窗口值rcv_wnd的处理。
接收端窗口值的初始化
根据《MPTCP 源码分析(二) 建立子路径》中描述服务端在发送完SYN/ACK并接收到ACK的时候建立新的sock。
在内核实现中,针对连接请求分为两个步骤处理:
- SYN队列处理:当服务端收到SYN的时候,此连接请求request_sock将被存放于listening socket的SYN队列,服务端发送SYN/ACK并等待相应的ACK。
- accept队列处理:一旦等待的ACK收到,服务端将会创建新的socket,并将连接请求从listening socket的SYN队列移到其accept队列。
当服务端进入LINSTEN状态后,收到第一个SYN包后的处理流程如下:
详细的函数调用为:
tcp_v4_rcv
=》 tcp_v4_do_rcv
=》 tcp_rcv_state_process
=》mptcp_conn_request
=》tcp_v4_conn_request
=》tcp_conn_request
=》tcp_openreq_init
在函数tcp_conn_request中对连接请求request_sock进行了分配内存。
"net/ipv4/tcp_input.c" line 6097 of 61956097 req = inet_reqsk_alloc(rsk_ops);6098 if (!req)6099 goto drop;
在函数tcp_openreq_init中对request_sock进行了初始化操作。
1226 static inline void tcp_openreq_init(struct request_sock *req,1227 struct tcp_options_received *rx_opt,1228 struct sk_buff *skb)1229 { 1230 struct inet_request_sock *ireq = inet_rsk(req);1231 1232 req->rcv_wnd = 0; /* So that tcp_send_synack() knows! */1233 req->cookie_ts = 0;1234 tcp_rsk(req)->rcv_isn = TCP_SKB_CB(skb)->seq;1235 tcp_rsk(req)->rcv_nxt = TCP_SKB_CB(skb)->seq + 1;1236 tcp_rsk(req)->snt_synack = 0;1237 req->mss = rx_opt->mss_clamp;1238 req->ts_recent = rx_opt->saw_tstamp ? rx_opt->rcv_tsval : 0;1239 ireq->tstamp_ok = rx_opt->tstamp_ok;1240 ireq->sack_ok = rx_opt->sack_ok;1241 ireq->snd_wscale = rx_opt->snd_wscale;1242 ireq->wscale_ok = rx_opt->wscale_ok;1243 ireq->acked = 0;1244 ireq->ecn_ok = 0;1245 ireq->mptcp_rqsk = 0;1246 ireq->saw_mpc = 0;1247 ireq->ir_rmt_port = tcp_hdr(skb)->source;1248 ireq->ir_num = ntohs(tcp_hdr(skb)->dest);1249 }
第1232行对request_sock的rcv_wnd进行了初始化为0。
当服务端收到ACK的时候就会建立相应的socket。将会调用tcp_create_openreq_child函数实现,定义如下:
"include/net/tcp.h" line 578 of 1787struct sock *tcp_create_openreq_child(struct sock *sk, struct request_sock *req, struct sk_buff *skb);
对于rcv_wnd的处理具体如下:
"net/ipv4/tcp_minisocks.c" line 441 of 872512 newtp->window_clamp = req->window_clamp;513 newtp->rcv_ssthresh = req->rcv_wnd;514 newtp->rcv_wnd = req->rcv_wnd;515 newtp->rx_opt.wscale_ok = ireq->wscale_ok;
这个阶段为MPTCP的第一条子路径建立情况的三次握手,因此此时创建的socket的属性为master而非slave.
下面的情景为创建一条子路径的情况,当服务端收到第一个SYN包的函数调用情况如下:
函数mptcp_v4_join_request将会对连接请求request_sock进行内存分配并初始化。具体的调用如下:
mptcp_v4_join_request
=》tcp_conn_request
=》inet_reqsk_alloc
=》tcp_openreq_init
当客户端的ACK到达后,内核会将此连接请求request_sock的rcv_wnd赋值给slave subsocket.
master sock 和 slave sock之间接收端窗口值的关系
TCP在发包的时候会告诉对方自身的接收端窗口值。MPTCP的实现如下:
"net/mptcp/mptcp_output.c" line 992 of 1667 992 u16 mptcp_select_window(struct sock *sk)993 { 994 u16 new_win = tcp_select_window(sk);995 struct tcp_sock *tp = tcp_sk(sk);996 struct tcp_sock *meta_tp = mptcp_meta_tp(tp);997 998 meta_tp->rcv_wnd = tp->rcv_wnd;999 meta_tp->rcv_wup = meta_tp->rcv_nxt;1000 1001 return new_win;1002 }
第994获得最新的窗口值并返回。第998行将slave sock的rcv_wnd赋值给master sock。
第994行的函数tcp_select_window的实现如下:
"net/ipv4/tcp_output.c" line 275 of 3327275 u16 tcp_select_window(struct sock *sk)276 {277 struct tcp_sock *tp = tcp_sk(sk);278 /* The window must never shrink at the meta-level. At the subflow we279 * have to allow this. Otherwise we may announce a window too large280 * for the current meta-level sk_rcvbuf.281 */282 u32 cur_win = tcp_receive_window(mptcp(tp) ? tcp_sk(mptcp_meta_sk(sk)) : tp);283 u32 new_win = tp->__select_window(sk);
对于第283行的__select_window()函数,MPTCP的内核实现如下:
"net/mptcp/mptcp_output.c" line 771 of 1667771 u32 __mptcp_select_window(struct sock *sk)772 {773 struct inet_connection_sock *icsk = inet_csk(sk);774 struct tcp_sock *tp = tcp_sk(sk), *meta_tp = mptcp_meta_tp(tp);775 struct sock *meta_sk = mptcp_meta_sk(sk);776 int mss, free_space, full_space, window;777778 /* MSS for the peer's data. Previous versions used mss_clamp779 * here. I don't know if the value based on our guesses780 * of peer's MSS is better for the performance. It's more correct781 * but may be worse for the performance because of rcv_mss782 * fluctuations. --SAW 1998/11/1783 */784 mss = icsk->icsk_ack.rcv_mss;785 free_space = tcp_space(meta_sk);786 full_space = min_t(int, meta_tp->window_clamp,787 tcp_full_space(meta_sk));788789 if (mss > full_space)790 mss = full_space;791792 if (free_space < (full_space >> 1)) {793 icsk->icsk_ack.quick = 0;794795 if (tcp_memory_pressure)796 /* TODO this has to be adapted when we support different797 * MSS's among the subflows.798 */799 meta_tp->rcv_ssthresh = min(meta_tp->rcv_ssthresh,800 4U * meta_tp->advmss);801802 if (free_space < mss)803 return 0;804 }805806 if (free_space > meta_tp->rcv_ssthresh)807 free_space = meta_tp->rcv_ssthresh;808809 /* Don't do rounding if we are using window scaling, since the810 * scaled window will not line up with the MSS boundary anyway.811 */812 window = meta_tp->rcv_wnd;813 if (tp->rx_opt.rcv_wscale) {814 window = free_space;815816 /* Advertise enough space so that it won't get scaled away.817 * Import case: prevent zero window announcement if818 * 1<<rcv_wscale > mss.819 */820 if (((window >> tp->rx_opt.rcv_wscale) << tp->821 rx_opt.rcv_wscale) != window)822 window = (((window >> tp->rx_opt.rcv_wscale) + 1)823 << tp->rx_opt.rcv_wscale);824 } else {825 /* Get the largest window that is a nice multiple of mss.826 * Window clamp already applied above.827 * If our current window offering is within 1 mss of the828 * free space we just keep it. This prevents the divide829 * and multiply from happening most of the time.830 * We also don't do any window rounding when the free space831 * is too small.832 */833 if (window <= free_space - mss || window > free_space)834 window = (free_space / mss) * mss;835 else if (mss == full_space &&836 free_space > window + (full_space >> 1))837 window = free_space;838 }839840 return window;841 }
影响window的计算的因素为:
- 收到的MSS( icsk->icsk_ack.rcv_mss)
- 套接字缓冲区总的空间(tcp_full_space)
- 套接字缓冲区的空闲空间(tcp_space)
- meta_tp->rcv_ssthresh /* Current window clamp */
观察上面的代码可以知道MPTCP的实现和__tcp_select_window的区别是都是依据meta_tp,而非tp。这说明
master sock 和 其余slave sock使用相同的 rcv_wnd。
结论:
1.master sock 和 其余slave sock使用相同的接收缓冲区和 rcv_wnd。
0 0
- MPTCP 源码分析(五) 接收端窗口值
- MPTCP 源码分析(五) 接收端窗口值
- MPTCP 源码分析(四) 发送和接收数据
- MPTCP 源码分析(四) 发送和接收数据
- MPTCP 源码分析(0) MPTCP 理解
- MPTCP 源码分析(一) MPTCP的三次握手
- MPTCP 源码分析(一) MPTCP的三次握手
- MPTCP 源码分析(二) 建立子路径
- MPTCP 源码分析(三) 子路径选择
- MPTCP 源码分析(六) 数据重发
- MPTCP 源码分析(七) 拥塞控制
- MPTCP 源码分析(二) 建立子路径
- MPTCP 源码分析(三) 子路径选择
- MPTCP 源码分析(六) 数据重发
- MPTCP 源码分析(七) 拥塞控制
- MPTCP协议相关分析
- dubbo源码分析-consumer端6-数据发送与接收
- dubbo源码分析-consumer端6-数据发送与接收
- 神一样的对手?猪一样的队友?
- 贪婪算法
- 第七届蓝桥杯 省赛 JavaB组 生日蜡烛
- Android通讯录的管理(联系人的增删改查)
- dos里面export用set
- MPTCP 源码分析(五) 接收端窗口值
- Android Studio 中英文 汉化对照
- HashMap实现原理分析
- 每日学习20170301--单点登录实现方案
- 逻辑数据库设计-单纯的树(递归关系数据)
- 深入剖析Spring(四)——AOP
- Android 7.0 settings中添加新菜单
- 需要学习的
- 02_python_练习题——图形界面