实践单机实现百万连接

2022-10-01  本文已影响0人  wayyyy
客户端最大连接数的上限?

操作系统采用 <客户端IP : 客户端端口> : <服务端IP : 服务端端口> 四元组来标识一条TCP连接。

所以要想实现百万连接:

但在这之前,需要我们先配置些参数:

文件句柄的限制

一个tcp连接就需要占用一个文件描述符,一旦文件描述符用完,新的连接就会返回给我们错误是:Can’topen so many files。linux系统出于安全角度的考虑,在多个位置都限制了可打开的文件描述符的数量,包括系统级、进程级、用户进程级。

端口号范围限制

操作系统上端口号1024以下是系统保留的,从1024-65535是用户使用的。但有时Linux 默认设置 端口范围并不是 1024-65535。

实践

按照方案一,我们只要实现单个客户端进程5w个连接就可以实现单机百万了。
所以这里实践只按照实现单个客户端进程5w个连接目标来做。

客户端代码:

#include <netinet/in.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h> 

#define MAX_CONNECTION_NUM 50000

int buildConnect(const char *lIp, const char *sIp, int sPort)
{
    int skFd; 
    if((skFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("\n Error : Could not create socket\n");
        return 0;
    }

    struct sockaddr_in cliAddr;
    cliAddr.sin_family = AF_INET;
    cliAddr.sin_addr.s_addr = inet_addr(lIp);
    cliAddr.sin_port = 0;
    if(bind(skFd,  (struct sockaddr *)&cliAddr, sizeof(cliAddr)) < 0)
    {
        printf("\n Error : Bind Failed \n");
    }

    struct sockaddr_in srvAddr;
    srvAddr.sin_family = AF_INET;
    srvAddr.sin_addr.s_addr = inet_addr(sIp);
    srvAddr.sin_port = htons(sPort); 
    if(connect(skFd, (struct sockaddr *)&srvAddr, sizeof(srvAddr)) < 0)
    {
       printf("\n Error : Connect Failed \n");
       return 0;
    } 

    return skFd;
}

int main(int argc, char *argv[])
{
    int i = 0, sPort, fd;
    char lIp[16], sIp[16];

    if(argc != 4)
    {
        printf("\n Usage: %s <local ip> <server ip> <server port>\n", argv[0]);
        return 1;
    }

    // 1. 从命令行获取并解析local ip、server ip以及端口
    strcpy(lIp, argv[1]);
    strcpy(sIp, argv[2]);
    sPort = atoi(argv[3]);
    
    // 2. 开始建立连接
    int *sockets = (int *)malloc(sizeof(int) * MAX_CONNECTION_NUM);
    for(i = 1; i <= MAX_CONNECTION_NUM; i++)
    {
        if(0 == i % 1000)
        {//稍稍停顿一下,避免把服务端的握手队列打满
            printf("%s 连接 %s:%d成功了 %d 条!\n", lIp, sIp, sPort, i);
            sleep(1);
        }
        
        fd = buildConnect(lIp, sIp, sPort);
        if(fd > 0)
        {
            sockets[i-1] = fd;
        }else{
            return 1;
        }
    }
    sleep(300);

    // 3. 释放所有的连接
    printf("关闭所有的连接...\n");
    for(i = 0; i < MAX_CONNECTION_NUM; i++)
    {
        close(sockets[i]);
    }
 
    return 0;
}

服务端代码

#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_CONNECTION_NUM 1100000

int main(int argc, char *argv[])
{
    char ip[16];
    int lisFd, conFd, port;
    struct sockaddr_in servAddr; 

    if(argc != 3)
    {
        printf("\n Usage: %s <server ip> <server port>\n", argv[0]);
        return 1;
    }

    // 1. 从命令行获取并解析server ip以及端口
    strcpy(ip, argv[1]);
    port = atoi(argv[2]);

    // 2. 创建server
    if((lisFd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("\n Error : Could not create socket\n");
        return 0;
    }

    servAddr.sin_family = AF_INET;
    servAddr.sin_addr.s_addr = inet_addr(ip);
    servAddr.sin_port = htons(port); 
    if(bind(lisFd, (struct sockaddr*)&servAddr, sizeof(servAddr)) < 0)
    {
        printf("\n Error : Bind Failed \n");
    }
    if((listen(lisFd, 1024)) < 0)
    {
        printf("\n Error : Listen Failed \n");
    }

    // 3. 接收连接
    int i = 0;
    int *sockets = (int *)malloc(sizeof(int) * MAX_CONNECTION_NUM);
    while(1)
    {
        conFd = accept(lisFd, (struct sockaddr*)NULL, NULL); 
        if(conFd > 0)
        {
            sockets[i++] = conFd;
            printf("%s %d accept success:%d\n", ip, port, i);
        }
    }
}

找两个机器,测试如下:
运行服务端

# gcc server.c -o server
# ./server 192.168.48.139 8080  # 服务端ip 为 192.168.48.139

运行客户端

# gcc client.c -o client
# ./client 192.168.48.137 192.168.48.139 8080  # 客户端ip为 192.168.48.137

参考资料:
1、https://mp.weixin.qq.com/s/f_CMt2ni0vegB3-pf2BTTg

上一篇下一篇

猜你喜欢

热点阅读