TCP之连接请求队列

1. 概述

TCP服务器端程序首先创建一个监听套接字,一旦有客户端连接该监听套接字,那么会创建一个新的通信套接字用来和客户端通信,而监听套接字继续等待其它客户端的连接请求。这期间就是三次握手过程,这个过程可能会失败,所以为了管理这期间的套接字,TCP协议特意定义了一组数据结构。这篇笔记的目的就是把这几个核心数据结构之间的关系理清楚,先来一张整体结构图,对着结构图看下面的数据结构定义会更容易理解。
在这里插入图片描述

2. inet_connection_sock

每个面向连接的套接字都是一个struct inet_connection_sock,该结构中有一个成员isck_accept_queue,该成员就是所谓的连接管理队列,用来管理所有正在进行三次握手、或者已经完成三次握手等待accept()的套接字。

注意:客户端套接字并不使用该队列,因为没有使用的必要。

/** inet_connection_sock - INET connection oriented sock
 *
 * @icsk_accept_queue:	   FIFO of established children 
 */

struct inet_connection_sock {
	...
	struct request_sock_queue icsk_accept_queue;
}

2.1 连接建立队列

struct request_sock_queue {
	//head和tail用于维护已经完成三次握手,等待用户程序accept的套接字,
	//后续称该队列为“accept连接队列”,其中的套接字为“已连接套接字”
	struct request_sock	*rskq_accept_head;
	struct request_sock	*rskq_accept_tail;
    
	//用于同步对listen_opt的操作
	rwlock_t		syn_wait_lock;
	//与TCP选选TCP_DEFER_ACCEPT有关,暂时忽略
	u8			rskq_defer_accept;
	/* 3 bytes hole, try to pack */
    
	//已经收到SYN,但是尚未完成三次握手的套接字保存在该结构中,其占用内存在listen()
	//系统调用期间分配,后续称该队列为“SYN请求队列”,其中的套接字为“半连接套接字”
	struct listen_sock	*listen_opt;
};

2.2 SYN请求队列

struct listen_sock {
	//其取值为nr_table_entries以2为底的对数
	u8			max_qlen_log;
	/* 3 bytes hole, try to use */
	//当前syn_table哈希表中套接字的数目,即有多少个半连接套接字
	int			qlen;
	//服务器端会超时重传SYN+ACK段,该变量记录了那些还尚未重传过SYN+ACK段的套接字个数
	int			qlen_young;
    
	int			clock_hand;
	//该随机数用于访问listen_opt哈希表时计算哈希值
	u32			hash_rnd;
	
	//syn_table哈希表的桶大小,该值和listen()系统调用的backlog参数有关
	u32			nr_table_entries;
	//半连接套接字哈希表,管理的元素就是连接请求块,见下方
	struct request_sock	*syn_table[0];
};

3. 连接请求块

类似于一般的套接字,有struct tcp_sock这样的TCB对应,已连接套接字和半连接套接字也同样需要一个类似的TCB,不过它们对应的不是struct tcp_sock,而是这里要介绍的struct tcp_request_sock。该结构同样是分层次的,依次是struct tcp_request_sock、struct inet_request_sock、struct request_sock下面是该结构的定义。

3.1 struct tcp_request_sock

struct tcp_request_sock {
	struct inet_request_sock req;

#ifdef CONFIG_TCP_MD5SIG
	/* Only used by TCP MD5 Signature so far. */
	const struct tcp_request_sock_ops *af_specific;
#endif
	//客户端SYN段中携带的seq,即客户端的初始序列号 */
	u32 rcv_isn; 
	//SYN+ACK段携带的seq,即服务器端的初始序列号
	u32 snt_isn;
	//SYN+ACK段发送的时间戳,基于jiffies
	u32 snt_synack;
};

struct inet_request_sock {
    struct request_sock req;

#if IS_ENABLED(CONFIG_IPV6)
    u16 inet6_rsk_offset;
#endif

    __be16 loc_port; /* 服务器端端口号 */
    __be32 loc_addr; /* 服务器端IP地址 */
    __be32 rmt_addr; /* 客户端IP地址 */
    __be16 rmt_port; /* 客户端端口号 */

    kmemcheck_bitfield_begin(flags);
    u16 snd_wscale : 4, /* 客户端的窗口扩大因子 */
        rcv_wscale : 4, /* 服务器端的窗口扩大因子 */
        tstamp_ok : 1, /* 标识本连接是否支持TIMESTAMP选项 */
        sack_ok : 1, /* 标识本连接是否支持SACK选项 */
        wscale_ok : 1, /* 标识本连接是否支持Window Scale选项 */
        ecn_ok : 1, /* 标识本连接是否支持ECN选项 */
        acked : 1,
        no_srccheck : 1;
    kmemcheck_bitfield_end(flags);

    struct ip_options_rcu *opt; /* IP选项 */
};

/* struct request_sock - mini sock to represent a connection request
 */
struct request_sock {
	//和其它struct request_sock对象形成链表
	struct request_sock		*dl_next; /* Must be first member! */
	//SYN段中客户端通告的MSS
	u16				mss;
	//SYN+ACK段已经重传的次数,初始化为0
	u8				retrans;
	u8				__pad;
	u32				window_clamp; /* window clamp at creation time */
	u32				rcv_wnd;	  /* rcv_wnd offered first time */
	u32				ts_recent;
	//SYN+ACK段的超时时间
	unsigned long			expires;
	//指向tcp_request_sock_ops,该函数集用于处理第三次握手的
	//ACK段以及后续accept过程中struct tcp_sock对象的创建
	const struct request_sock_ops	*rsk_ops;
	//连接建立前无效,建立后指向创建的tcp_sock结构
	struct sock			*sk;
	u32				secid;
	u32				peer_secid;
};

3.1.1 操作函数struct tcp_request_sock_ops

struct request_sock_ops {
    int family; /* 所属的协议族 */
    int obj_size; /* 连接请求块的大小 */
    struct kmem_cache *slab; /* 连接请求块的高速缓存 */
    char *slab_name;

    //SYN+ACK段重传时调用该函数
    int (*rtx_syn_ack) (struct sock *sk, struct request_sock *req, struct request_values *rvp);

    //发送ACK段时调用该函数
    void (*send_ack) (struct sock *sk, struct sk_buff *skb, struct request_sock *req);

    //发送RST段时调用该函数
    void (*send_reset) (struct sock *sk, struct sk_buff *skb);

    //析构函数
    void (*destructor) (struct request_sock *req);

    //SYN+ACK段超时处理函数
    void (*syn_ack_timeout) (struct sock *sk, struct request_sock *req);
};

//TCP的实际定义
struct request_sock_ops tcp_request_sock_ops__read_mostly = {
    .family = PF_INET,
    .obj_size = sizeof(struct tcp_request_sock),

    .rtx_syn_ack = tcp_v4_rtx_synack,
    .send_ack = tcp_v4_reqsk_send_ack,
    .destructor = tcp_v4_reqsk_destructor,
    .send_reset = tcp_v4_send_reset,
    .syn_ack_timeout = tcp_syn_ack_timeout,
};
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目 录 译者序 前言 第1章 概述 1 1.1 引言 1 1.2 分层 1 1.3 TCP/IP的分层 4 1.4 互联网的地址 5 1.5 域名系统 6 1.6 封装 6 1.7 分用 8 1.8 客户-服务器模型 8 1.9 端口号 9 1.10 标准化过程 10 1.11 RFC 10 1.12 标准的简单服务 11 1.13 互联网 12 1.14 实现 12 1.15 应用编程接口 12 1.16 测试网络 13 1.17 小结 13 第2章 链路层 15 2.1 引言 15 2.2 以太网和IEEE 802封装 15 2.3 尾部封装 17 2.4 SLIP:串行线路IP 17 2.5 压缩的SLIP 18 2.6 PPP:点对点协议 18 2.7 环回接口 20 2.8 最大传输单元MTU 21 2.9 路径MTU 21 2.10 串行线路吞吐量计算 21 2.11 小结 22 第3章 IP:网际协议 24 3.1 引言 24 3.2 IP首部 24 3.3 IP路由选择 27 3.4 子网寻址 30 3.5 子网掩码 32 3.6 特殊情况的IP地址 33 3.7 一个子网的例子 33 3.8 ifconfig命令 35 3.9 netstat命令 36 3.10 IP的未来 36 3.11 小结 37 第4章 ARP:地址解析协议 38 4.1 引言 38 4.2 一个例子 38 4.3 ARP高速缓存 40 4.4 ARP的分组格式 40 4.5 ARP举例 41 4.5.1 一般的例子 41 4.5.2 对不存在主机的ARP请求 42 4.5.3 ARP高速缓存超时设置 43 4.6 ARP代理 43 4.7 免费ARP 45 4.8 arp命令 45 4.9 小结 46 第5章 RARP:逆地址解析协议 47 5.1 引言 47 5.2 RARP的分组格式 47 5.3 RARP举例 47 5.4 RARP服务器的设计 48 5.4.1 作为用户进程的RARP服务器 49 5.4.2 每个网络有多个RARP服务器 49 5.5 小结 49 第6章 ICMP:Internet控制报文协议 50 6.1 引言 50 6.2 ICMP报文的类型 50 6.3 ICMP地址掩码请求与应答 52 6.4 ICMP时间戳请求与应答 53 6.4.1 举例 54 6.4.2 另一种方法 55 6.5 ICMP端口不可达差错 56 6.6 ICMP报文的4.4BSD处理 59 6.7 小结 60 第7章 Ping程序 61 7.1 引言 61 7.2 Ping程序 61 7.2.1 LAN输出 62 7.2.2 WAN输出 63 7.2.3 线路SLIP链接 64 7.2.4 拨号SLIP链路 65 7.3 IP记录路由选项 65 7.3.1 通常的例子 66 7.3.2 异常的输出 68 7.4 IP时间戳选项 69 7.5 小结 70 第8章 Traceroute程序 71 8.1 引言 71 8.2 Traceroute 程序的操作 71 8.3 局域网输出 72 8.4 广域网输出 75 8.5 IP源站选路选项 76 8.5.1 宽松的源站选路的traceroute 程序示例 78 8.5.2 严格的源站选路的traceroute 程序示例 79 8.5.3 宽松的源站选路traceroute程序 的往返路由 80 8.6 小结 81 第9章 IP选路 83 9.1 引言 83 9.2 选路的原理 84 9.2.1 简单路由表 84 9.2.2 初始化路由表 86 9.2.3 较复杂的路由表 87 9.2.4 没有到达目的地的路由 87 9.3 ICMP主机与网络不可达差错 88 9.4 转发或不转发 89 9.5 ICMP重定向差错 89 9.5.1 一个例子 90 9.5.2 更多的细节 91 9.6 ICMP路由器发现报文 92 9.6.1 路由器操作 93 9.6.2 主机操作 93 9.6.3 实现 93 9.7 小结 94 第10章 动态选路协议 95 10.1 引言 95 10.2

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值