赞
踩
ubuntu 虚拟机
服务器:使用epoll,接收所有连接,看是否能到达1M的连接数。
客户端:就是一个简单的while循环,循环只做一件事,就是申请一个socket然后connect()。
不断输出连接数看看能到达多少。
启动服务器绑定9911端口,启动client连接,发现只能保持1k左右的连接。
原因就在于shell 进程的最大打开文件fd,用于通信的clientfd也算,可以查看最大支持的fd数量是多少,使用 ulimit -n 查看。
ulimit -n
发现是1024,所以被限制在了1024。
通过改个大的值,同样的客户端也需要修改。(这个方法只在这个shell下生效)
ulimit -n 1048576
解决了fd限制的问题之后,继续测试。
客户端在连接到大2w8左右的时候connect()出错了,打印errno看看是99,cannot assign requested address,说明端口耗尽(实际上只是客户端对服务器的那个端口的组合耗尽了,客户端如果连接其他端口还能继续用,没有影响)。
TCP连接靠的是 sock 进行通信,每一个TCP使用的socket对应一个四元组(也就是一个连接) (源IP, 源Port,目的IP, 目的Port),要建立不同连接就需要改变这个四个中的一个,因为是同一台主机(我只用了一个IP),所以只能改变端口了。
可用端口可通过命令查看:
cat /proc/sys/net/ipv4/ip_local_port_range
可以看到这台虚拟机最多只能用 2w8 左右个端口。服务器绑定一个端口9911,客户端最多2w8个端口可用,连接最大只能到 1 * 2w8 = 2w8。
那么怎么才能多创建几个连接呢,如果服务端多几个端口,组合数就多了,sock对应的四元组组合(连接)就多了,如图:多了蓝色的几个连接。
服务器多监听几个端口试试。
这里在服务端创建了100个listenfd,绑定在100个不同的端口上,把这些listenfd都放到epoll里面去。客户端在connect失败之后就换一个服务器的端口进行connect(),结果如下,可以看到已经超过了可用端口数2w8.
上面已经超过了2w8(可用端口数),但是在连接停止在6w5左右。
解决了前两个限制,还有一个限制就是 nf_conntrack_max,当连接超过设定值(65535)的时候就无法连接上了,服务器配置问题。
Linux 内核中有一个内核模块 nf_conntrack 用于做连接跟踪,当 iptables 添加了 nat 相关规则时此模块会被自动启用。net.netfilter.nf_conntrack_max: 连接跟踪表最大值,超出后会导致操作系统会丢包。默认为 65535,优化至 6553500。
– 引用《https://xie.infoq.cn/article/c271f0b17cc3b0cf2c2f7f73c》
查看nf_conntrack_max的命令:
sysctl -a | grep nf_conntrack_max
# 输出:
# net.netfilter.nf_conntrack_max = 65535
# net.nf_conntrack_max = 65535
vi /etc/sysctl.conf
# 添加一行
net.netfilter.nf_conntrack_max = 1048576
# 执行下列命令使其生效
sysctl -p
再次测试:
20W连接,内存只有100M了,很慢了,没时间等了,就不继续往下了,维护TCP连接用了大概 1600M的内存。。。
#以下两个是进程级别的
# vi /etc/sysctl.conf
fs.nr_open=1100000 //要比 hard nofile 大一点
# sysctl -p(生效)
# vi /etc/security/limits.conf
* soft nofile 1000000
* hard nofile 1000000
# file-max 是系统级别的
fs.file-max=1100000
ls /proc/{pid}/fd/
epoll服务器测试代码。
g++ test_server.cpp -o server
./server server_port
#include <unistd.h> #include <sys/types.h> #include <cstring> #include <sys/socket.h> #include <arpa/inet.h> #include <stdlib.h> #include <sys/epoll.h> #include <iostream> #include <signal.h> #include <unordered_set> #define _TEST_MODE_ #define EPOLL_SIZE (1024 * 20) #define BUFF_SIZE 1024 // 多少个监听端口 #define MAX_SERVER_PORTS 100 bool is_listenfd(std::unordered_set<int>& fds, int fd) { return fds.find(fd) != fds.end(); } int main(int argc, char** argv) { #if 1 int epollfd = epoll_create(EPOLL_SIZE); epoll_event ev, events[EPOLL_SIZE]; // 创建100个listenfd std::unordered_set<int> listenfds; for(int i = 0; i < MAX_SERVER_PORTS; i++) { int listenfd = socket(AF_INET, SOCK_STREAM, 0); sockaddr_in server_addr; server_addr.sin_family = AF_INET; server_addr.sin_port = htons(atoi(argv[1])+i); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); bind(listenfd, (sockaddr*)&server_addr, sizeof(sockaddr_in)); if(-1 == listen(listenfd, 1024)) { std::cerr << "listen failed." << std::endl; exit(-1); } std::cout << i << " listening " << std::endl; listenfds.insert(listenfd); ev.events = EPOLLIN; ev.data.fd = listenfd; epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev); } char buff1[16] = {0}; char msg_buff[BUFF_SIZE + 1] = {0}; int conn_cnt = 0; while(1) { int nReady = epoll_wait(epollfd, events, EPOLL_SIZE, -1); for(int i = 0; i < nReady; i++) { if(is_listenfd(listenfds, events[i].data.fd)) { sockaddr_in client_addr; socklen_t len = sizeof(sockaddr_in); int clientfd = accept(events[i].data.fd, (sockaddr*)&client_addr, &len); if(clientfd <= 0) continue; ++conn_cnt; printf("listenfd: %d new clientfd:%d \t connections: %d\n",events[i].data.fd, clientfd, conn_cnt); ev.events = EPOLLIN | EPOLLET; ev.data.fd = clientfd; epoll_ctl(epollfd, EPOLL_CTL_ADD, clientfd, &ev); } else { int clientfd = events[i].data.fd; int read_len = recv(clientfd, msg_buff, BUFF_SIZE, 0); if(read_len <= 0) { close(clientfd); ev.data.fd = clientfd; ev.events = EPOLLIN | EPOLLET; epoll_ctl(epollfd, EPOLL_CTL_DEL, clientfd, &ev); } else { // 这个else分支可以不需要 } } } } close(epollfd); // close(listenfd); #endif return 0; }
客户端代码:
g++ test_client.cpp -o client
./client server_ip server_port
#include <unistd.h> #include <sys/types.h> #include <stdio.h> #include <memory> #include <stdlib.h> #include <sys/socket.h> #include <arpa/inet.h> #include <time.h> #include <errno.h> // 最多尝试 100 个server的监听端口 #define MAX_SERVER_PORTS 100 int main(int argc, char** argv) { sockaddr_in server_addr_list[MAX_SERVER_PORTS]; for(int i = 0; i < MAX_SERVER_PORTS; ++i) { server_addr_list[i].sin_family = AF_INET; server_addr_list[i].sin_port = htons(atoi(argv[2]) + i); server_addr_list[i].sin_addr.s_addr = inet_addr(argv[1]); } sockaddr_in server_addr; int cnt = 0; time_t start, last; start = last = time(NULL); int i = 0; while(1) { int sockfd = socket(AF_INET, SOCK_STREAM, 0); int state = connect(sockfd, (sockaddr*)&server_addr_list[i], sizeof(sockaddr)); // 换一个服务器端口进行连接 if(state == -1) { i++; printf("连接失败, errno: %d \n", errno); printf("connecting to %d(%d) server port with max_connections: %d in %d s.\n", i, ntohs(server_addr_list[i].sin_port), cnt, (int)difftime(time(NULL), start)); if(i >= MAX_SERVER_PORTS) break; continue; } if(++cnt % 1000 == 0) { time_t cur = time(NULL); printf("current connections: %d (1000 connections in %f s)\n", cnt, (float)difftime(cur, last)); last = cur; } } return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。