mytcp_v4_connect是IPv4相关的一个tcp协议连接函数,在myinet_stream_connect中,检查到struct socket->state状态为SS_UNCONNECTED后,调用该函数。该函数首先调用myip_route_connect确定到连接对端的路由,根据路由查询的结果,填充inet_sock->saddr, inet_sock->rcv_saddr, inet_sock->daddr的值。 接下来是一些tcp socket相关的参数的设置,我们暂时先略过。在mytcp_v4_connect中把struct sock->sk_state的状态改为TCP_SYN_SENT,这是建立TCP连接的三次握手协议的第一步的状态。 接下来调用函数myinet_hash_connect,为tcp socket绑定一个本地端口。mytcp_hashinfo是一个全局变量,它管理很多tcp协议中需要用到的哈希表,其类型是一个结构体struct inet_hashinfo,定义如下: struct inet_hashinfo { struct inet_ehash_bucket *ehash; struct inet_bind_hashbucket *bhash; int bhash_size; unsigned int ehash_size; struct hlist_head listening_hash[INET_LHTABLE_SIZE]; rwlock_t lhash_lock ____cacheline_aligned; atomic_t lhash_users; wait_queue_head_t lhash_wait; kmem_cache_t *bind_bucket_cachep; }; bind_bucket_cachep是一块后备高速缓存,其单位内存大小为一个结构体struct inet_bind_bucket的大小,插入哈希表bhash中的结构体struct inet_bind_bucket都是从这块后备高速缓存中进行分配。该结构体是一个关于本地端口信息的结构体,其完整定义如下: struct inet_bind_bucket { unsigned short port; //端口 signed short fastreuse; //重用。 struct hlist_node node; //作为bhash链表的节点。 struct hlist_head owners; //绑定在该端口上的socket的链表。 }; bhash_size是关于哈希表bhash的大小。 myinet_hash_connect首先在范围32768-61000之间为该tcp socket选择一个可以使用的本地端口,然后为该端口建立一个结构体struct inet_bind_bucket,插入到mytcp_hashinfo.bhash中。 接下来,inet_sock->snum = port。套接口struct sock有一个成员sk_bind_node,用于把struct sock放入到一个链表中,在这里,我们把该tcp socket放入到struct inet_bind_bucket->owners的链表中,表示该socket绑定在了该端口上。同时,struct inet_connection_sock是结构体struct inet_sock的一个扩展,它有一个成员icsk_bind_hash,用于指向刚刚创建的struct inet_bind_bucket。这三步操作把一个tcp本地套接口完全绑定到了一个端口上。 接下来,我们还要把struct sock本身放到一个哈希队列中,这跟udp, raw的哈希表功能相似,是为了接收数据时,能够找到这个socket。根据tcp socket本身的工作状态,如果它处于TCP_LISTEN,则把这个socket放入到哈希队列listening_hash中,否则,放入到哈希队列ehash中,ehash实际上是被一分为二的,前半部分放非TIME_WAIT状态的socket,后半部分放TIME_WAIT状态的socket。struct sock的成员sk_node用于把socket放入到队列中。 struct inet_hashinfo的成员lhash_wait中存放的应该是等待连接接收的来自网络中其它主机的连接请求,如果当前有一个tcp socket进入listening_hash哈希队列,则唤醒该等待队列。 如果在myinet_hash_connect函数中,我们发现要绑定的本地端口已经在bhash哈希队列中存在,我们可以选择跳过,取下一个端口继续尝试,也可以重用该端口,这里有一些规则需要遵守。首先,如果结构体的成员fastreuse>=0,则不可重用,直接取下一个。否则,调用__myinet_check_established进行验证,验证的基本原理是绑定在不同网络设备接口上的socket可以共用端口;如果每个socket的sk_reuse被置位,并且都不处于TCP_LISTEN状态,则可以共用端口;如果所有的socket都被绑定到同一个rcv_saddr本地地址,但它们都是不相同的,则可以共用端口。
TCP socket上的connect
最新推荐文章于 2024-09-15 19:39:42 发布