“因为 TCP 端口号是 16 位无符号整数, 最大 65535, 所以一台服务器最多支持 65536 个TCP socket连接.” - 一个非常经典的误解! 即使是有多年网络编程经验的人, 也会持有这个错误结论.
要戳破这个错误结论, 可以从理论和实践两方面来.
理论:
系统通过一个四元组来唯一标识一条 TCP 连接. 这个四元组的结构是(local_ip, local_port, remote_ip, remote_port), 对于 IPv4, 系统理论上最多可以管理 2^(32+16+32+16), 2 的 96 次方个连接.
对于一个 tcp client而言,本地 ip 是确定的,server 的 ip 和 port 也是确定的,那么客户端能够维持的 TCP 连接数量是 2^16(65536)个。如果我们在 server 上再多监听一个端口,那么理论上 client 到 server 之间就能够维持 2^16 * 2 个 TCP 连接。
实践:
TCP 客户端(TCP 的主动发起者)可以在同一 ip:port 上向不同的服务器发起主动连接, 只需在 Bind 之前对 socket 设置 SO_REUSEADDR 选项即可。
服务器端代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 func main () { go startServer(8000 ) startServer(8001 ) } func startServer (port int ) { var fd int var err error var ServerAddr syscall.SockaddrInet4 if fd, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_IP); err != nil { panic (err) } ServerAddr.Port = port ServerAddr.Addr = [4 ]byte {0 , 0 , 0 , 0 } if err = syscall.Bind(fd, &ServerAddr); err != nil { panic (err) } if err = syscall.Listen(fd, 512 ); err != nil { panic (err) } for { nfd, sa, err := syscall.Accept(fd) fmt.Println(nfd, sa, err) time.Sleep(time.Second * 3 ) syscall.Close(nfd) fmt.Println("close conn" ) } }
客户端代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 var mu sync.Mutexfunc main () { var localPort = 3000 go userConnect(localPort, 8000 ) userConnect(localPort, 8001 ) } func userConnect (localPort, serverPort int ) { var fd int var err error var ServerAddr syscall.SockaddrInet4 if fd, err = syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_IP); err != nil { panic (err) } if err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1 ); err != nil { panic (err) } ServerAddr.Port = localPort ServerAddr.Addr = [4 ]byte {127 , 0 , 0 , 1 } mu.Lock() if err = syscall.Bind(fd, &ServerAddr); err != nil { panic (err) } else { fmt.Printf("Bind port %d\n" , localPort) } mu.Unlock() ServerAddr.Port = serverPort ServerAddr.Addr = [4 ]byte {127 , 0 , 0 , 1 } if err = syscall.Connect(fd, &ServerAddr); err != nil { panic (err) } else { fmt.Printf("Connect success\n" ) } time.Sleep(time.Second * 10 ) }
是什么限制了服务器的 TCP 连接数:
端口范围
如果某个客户端向同一个 TCP 端点 (ip:port) 发起主动连接, 那么每一条连接都必须使用不同的本地TCP端点, 如果客户端只有一个IP则是使用不同的本地端口, 该端口的范围在 linux 系统上的一个例子是32768到61000, 可以通过如下命令查看:
1 2 [root@VM_0_13_centos ~]# cat /proc/sys/net/ipv4/ip_local_port_range 32768 60999
也就是说, 一个客户端连接同一个服务器的同一个ip:port(比如进行压力测试), 最多可以发起30000个左右的连接.
TCP客户端(TCP的主动发起者)可以在同一ip:port上向不同的服务器发起主动连接, 只需在bind之前对socket设置 SO_REUSEADDR
选项.
系统支持的最大打开文件描述符数
全局限制:
1 2 [root@VM_0_13_centos ~]# cat /proc/sys/fs/file-max 183994
进程限制:
1 2 [root@VM_0_13_centos ~]# ulimit -n 10240
结论:
无论是对于服务器还是客户端, 认为“一台机器最多建立65536个TCP连接”是没有根据的, 理论上远远超过这个值.
另外, 对于client端, 操作系统会自动根据不同的远端 ip:port, 决定是否重用本地端口.