tcp ip 三次握手时数据结构-

 

struct tcp_request_sock {
    struct inet_request_sock     req;
    const struct tcp_request_sock_ops *af_specific;
    struct skb_mstamp        snt_synack; /* first SYNACK sent time */
    bool                tfo_listener;
    u32                txhash;
    u32                rcv_isn;
    u32                snt_isn;
    u32                last_oow_ack_time; /* last SYNACK */
    u32                rcv_nxt; /* the ack # by SYNACK. For
                          * FastOpen it's the seq#
                          * after data-in-SYN.
                          */
};



struct inet_request_sock {
    struct request_sock    req;
#define ir_loc_addr        req.__req_common.skc_rcv_saddr
#define ir_rmt_addr        req.__req_common.skc_daddr
#define ir_num            req.__req_common.skc_num
#define ir_rmt_port        req.__req_common.skc_dport
#define ir_v6_rmt_addr        req.__req_common.skc_v6_daddr
#define ir_v6_loc_addr        req.__req_common.skc_v6_rcv_saddr
#define ir_iif            req.__req_common.skc_bound_dev_if
#define ir_cookie        req.__req_common.skc_cookie
#define ireq_net        req.__req_common.skc_net
#define ireq_state        req.__req_common.skc_state
#define ireq_family        req.__req_common.skc_family

    kmemcheck_bitfield_begin(flags);
    u16            snd_wscale : 4,
                rcv_wscale : 4,
                tstamp_ok  : 1,
                sack_ok       : 1,
                wscale_ok  : 1,
                ecn_ok       : 1,
                acked       : 1,
                no_srccheck: 1;
    kmemcheck_bitfield_end(flags);
    u32                     ir_mark;
    union {
        struct ip_options_rcu __rcu    *ireq_opt;
        struct sk_buff        *pktopts;
    };

};


/* struct request_sock - mini sock to represent a connection request
 */
struct request_sock {
    struct sock_common        __req_common;
#define rsk_refcnt            __req_common.skc_refcnt
#define rsk_hash            __req_common.skc_hash
#define rsk_listener            __req_common.skc_listener
#define rsk_window_clamp        __req_common.skc_window_clamp
#define rsk_rcv_wnd            __req_common.skc_rcv_wnd

    struct request_sock        *dl_next;
    u16                mss;
    u8                num_retrans; /* number of retransmits */
    u8                cookie_ts:1; /* syncookie: encode tcpopts in timestamp */
    u8                num_timeout:7; /* number of timeouts */
    u32                ts_recent;
    struct timer_list        rsk_timer;
    const struct request_sock_ops    *rsk_ops;
    struct sock            *sk;
    u32                *saved_syn;
    u32                secid;
    u32                peer_secid;
};

 

客户端主动发起连接

  •  调用 socket(2) 创建 TCP socket,tcp_sock 处于 CLOSE 状态

 

  • 调用 connect(2) 发起连接,tcp_sock 进入 SYN_SENT 状态。
    1. 选择 ephemeral port
    2. 创建 inet_bind_bucket 并加入 bhash
    3. 将 tcp_sock 加入 ehash
    4. 发送 SYN segment

 

 

 

  • 收到服务器发来的 SYN+ACK segment。
    1. 从 ehash 中找到 tcp_sock
    2. 完成握手,进入 ESTABLISHED 状态。数据结构不变,图略。
    3. 发送 ACK segment

 

服务器被动接受连接

    1. 调用 socket(2) 创建 TCP socket,tcp_sock 处于 CLOSE 状态。数据结构如图二。
    2. 调用 bind(2) 将 tcp_sock 加入 bhash。
    3. 调用 listen(2) 将 tcp_sock 设为 LISTEN 状态,并加入 listening_hash。
      这时数据结构的状态如下图:

 

 

 

    1. 调用 socket(2) 创建 TCP socket,tcp_sock 处于 CLOSE 状态。数据结构如图二。
    2. 调用 bind(2) 将 tcp_sock 加入 bhash。
    3. 调用 listen(2) 将 tcp_sock 设为 LISTEN 状态,并加入 listening_hash。
      这时数据结构的状态如下图:
      原图:
    4. 收到 SYN segment,从 listening_hash 找到 listen tcp_sock,创建 tcp_request_sock(NEW_SYN_RECV 状态),并将其加入 ehash,然后发送 SYN+AC
    5.  收到 ACK segment,从 ehash 中找到 tcp_request_sock,顺藤摸瓜找到 listen tcp_sock,创建新的 tcp_sock(SYN_RECV 状态),将其链入 listen tcp_sock 所属的 inet_bind_bucket 中,然后用它替换 tcp_request_sock 在 ehash 中的位置。接下来把 tcp_request_sock 加入 listen socket 的 accept queue 中,最后经过一系列处理,把新 tcp_sock 设为 ESTABLISHED 状态,可以接收数据了。

    6. 用户程序调用 accept(2),从 listen socket 的 accept queue 中取出新 tcp_sock,销毁 tcp_request_sock,然后创建 file/dentry/socket_alloc 等对象将 tcp_sock 加入进程的文件描述符表中,返回相应的文件描述符。

与以前的版本相比,第 4 步改动最大,原来是把 tcp_request_sock 挂在 listen socket 下,收到 ACK 之后从 listening_hash 找到 listen socket 再进一步找到 tcp_request_sock;新的做法是直接把 tcp_request_sock 挂在 ehash 中,这样收到 ACK 之后可以直接找到 tcp_request_sock,减少了锁的争用(contention)。具体的 commit 是:

 

 

posted @ 2021-06-14 23:42  codestacklinuxer  阅读(117)  评论(0编辑  收藏  举报