tcp的65535个连接之迷

前言

上篇《post真的安全么》的最后有提到一个问题,其实这是个既简单又复杂的问题。

机器连接数

记得以前一台机器只能建立65535个连接的这种想法一直长时间占据着思维方式,为什么会有这种想法呢,估计最早起源于学校的portshort1665535)吧。

一台机器connect同一IP,port的最大连接数

嗯,既然一台机器只能最大建立65535个连接,那当然 为什么一台机器connect同一个IPporttcp连接数不能超过65535这个问题的答案是对的,没有为什么。

真的是这样的么。 

TCP连接的唯一性

前面提到的所有的问题,其实都可以归结为一个问题,就是TCP连接的唯一性,是靠系统内部的什么来保证的。其实以前我已经说过,TCP连接在内部是由一个四元组来形成,即(src_ip,src_port,dst_ip,dst_port),当然其实是这4个值的简单的hash值来决定的。侯捷说,源码之前,了无私密。所以我们简单的如下看下去(采用linux内核2.6.16)

如下所示:

inet_hashtables.h

static inline struct sock *inet_lookup(struct inet_hashinfo *hashinfo,

                                       const u32 saddr, const u16 sport,

                                       const u32 daddr, const u16 dport,

                                       const int dif)

{

        struct sock *sk;

 

        local_bh_disable();

        sk = __inet_lookup(hashinfo, saddr, sport, daddr, ntohs(dport), dif);

        local_bh_enable();

 

        return sk;

}

static inline struct sock *__inet_lookup(struct inet_hashinfo *hashinfo,

                                         const u32 saddr, const u16 sport,

                                         const u32 daddr, const u16 hnum,

                                         const int dif)

{

        struct sock *sk = __inet_lookup_established(hashinfo, saddr, sport, daddr,

                                                    hnum, dif);

        return sk ? : inet_lookup_listener(hashinfo, daddr, hnum, dif);

}

static inline struct sock *

        __inet_lookup_established(struct inet_hashinfo *hashinfo,

                                  const u32 saddr, const u16 sport,

                                  const u32 daddr, const u16 hnum,

                                  const int dif)

{

        INET_ADDR_COOKIE(acookie, saddr, daddr)

        const __u32 ports = INET_COMBINED_PORTS(sport, hnum);

        struct sock *sk;

        const struct hlist_node *node;

        /* Optimize here for direct hit, only listening connections can

         * have wildcards anyways.

         */

        unsigned int hash = inet_ehashfn(daddr, hnum, saddr, sport);

        struct inet_ehash_bucket *head = inet_ehash_bucket(hashinfo, hash);

 

        prefetch(head->chain.first);

        read_lock(&head->lock);

        sk_for_each(sk, node, &head->chain) {

                if (INET_MATCH(sk, hash, acookie, saddr, daddr, ports, dif))

                        goto hit; /* You sunk my battleship! */

        }

 

        /* Must check for a TIME_WAIT'er before going to listener hash. */

        sk_for_each(sk, node, &(head + hashinfo->ehash_size)->chain) {

                if (INET_TW_MATCH(sk, hash, acookie, saddr, daddr, ports, dif))

                        goto hit;

        }

        sk = NULL;

out:

        read_unlock(&head->lock);

        return sk;

hit:

        sock_hold(sk);

        goto out;

}

 

其中:socksocket的一个内部结构,从__inet_lookup_established的名字可以看出,这个是查找建立的socket连接的函数,好,我们继续看下去:

inet_sock.h

static inline unsigned int inet_ehashfn(const __u32 laddr, const __u16 lport,

                                        const __u32 faddr, const __u16 fport)

{

        unsigned int h = (laddr ^ lport) ^ (faddr ^ fport);

        h ^= h >> 16;

        h ^= h >> 8;

        return h;

}

 

static inline int inet_sk_ehashfn(const struct sock *sk)

{

        const struct inet_sock *inet = inet_sk(sk);

        const __u32 laddr = inet->rcv_saddr;

        const __u16 lport = inet->num;

        const __u32 faddr = inet->daddr;

        const __u16 fport = inet->dport;

 

        return inet_ehashfn(laddr, lport, faddr, fport);

}

从上面的代码里面明显可以看出,inet_ehashfn函数就是计算hash值的方法,也证明了socket其实是4元组组成的论据。

有了上面的源码,现在回到问题

问题一:机器的连接数

所以一台机器的TCP连接数是不只65535的,为什么,因为4元组的组合至少是无限的,机器的连接数,取决于机器的内存,机器的CPU等等这些因素。

问题二:一台机器connect同一IP,port的最大连接数

那为什么同一台机器连接同一ipport的最大连接数是63335呢,是因为在4元组(src_ip,src_port,dst_ip,dst_port)中,src_ip, dst_ip, dst_port是都是固定的,只有src_port可以变化,所以根据short,所以才是65535.

posted @ 2012-10-29 21:52 connoryan 阅读(...) 评论(...) 编辑 收藏