测试Linux下tcp最大连接数限制

原文地址: https://blog.csdn.net/lyztyycode/article/details/80463806

今天写程序用到epoll的模型,翻出原先的代码跑了一下,看到原来define的最大的处理用户上限,感觉有些不妥,所以决定测试一下我的ubuntu 16.04,1G内存的单机上究竟可以建立多少个连接。虽然网上有很多这方面的案例,但是我还是决定自己测试一下,印象深刻,对问题场景有更深的印象。

如果文中有错误或者不全面的地方,希望大家指正。

纠正博客中的错误:我的客户端和服务器在单机上进行,所以其实端口是被客户端消耗掉的,所以我本文中说的端口限制只能限制开放的客户端的上限,实际并不能限制服务器处理的连接数的上限,理论上一台服务器处理的连接数在内存足够多的情况下是可以处理无数连接的(2^48理论数量)。

这篇文章中我没有理解的概念是listen的soccketfd和accept返回的socketfd的不同,listen的socket创建的socketfd绑定的端口,这个socketfd是告诉TCP/IP协议栈这个socketfd指向绑定的ip+port,以后有发往这个ip+port的数据包都是发给这个socket,accept返回的connfd是用来标识一个连接五元组的,所以,tcp服务器能处理的连接数实际是由五元组来确定的,更准确的说是由客户端的ip+port来决定连接数的,对于ipv4地址232,port是216,所以理论的连接数最多是2^48。但实际情况中这个和设备的内存,一条tcp连接占用的内存有关,所以,要切记,65535并不是单机服务器处理的连接数上限。65535硬要说是上限,那就是单机开放不同客户端的连接数。实际这也是不确切的,单机情况下,可以通过设置虚拟ip来突破单机65535这个上限。后续我会把我单机测试的服务器最大连接数的测试实例、如何修改内核参数来增大这个连接数的方法发出来。

我测试代码是自己写的,也很简单,就是客户端程序不停的请求连接(这里都是短链接,长连接和需要数据交互的连接请求情况可能和本文的情况不同,长连接就不会有大量的time_wait情况产生),服务器接收了客户端的请求建立连接,这种情况下,客户端不停的创建socket描述符,起初我的系统设置的上限是1024(ulimit -n查看)。

上代码:

server:

#include  <sys/types.h>       /* basic system data types */
#include  <sys/socket.h>      /* basic socket definitions */
#include  <netinet/in.h>      /* sockaddr_in{} and other Internet defns */
#include  <arpa/inet.h>       /* inet(3) functions */
#include <sys/epoll.h> /* epoll function */
#include <fcntl.h>     /* nonblocking */
#include <sys/resource.h> /*setrlimit */
 
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
 
#define MAXEPOLLSIZE 65535
#define MAXLINE 10
int setnonblocking(int sockfd)
{
    if (fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)|O_NONBLOCK) == -1) {
        return -1;
    }
    return 0;
}
 
static int iCount = 0;
static int iFaild = 0;
int main(void)
{
    int  servPort = 6888;
    int listenq = 65535;
 
    int listenfd, connfd, kdpfd, nfds, n, curfds, acceptCount = 0;
    struct sockaddr_in servaddr, cliaddr;
    socklen_t socklen = sizeof(struct sockaddr_in);
    struct epoll_event ev;
    struct epoll_event events[MAXEPOLLSIZE];
    struct rlimit rt;
    char buf[MAXLINE];
 
    /* 设置每个进程允许打开的最大文件数 */
    rt.rlim_max = rt.rlim_cur = MAXEPOLLSIZE;
    if (setrlimit(RLIMIT_NOFILE, &rt) == -1){
        perror("setrlimit error");
        return -1;
    }
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET; 
    servaddr.sin_addr.s_addr = htonl (INADDR_ANY);
    servaddr.sin_port = htons (servPort);
 
    listenfd = socket(AF_INET, SOCK_STREAM, 0); 
    if (listenfd == -1) {
        perror("can't create socket file");
        return -1;
    }
 
    int opt = 1;
    setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
 
    if (setnonblocking(listenfd) < 0) {
        perror("setnonblock error");
    }
 
    if (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(struct sockaddr)) == -1){
        perror("bind error");
        return -1;
    } 
    if (listen(listenfd, listenq) == -1){
        perror("listen error");
        return -1;
    }
    /* 创建 epoll 句柄,把监听 socket 加入到 epoll 集合里 */
    kdpfd = epoll_create(MAXEPOLLSIZE);
    //ev.events = EPOLLIN | EPOLLET;
    ev.events = EPOLLIN;
    ev.data.fd = listenfd;
    if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, listenfd, &ev) < 0){
        fprintf(stderr, "epoll set insertion error: fd=%d\n", listenfd);
        return -1;
    }
    curfds = 1;
 
    printf("epollserver startup,port %d, max connection is %d, backlog is %d\n", servPort, MAXEPOLLSIZE, listenq);
 
    for (;;) {
        /* 等待有事件发生 */
        nfds = epoll_wait(kdpfd, events, curfds, -1);
        if (nfds == -1){
            perror("epoll_wait");
            continue;
        }
        printf("epoll_wait return\n");
        
        /* 处理所有事件 */
        for (n = 0; n < nfds; ++n){
            if (events[n].data.fd == listenfd){
                connfd = accept(listenfd, (struct sockaddr *)&cliaddr,&socklen);
                if (connfd < 0){
                    iFaild ++;
                    if(iFaild >= 10)
                        close(listenfd);
                    perror("accept error");
                    continue;
                }
                iCount++;
                sprintf(buf, "accept form %s:%d\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port);
                printf("%d:%s  %d", ++acceptCount, buf, iCount);
                close(connfd);
                continue;
                } 
        }
    }
    close(listenfd);
    return 0;
}

client:


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <errno.h>
#include <arpa/inet.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
#define ERR_EXIT(m)\
    do{\
        perror(m); \
        exit(1); \
    }while(0)
int main(void)
{
 
    int socketfd, connfd;
    while(1)
    {
    socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if(socketfd < 0)
        ERR_EXIT("SOCKET");
    printf("socketfd = %d\n", socketfd);
    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    serveraddr.sin_port = htons(6888);
 
    //无限循环建立连接,不做数据处理
    int opt = 1;
    int ret2 = setsockopt(socketfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
    if(ret2 < 0)
        ERR_EXIT("setsockopt");
        printf("1\n");
        socklen_t socklen = sizeof(serveraddr);
        connfd = connect(socketfd, (const struct sockaddr*)&serveraddr, socklen);
        if(connfd < 0)
            ERR_EXIT("connfd");
    }
    close(socketfd);

服务器代码采用epoll,逻辑也简单,有必要说明一点,需要注意close(connfd)的位置,连接建立立马断开连接。还有需要设置SO_REUSEADDR。

ulimit -n 1024情况下:



这时候建立的连接数是1021个短链接,客户端请求了1021个socket描述符,012三个描述符被占用。

单机处理1024个连接轻而易举。

我们来看看提高file descriptors的上限,ulimit -n 65535(端口号是16位无符号整数)

我们再来运行,需要一点时间。

结果是:


意思是,没有可用端口了,我们从服务器端看看一共建立了多少次连接



你会不会想这个数据从何而来?我可以告诉你,其实如果你的机器速度快,这个数据在默认情况下应该是28232,为什么这莫说呢?别忘了我们建立的是短链接,我们的服务器是主动关闭连接的一方,熟悉tcp四挥手的你应该想知道主动关闭的一方在调用了close之后会进入TIME_WAIT状态,我之所以说我的这个数据是28386是因为我的运行时间大于linux默认的timewait时间,有的端口可以复用了,所以这个数值大于28233,那么28232是怎么来的呢?

我们运行这一条命令:

sysctl -a | grep port 

得到如下结果:



注意这一行:系统给我们的可用端口的范围是32768-60999,我们计算60999-32768+1正好是28232。所以说我们可以修改这个参数范围来提高支持的连接数。上面还提到产生了大量的timewait,我通过命令:

netstat -nt | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}' 

查看,也可以使用这个命令,netstat -a | grep -i TIME_WAIT | wc -l 结果相同。



用netstat -tcp更加直观:


提一下,之前我的测试服务器close(connfd)这一句并不是建立连接立即调用的,而是等客户端关闭然后服务器才调用,所以服务器是被动关闭的一方,你也可以试一试,这时候服务器会产生大量的closewait:

可能CLOSE_WAIT产生的原因:



大多数都是close的问题。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,607评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,239评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,960评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,750评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,764评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,604评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,347评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,253评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,702评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,893评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,015评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,734评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,352评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,934评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,052评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,216评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,969评论 2 355

推荐阅读更多精彩内容

  • 原文地址: https://www.cnblogs.com/duanxz/p/4464178.html 网络编程 ...
    Caiaolun阅读 16,258评论 0 8
  • 计算机网络概述 网络编程的实质就是两个(或多个)设备(例如计算机)之间的数据传输。 按照计算机网络的定义,通过一定...
    蛋炒饭_By阅读 1,227评论 0 10
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,657评论 18 139
  • 网络编程 网络编程对于很多的初学者来说,都是很向往的一种编程技能,但是很多的初学者却因为很长一段时间无法进入网络编...
    程序员欧阳阅读 2,014评论 1 37
  • 如果你感觉很好,就是在创造一个符合你向往的未来,如果你感觉不好,就是在创造一个偏离你向往的未来。你在生活的同时,...
    明媚如初_7f68阅读 404评论 3 7