TCP/IP工作流5 connect 3

来源:互联网 发布:微信小店数据推送 编辑:程序博客网 时间:2024/06/05 07:25

继续TCP connect

如下,来到252行。

252 err = inet_hash_connect(&tcp_death_row, sk);253     if (err)254         goto failure;256     err = ip_route_newports(&rt, IPPROTO_TCP,257                 inet->sport, inet->dport, sk);258     if (err)259         goto failure;262     sk->sk_gso_type = SKB_GSO_TCPV4;263     sk_setup_caps(sk, &rt->u.dst);265     if (!tp->write_seq)266         tp->write_seq = secure_tcp_sequence_number(inet->saddr,267                                inet->daddr,268                                inet->sport,269                                usin->sin_port);271     inet->id = tp->write_seq ^ jiffies;273     err = tcp_connect(sk);274     rt = NULL;275     if (err)276         goto failure;278     return 0;280 failure:285     tcp_set_state(sk, TCP_CLOSE);286     ip_rt_put(rt);287     sk->sk_route_caps = 0;288     inet->dport = 0;289     return err;290 }

252:一个函数调用,传入的参数tcp_death_row,变更类型为struct inet_timewait_death_row。定义在net/ipv4/tcp_minisocks.c中。它的作用现在猜测可能是用来记录出现连接失败时,把此TCP socket加入到记录中。另外一个变量sk则是TCP连接的主要数据实例。
函数定义在net/ipv4/inet_hashtables.c中。
这个函数也很长,分两部分来说下。

273 int inet_hash_connect(struct inet_timewait_death_row *death_row,274               struct sock *sk)275 {276     struct inet_hashinfo *hinfo = death_row->hashinfo;277     const unsigned short snum = inet_sk(sk)->num;278     struct inet_bind_hashbucket *head;279     struct inet_bind_bucket *tb;280     int ret;282     if (!snum) {283         int i, remaining, low, high, port;284         static u32 hint;285         u32 offset = hint + inet_sk_port_offset(sk);286         struct hlist_node *node;287         struct inet_timewait_sock *tw = NULL;289         inet_get_local_port_range(&low, &high);290         remaining = (high - low) + 1;292         local_bh_disable();293         for (i = 1; i <= remaining; i++) {294             port = low + (i + offset) % remaining;295             head = &hinfo->bhash[inet_bhashfn(port, hinfo->bhash_size)];296             spin_lock(&head->lock);302             inet_bind_bucket_for_each(tb, node, &head->chain) {303                 if (tb->port == port) {304                     BUG_TRAP(!hlist_empty(&tb->owners));305                     if (tb->fastreuse >= 0)306                         goto next_port;307                     if (!__inet_check_established(death_row,308                                       sk, port,309                                       &tw))310                         goto ok;311                     goto next_port;312                 }313             }315             tb = inet_bind_bucket_create(hinfo->bind_bucket_cachep, head, port);316             if (!tb) {317                 spin_unlock(&head->lock);318                 break;319             }320             tb->fastreuse = -1;321             goto ok;323         next_port:324             spin_unlock(&head->lock);325         }326         local_bh_enable();328         return -EADDRNOTAVAIL;

276-280:变量声明。hinfo是struct inet_hashinfo类型,用来记录已经散列的socket和正在监听的socket。
snum:在初始化socket时写入的源端口。当我们发起一个连接时,可以指定一个没有被占用的端口。
head:与hinfo中的成员bhash同样类型,指向一个散列表的表头。
tb:一个散列表项。是head->chain所指向的散列表的元素,从名称可以看出来。bucket就是桶的意思。
ret: return, 与retval一样,是linux系统里的返回值。虽然linux源码格式高度统一,但还是会出现一些不一样的地方。比如这里,跟之前用retval的代码段,应该不是一个人维护的。
282-328:在一个if里面。记得在socket创建时没有指定本地要使用的端口,所以这里传入的端口是0。这个0是在什么时候设置的呢?通过分析socket创建时的代码可以知道,只有当socket类型为RAW_SOCK时,这个端口号才不为0。而我们正在创建的是TCP类型的socket所以是0。代码还是要看下
这段代码是根据本地配置的许可端口范围里,一个个试,直到找到一个可以用的端口。假设每一个端口都有一大堆socket绑定着,如果符合了端口重用的规则的话,说明这个端口我们也可以用。说明找到了就可以跳出循环了。goto ok中的ok标签在下面。这里我们也看到了,可以用goto跳出循环体。

0 0
原创粉丝点击