前言
在上篇《post真的安全么》的最后有提到一個問題,其實這是個既簡單又復雜的問題。
機器連接數
記得以前一台機器只能建立65535個連接的這種想法一直長時間占據着思維方式,為什么會有這種想法呢,估計最早起源於學校的port的short(16位65535)吧。
一台機器connect同一IP,port的最大連接數
嗯,既然一台機器只能最大建立65535個連接,那當然” 為什么一台機器connect同一個IP,port的tcp連接數不能超過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;
}
其中:sock是socket的一個內部結構,從__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的最大連接數
那為什么同一台機器連接同一ip,port的最大連接數是63335呢,是因為在4元組(src_ip,src_port,dst_ip,dst_port)中,src_ip, dst_ip, dst_port是都是固定的,只有src_port可以變化,所以根據short,所以才是65535.