端口选择
继续看inet_csk_get_port函数:
在端口选择前,先确定当前该socket的“属性”,即是否可以端口复用,是否在TCP_LISTEN状态,以便后面插入到桶队列时设置fastreuse字段。
bool reuse = sk- >sk_reuse && sk- >sk_state != TCP_LISTEN;
进行端口选择和绑定:
端口绑定分为两种,一种指定端口,一种随机选择。如果给 bind 传 递的地址参数中,port 字段为 0,那么就会自动选择参数。
如代码所示,当端口port没有指定时,调用inet_csk_find_open_port(sk, &tb, &port):
if (!port) {
head = inet_csk_find_open_port(sk, &tb, &port);
if (!head)
return ret;
if (!tb)
goto tb_not_found;
goto success;
}
主要逻辑在net_csk_find_open_port实现,重点看端口指定,暂时不看端口不指定(其实逻辑差不多)。那么当用户指定了端口,也就是port有值时:
head = &hinfo- >bhash[inet_bhashfn(net, port,hinfo- >bhash_size)];
inet_bind_bucket_for_each(tb, &head- >chain)
if (net_eq(ib_net(tb), net) && tb- >port == port)
goto tb_found;
tb_not_found:
tb = inet_bind_bucket_create(hinfo- >bind_bucket_cachep,
net, head, port);
if (!tb)
goto fail_unlock;
tb_found:
if (!hlist_empty(&tb- >owners)) {
if (sk- >sk_reuse == SK_FORCE_REUSE)
goto success;
if ((tb- >fastreuse > 0 && reuse) ||
sk_reuseport_match(tb, sk))
goto success;
if (inet_csk_bind_conflict(sk, tb, true, true))
goto fail_unlock;
}
head = &hinfo- >bhash[inet_bhashfn(net, port,hinfo- >bhash_size)];
inet_bind_bucket_for_each(tb, &head- >chain)
if (net_eq(ib_net(tb), net) && tb- >port == port)
goto tb_found;
tb_not_found:
tb = inet_bind_bucket_create(hinfo- >bind_bucket_cachep,
net, head, port);
if (!tb)
goto fail_unlock;
端口复用的解释
还是要从文章开头的图说起,bind时端口号都会经过哈希计算分配在【相应的哈希桶结构inet_bind_hashbucket】上的chain链表节点的【桶结构inet_bind_bucket上】,inet_bind_bucket 结构就是用来描述端口和 sock 之间的绑定关系的。它的 port 字段表示一个绑定的端口,而 owners 则表示绑定到这个端口之上的所有 sock,因为端口可以重用,所以同一端口可能有多个 sock 绑定。
bind端口复用的实际用途基本上也就是:
防止服务器重启时之前绑定的端口还未释放或者程序突然退出而系统没有释放端口。这种情况下如果设定了端口复用,则新启动的服务器进程可以直接绑定端口。如果没有设定端口复用,绑定会失败,提示ADDR已经在使用中!
全部0条评论
快来发表一下你的评论吧 !