TCP连接接收过程 connect

当网络中一台主机向本地主机发起tcp连接请求时,它所发出的第一个tcp数据报是一个SYN,并带上自己的ISN(初始序号)。该数据报会被送往本地主机协议栈的mytcp_v4_rcv函数,该函数对数据进行一些基本的正确性检查后,从mytcp_hashinfo哈希表集中,寻找应当处理这个连接请求的socket。
    首先在ehash表的前半部分中查寻,即在处于TCP_ESTABLISH状态的socket中查寻匹配。匹配逻辑是:socket结构体成员sk_hash要等于根据请求数据报的目的地址和源地址等信息计算出来的哈希值,数据报的目的地址等于本地socket的接收源地址,数据报的源地址等于本地socket的目的地址,数据的源端口和目的端口与本地socket的目的端口与源端口匹配,并且,如果本地socket绑定了输入网络设备接口,那数据报的输入接口要跟本地socket的输入接口相同。可见,ehash表中的socket匹配是一个非常严格的完全匹配的过程,在前半部分没有找到,则到后半部分在TIME_WAIT状态的socket中继续寻找。
    因为这是一个连接请求,所以在ehash表中是不可能找到匹配的socket的。接下来到listening_hash中寻找侦听socket。只要目的地址和端口匹配,如果侦听socket绑定了输入接口,输入接口也匹配,则寻找成功。
    找到一个侦听socket后,首先在这个socket的accept队列(struct inet_connection_sock->icsk_accept_queue)中寻找匹配的request_sock,因为有可能该请求已经被接受,并保存在接受队列中了(但还没有被accept处理,因为如果accept处理过了,会出现在ehash表中)。下面先看一下request_sock结构体的定义:
    struct request_sock {
        struct request_sock     *dl_next;       //指向链表的下一个结点。
        u16             mss;
        u8              retrans;
        u8              __pad;
        u32             window_clamp;       //窗口上限值。
        u32             rcv_wnd;            //接收窗口大小。
        u32             ts_recent;
        unsigned long           expires;
        struct request_sock_ops     *rsk_ops;
        struct sock         *sk;
    };
    struct inet_request_sock是request_sock的扩展,其定义如下: 
    struct inet_request_sock {
        struct request_sock req;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
        u16         inet6_rsk_offset;
#endif
        u32         loc_addr;           //本地地址
        u32         rmt_addr;           //远端地址
        u16         rmt_port;           //远端端口
        u16         snd_wscale : 4,     //tcp远项
                    rcv_wscale : 4,
                    tstamp_ok  : 1,
                    sack_ok    : 1,
                    wscale_ok  : 1,
                    ecn_ok     : 1,
                    acked      : 1;
        struct ip_options   *opt;       //ip选项
    };
    寻找request_sock的逻辑是请求数据的源地址和端口跟rmt_addr和rmt_port匹配,目的地址跟loc_addr匹配。显然,对于刚刚发起第一个SYN的socket请求来讲,这种寻找总是失败的,它不可能已经在接受队列中存在。
    接下来到ehash表中去查找匹配的socket,显然这种查找也是注定要失败的。
    没有找到任何socket,我们继续处理这个侦听socket本身,它当前处于TCP_LISTEN状态,并且收到了来自网络对端的第一个SYN数据报,所以,我们为其调用mytcp_v4_conn_request,接受这个连接请求。mytcp_v4_conn_request首先检查这个请求数据报,如果是广播包或组播包,则直接丢弃,不进行处理。如果请求队列已满(即struct inet_connection_sock->isck_accept_queue->listen_sock中的request_sock队列),则不进行处理。如果接受队列已满(sock->sk_ack_backlog > sock->sk_max_ack_backlog)并且listen_sock->qlen_young大于1,则不进行处理。否则,在内存中创建一个struct request_sock。
    创建了struct request_sock后,首先分析请求数据报的tcp选项,将分析得到的值存入struct inet_request_sock。然后为rmt_addr, rmt_port, loc_addr赋值,然后保存ip选项
    mytcp_v4_conn_request函数对于接受tcp连接请求的处理,它在创建了一个struct request_sock之后,需要生成一个ISN(初始序号)。先来看一下struct tcp_request_sock结构体,它是对inet_request_sock的扩展:
    struct tcp_request_sock {
        struct inet_request_sock req;
        __u32            rcv_isn;
        __u32            snt_isn;
    };
    它增加了两个成员,snt_isn是本地socket用于跟接受的连接请求进行tcp通讯的初始序号。rcv_isn是来自对端的初始序号。
    有了ISN后,对请求socket发出的SYN发回一个ACK跟本地的SYN。然后把创建的struct request_sock加入到侦听socket的请求socket哈希表中(struct inet_connection_sock->isck_accept_queue->listen_opt->syn_table)>,但是需要注意的是,此时该request_sock还没有进入接受队列(即通过struct inet_connection_sock->isck_accept_queue->rskq_accept_head还不能找到它)。
    接下来,当远端的socket收到来自本地socket的ACK+SYN后,会发回来一个ACK。继续走tcp数据报的接收流程。此时我们还是只能在mytcp_hashinfo哈希表集中的listening_hash表中找到侦听socket对它进行处理。但此时,我们可以在侦听socket的syn_table表中找到这个request_sock。
    找出这个request_sock后,要对它进行检查,检查通过,从侦听socket上新克隆一个strcut tcp_sock,置其状态为TCP_SYN_RECV,放入mytcp_hashinfok中的ehash哈希表,然后把该新创建的tcp_sock也绑定在跟侦听端口相同的本地端口上。将request_sock从syn_table中取走,令request_sock->sk等于新创建的struct tcp_sock。把request_sock加入接受队列。
    最后,把socket的状态改为TCP_ESTABLISH,接受一个tcp连接请求的过程结束。
    accept系统调用把socket从接受队列中取走,取回request_sock->sk,并释放掉request_sock,然后就可以进行tcp通讯了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值