tcp connection setup的实现(一)
2009-09-10 00:00:00 来源:WEB开发网这里我先来介绍下inet_csk_get_port的流程.
当绑定的port为0时,这时也就是说需要kernel来分配一个新的port.
1 首先得到系统的port范围.
2 随机分配一个port.
3 从bhash中得到当前随机分配的端口的链表(也就是inet_bind_bucket链表).
4 遍历这个链表(链表为空的话,也说明这个port没有被使用),如果这个端口已经被使用,则将端口号加一,继续循环,直到找到当前没有被使用的port,也就是没有在bhash中存在的port.
5 新建一个inet_bind_bucket,并插入到bhash中.
当指定port时.
1 从bhash中根据hash值(port计算的)取得当前指定端口对应的inet_bind_bucket结构.
2 如果bhash中存在,则说明,这个端口已经在使用,因此需要判断这个端口是否允许被reuse.
3 如果不存在,则步骤和上面的第5部一样.
Java代码
int inet_csk_get_port(struct sock *sk, unsigned short snum)
{
struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo;
struct inet_bind_hashbucket *head;
struct hlist_node *node;
struct inet_bind_bucket *tb;
int ret;
struct net *net = sock_net(sk);
local_bh_disable();
if (!snum) {
///端口为0,也就是需要内核来分配端口.
int remaining, rover, low, high;
///得到端口范围.
inet_get_local_port_range(&low, &high);
remaining = (high - low) + 1;
rover = net_random() % remaining + low;
///循环来得到一个当前没有使用的端口.
do {
///通过端口为key,来得到相应的inet_bind_bucket
head = &hashinfo->bhash[inet_bhashfn(net, rover,
hashinfo->bhash_size)];
spin_lock(&head->lock);
inet_bind_bucket_for_each(tb, node, &head->chain)
if (tb->ib_net == net && tb->port == rover)
///说明这个端口已被使用,因此需要将端口加1,重新查找.
goto next;
break;
next:
spin_unlock(&head->lock);
///如果端口大于最大值,则将它赋值为最小值(这是因为我们这个端口是随机值,因此有可能很多端口就被跳过了),重新查找.
if (++rover > high)
rover = low;
} while (--remaining > 0);
/* Exhausted local port range during search? It is not
* possible for us to be holding one of the bind hash
* locks if this test triggers, because if 'remaining'
* drops to zero, we broke out of the do/while loop at
* the top level, not from the 'break;' statement.
*/
ret = 1;
if (remaining <= 0)
goto fail;
///将要分配的端口号.
snum = rover;
} else {
///指定端口号的情况.和上面的方法差不多,只不过只需要一次.
head = &hashinfo->bhash[inet_bhashfn(net, snum,
hashinfo->bhash_size)];
spin_lock(&head->lock);
inet_bind_bucket_for_each(tb, node, &head->chain)
if (tb->ib_net == net && tb->port == snum)
goto tb_found;
}
tb = NULL;
goto tb_not_found;
tb_found:
///用来处理端口号已经被使用的情况.他被使用的socket不为空的情况.
if (!hlist_empty(&tb->owners)) {
///fastreuse大于0说明其他的socket允许另外的socket也使用这个端口,而reuse表示当前的端口也允许和其他的端口分享这个port.并且socket的状态必须是TCP_LISTEN,才能做这个判断.
if (tb->fastreuse > 0 &&
sk->sk_reuse && sk->sk_state != TCP_LISTEN) {
goto success;
} else {
ret = 1;
///如果出错,调用inet_csk_bind_conflict.主要是有可能一些使用这个端口的socket,有可能使用不同的ip地址.此时,我们是可以使用这个端口的.
if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb))
goto fail_unlock;
}
}
tb_not_found:
ret = 1;
///重新分配一个inet_bind_bucket,并链接到bhash.
if (!tb && (tb = inet_bind_bucket_create(hashinfo->bind_bucket_cachep,
net, head, snum)) == NULL)
goto fail_unlock;
if (hlist_empty(&tb->owners)) {
///设置当前端口的fastreuse,这个域也只能是处于listen的socket才能设置.
if (sk->sk_reuse && sk->sk_state != TCP_LISTEN)
tb->fastreuse = 1;
else
tb->fastreuse = 0;
} else if (tb->fastreuse &&
(!sk->sk_reuse || sk->sk_state == TCP_LISTEN))
tb->fastreuse = 0;
success:
///将这个socket加到这个端口的ower中.
if (!inet_csk(sk)->icsk_bind_hash)
inet_bind_hash(sk, tb, snum);
WARN_ON(inet_csk(sk)->icsk_bind_hash != tb);
ret = 0;
fail_unlock:
spin_unlock(&head->lock);
fail:
local_bh_enable();
return ret;
}
Tags:tcp connection setup
编辑录入:爽爽 [复制链接] [打 印]更多精彩
赞助商链接