MPTCP 源码分析(四) 发送和接收数据

简述:

MPTCP在发送数据方面和TCP的区别是可以从多条路径中选择一条

路径来发送数据。MPTCP在接收数据方面与TCP的区别是子路径对无序包

进行重排后,MPTCP的mpcb需要多所有子路径的包进行排序。查看图1可知。

                                   +-------------------------------+
                                   |           Application         |
      +---------------+            +-------------------------------+
      |  Application  |            |             MPTCP             |
      +---------------+            + - - - - - - - + - - - - - - - +
      |      TCP      |            | Subflow (TCP) | Subflow (TCP) |
      +---------------+            +-------------------------------+
      |      IP       |            |       IP      |      IP       |
      +---------------+            +-------------------------------+

      Figure 1: Comparison of Standard TCP and MPTCP Protocol Stacks

数据序号映射(Data Sequence Mapping) 

由于所有的数据会通过不同的子路径发送,在接收端MPTCP需要对数据进行重新排序。

因此我们需要数据序号映射。数据序号映射定义从子路径序列空间到数据序列空间的映射。

子路径的序列空间是子路径自身的序列号,而数据序列空间维护着所有需发送的数据。如下图

  红色子路径上的子路径序号分别是1、2,其数据序号是1000、1002。而下面的蓝色的子路径上的子路径序号和

数据序号分别是200,1001。这说明从下面的蓝色子路径已经发送了199个报文,而上面的红色子路径才开始发送。

在MPTCP协议定义如下:

                      1                   2                   3
      0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     +--------------------------------------------------------------+
     |                                                              |
     |                Data Sequence Number (8 octets)               |
     |                                                              |
     +--------------------------------------------------------------+
     |              Subflow Sequence Number (4 octets)              |
     +-------------------------------+------------------------------+
     |  Data-Level Length (2 octets) |        Zeros (2 octets)      |
     +-------------------------------+------------------------------+

内核中的实现:

函数mptcp_write_dss_mapping对 Data Sequeue Number  和  Subflow Sequence Number进行了赋值。实现如下:

"net/mptcp/mptcp_output.c" line 318 of 1667
318 static int mptcp_write_dss_mapping(struct tcp_sock *tp, struct sk_buff *skb,
319                    __be32 *ptr)
320 {
321     struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
322     __be32 *start = ptr;
323     __u16 data_len;
324
325     *ptr++ = htonl(tcb->seq); /* data_seq */
326
327     /* If it‘s a non-data DATA_FIN, we set subseq to 0 (draft v7) */
328     if (mptcp_is_data_fin(skb) && skb->len == 0)
329         *ptr++ = 0; /* subseq */
330     else
331         *ptr++ = htonl(tp->write_seq - tp->mptcp->snt_isn); /* subseq */
332 

第325行和331行分别对子路径序号和数据序号进行了赋值。

###

data_seq and subseq

The mapping is identify by the relative subflow seq, the data seq and
the data len. Basically, it means that isn+sub_seq->isn+sub_seq+len at
the subflow-level corresponds to data_seq->data_seq+len at the
connection-level.

###

数据接收中的重组

内核使用三种队列接收数据,分别是:Backlog queue(sk->backlog)、Prequeue queue(tp->ucopy.prequeue)

和 Receive queue (sk->receeive_queue)。MPTCP的实现增加了一个新的队列out-of-order queue对于各个子路径

收到的数据进行重组。内核中 tcp_v4_rcv()的关键实现如下:

"net/ipv4/tcp_ipv4.c" line 1735 of 2581
1735     if (mptcp(tcp_sk(sk))) {
1736         meta_sk = mptcp_meta_sk(sk);
1737
1738         bh_lock_sock_nested(meta_sk);
1739         if (sock_owned_by_user(meta_sk))
1740             skb->sk = sk;
1741     } else {
1742         meta_sk = sk;
1743         bh_lock_sock_nested(sk);
1744     }
1745
1746     ret = 0;
1747     if (!sock_owned_by_user(meta_sk)) {
1748 #ifdef CONFIG_NET_DMA
1749         struct tcp_sock *tp = tcp_sk(meta_sk);
1750         if (!tp->ucopy.dma_chan && tp->ucopy.pinned_list)
1751             tp->ucopy.dma_chan = net_dma_find_channel();
1752         if (tp->ucopy.dma_chan)
1753             ret = tcp_v4_do_rcv(sk, skb);
1754         else
1755 #endif
1756         {
1757             if (!tcp_prequeue(meta_sk, skb))
1758                 ret = tcp_v4_do_rcv(sk, skb);
1759         }
1760     } else if (unlikely(sk_add_backlog(meta_sk, skb,
1761                        meta_sk->sk_rcvbuf + meta_sk->sk_sndbuf))) {
1762         bh_unlock_sock(meta_sk);
1763         NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP);
1764         goto discard_and_relse;
1765     }
1766     bh_unlock_sock(meta_sk);

从第1757和1760可以看出skb只进入meta的backlog和prequeue,而和子路径的sock没有什么关系。因此,我们得出包的入队操作如下:

1.进入meta_sk的backlog

2.进入meta_sk的prequeue

3.进入子路径的receive_queue

第1和2种入队操作后续操作和正常TCP一致,如果是第3种情况,后续将通过函数mptcp_queue_skb()进入tcp_sk(meta_sk)->out_of_order_queue。

结论:

1.MPTCP利用自身的Data Sequeue Number  和  Subflow Sequence Number进行了数据在各种子路径间的传输。此实现独立于TCP。

2.为了实现子路径的数据重组,MPTCP利用了队列out_of_order_queue。

问题:

1. DATA_ACK作用是?



The Data ACK is analogous to the behavior of the  standard TCP
 cumulative ACK -- indicating how much data has been  successfully received (with no holes). The Data ACK specifies the next data sequence number it expects to
receive.

				
时间: 2024-10-05 23:34:52

MPTCP 源码分析(四) 发送和接收数据的相关文章

MPTCP 源码分析(五) 接收端窗口值

简述: 在TCP协议中影响数据发送的三个因素分别为:发送端窗口值.接收端窗口值和拥塞窗口值. 本文主要分析MPTCP中各个子路径对接收端窗口值rcv_wnd的处理. 接收端窗口值的初始化 根据<MPTCP 源码分析(二) 建立子路径>中描述服务端在发送完SYN/ACK并接收到ACK的时候建立新的sock. 在内核实现中,针对连接请求分为两个步骤处理: SYN队列处理:当服务端收到SYN的时候,此连接请求request_sock将被存放于listening socket的SYN队列,服务端发送S

baksmali和smali源码分析(四)

baksmali 首先执行的第一个main 函数     public static void main(String[] args) throws IOException {         Locale locale = new Locale("en", "US");         Locale.setDefault(locale);         CommandLineParser parser = new PosixParser();         C

Nouveau源码分析(四):NVIDIA设备初始化之nouveau_drm_load (1)

Nouveau源码分析(四) probe函数成功返回之后,DRM模块就会调用struct drm_driver的load函数,对应nouveau的nouveau_drm_load. 这个函数虽然看起来不是特别长,但每一个调用的函数展开后就会变得非常长了! // /drivers/gpu/drm/nouveau/nouveau_drm.c 364 static int 365 nouveau_drm_load(struct drm_device *dev, unsigned long flags)

mybatis源码分析(四) mybatis与spring事务管理分析

mybatis源码分析(四) mybatis与spring事务管理分析 一丶从jdbc的角度理解什么是事务 从mysql获取一个连接之后, 默认是自动提交, 即执行完sql之后, 就会提交事务. 这种事务的范围是一条sql语句. 将该连接设置非自动提交, 可以执行多条sql语句, 然后由程序决定是提交事务, 还是回滚事务. 这也是我们常说的事务. Connection connection = dataSource.getConnection(); // connection.setTransa

ifconfig源码分析之与内核交互数据

<ifconfig源码分析之与内核交互数据>本文档的Copyleft归rosetta所有,使用GPL发布,可以自由拷贝.转载,转载时请保持文档的完整性.参考资料:<Linux设备驱动程序 第三版>,scull源码,Linux内核源码来源:http://blog.csdn.net/rosetta/article/details/7563615 ifconifg是Linux提供的一个操作网络接口的应用层程序,虽然和设备驱动编写没什么联系,但分析它的部分核心代码有助于理解应用层和内核层交

vlc源码分析之调用live555接收RTSP数据

首先了解RTSP/RTP/RTCP相关概念,尤其是了解RTP协议:RTP与RTCP协议介绍(转载). vlc使用模块加载机制调用live555,调用live555的文件是live555.cpp. 一.几个重要的类 以下向左箭头("<-")为继承关系. 1. RTPInterface RTPInterface是RTPSource的成员变量,其成员函数handleRead会读取网络数据存入BufferedPacket内,该类最终会调到UDP的发送接收函数. Boolean RTPIn

ABP源码分析四十七:ABP中的异常处理

ABP 中异常处理的思路是很清晰的.一共五种类型的异常类. AbpInitializationException用于封装ABP初始化过程中出现的异常,只要抛出AbpInitializationException异常就可以,无须做额外处理.这类异常往往是需要维护人员介入分析的. 其他四个异常都在AbpController中被集中处理,处理分为两步:一,通过EventBus触发异常事件,相应的异常处理函数则处理异常.而针对AbpValidationException,UserFriendlyExce

docker 源码分析 四(基于1.8.2版本),Docker镜像的获取和存储

前段时间一直忙些其他事情,docker源码分析的事情耽搁了,今天接着写,上一章了解了docker client 和 docker daemon(会启动一个http server)是C/S的结构,client端发出的命令由docker daemon接收并处理. 我们在运行docker的时候,可能会使用到docker run命令(当然通过Dockerfile运行docker build命令也是一样的)时,如果本地没有你需要的镜像,docker daemon首先会去下载你需要的docker镜像,然后存

[Hadoop] - TaskTracker源码分析(状态发送)

TaskTracker节点向JobTracker汇报当前节点的运行时信息时候,是将运行状态信息同心跳报告一起发送给JobTracker的,主要包括TaskTracker的基本信息.节点资源使用信息.各任务状态等.所以信息被序列化为TaskTrackerStatus实例对象.每次发送心跳报告的时候,会重新构造一个Status对象,并重置这些信息,而且需要主要的是每次发送的status对象的大小是不一定的,因为很多信息的发送是有时间间隔的.这些操作主要位于方法transmitHeartBeat的上半