百万连接实现01:使用epoll+多线程+多IP地址管理tcp客户端集群

news2024/9/24 7:21:54

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

所以要想实现百万连接:

  • 第一种是服务器端只开启一个进程,然后使用很多个客户端进程绑定不同的客户端 ip 来连接,假设 20个ip * 5w(端口范围是最大65535,这里保守算5w)

  • 第二种是服务器开启多个进程,这样客户端就可以只使用一个 ip 即可,原理类似。

实验说明 

 第一个实验

只是简单的创建一个连接,并使用epoll监听它的读事件

第二的实验  

1 使用任意多个线程,

2 每个线程创建任意数量的客户端

3 每个线程都有一个独立的epoll

4 各个线程管理各自的epoll

5 每个epoll管理各自所在线程创建的客户端

6 每个线程绑定一个独立的IP地址,这个IP地址是通过下面的代码创建的,这里也是使用一个进程能够创建百万连接的关键。

        snprintf(pd->ip_str, sizeof(pd->ip_str),"192.168.0.%d",host_index);
        DEBUG_INFO("pd->ip_str = %s",pd->ip_str);
        snprintf(cmd_str, sizeof(cmd_str),"sudo ip addr add %s/24 dev ens33 1>/dev/null 2>&1",pd->ip_str);        
        DEBUG_INFO("cmd_str = %s",cmd_str);
        system(cmd_str);

第一个实验:单客户端测试

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>

#define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%s:%d -- "format"\n" \
,__FILE__,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endif

#define _DEBUG_PRINT
#ifdef _DEBUG_PRINT
#define DEBUG_PRINT(format,...)	printf(format,##__VA_ARGS__)	
#else
#define DEBUG_PRINT(format,...)
#endif



struct epoll_event evlist[10];
static char buf[1024 + 1];

int main(int argc, char **argv)
{
    int s;
    s = socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);

    int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);

    struct sockaddr_in server_addr;
    server_addr.sin_port = htons(6600);
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr("192.168.0.5");
    connect(s,(const struct sockaddr *)&server_addr,sizeof(server_addr));    

    int epfd = epoll_create(1);
    struct epoll_event client_ev;
    client_ev.data.fd = s;
    client_ev.events = EPOLLIN;

    epoll_ctl(epfd, EPOLL_CTL_ADD,s,&client_ev);

    while(1){
        int ready = epoll_wait(epfd,evlist,sizeof(evlist)/sizeof(evlist[0]),-1);
        if(ready == -1){
            if(errno == EINTR){
                continue;
            }
            perror("epoll_wait");
            exit(1);
        }
        for(int i=0;i<ready;i++){
            int fd = evlist[i].data.fd;
            DEBUG_INFO("fd = %d:events:%s%s%s",fd,
                        (evlist[i].events & EPOLLIN)?"EPOLLIN":"",
                        (evlist[i].events & EPOLLHUP)?"EPOLLHUP":"",
                        (evlist[i].events & EPOLLERR)?"EPOLLERR":"");
            int read_len = read(fd,buf,sizeof(buf) - 1);
            if(read_len == -1){
                if(errno == EINTR){
                    continue;
                }else{
                    perror("Read");
                    close(fd);
                }
            }
            DEBUG_INFO("read_len: %d",read_len);
            if(read_len == 0){
                DEBUG_INFO("client disconnected");
                close(fd);
            }
            buf[read_len] = '\0';
            DEBUG_INFO("buf = %s",buf);
        }
    }
    DEBUG_INFO("bye bye");
    return 0;
}

使用调试助手创建服务器

 使用调试助手发送数据,然后关闭服务器,测试结果:

第二个实验:客户端集群

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>

#define _DEBUG_INFO
#ifdef _DEBUG_INFO
#define DEBUG_INFO(format, ...) printf("%s:%s:%d -- "format"\n" \
,__FILE__,__func__,__LINE__ \
, ##__VA_ARGS__)
#else
#define DEBUG_INFO(format, ...)
#endif

#define _DEBUG_PRINT
#ifdef _DEBUG_PRINT
#define DEBUG_PRINT(format,...)	printf(format,##__VA_ARGS__)	
#else
#define DEBUG_PRINT(format,...)
#endif

// ip addr add 192.168.0.12/24 dev ens33


static char buf[1024 + 1];

#define DEFAULT_CLIENT_COUNT 20

#define HOST_NUM 40

struct private_data{
    char ip_str[20];
    char server_ip[20];
    pthread_t thread_id;
    int client_count;
    struct epoll_event evlist[10000];
    uint16_t port;
};

void * client_thread(void * arg){
    struct epoll_event client_ev;
    int epfd;
    int client_socket;
    struct private_data *pd = (struct private_data *)arg;
    int res = 0;

    epfd = epoll_create(1);
    if(epfd < 0){
        perror("epoll_create");
        return NULL;
    }

    struct sockaddr_in server_addr;
    server_addr.sin_port = htons(pd->port);
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(pd->server_ip);//"192.168.0.5");

    struct sockaddr_in client_addr;
    client_addr.sin_port = 0;
    client_addr.sin_family = AF_INET;
    client_addr.sin_addr.s_addr = inet_addr(pd->ip_str);
    
    for(int i = 0; i < pd->client_count; i++){
        client_socket = socket(PF_INET, SOCK_STREAM | SOCK_CLOEXEC,0);// | SOCK_NONBLOCK, 0);
        if(client_socket < 0){
            perror("socket");
            exit(1);
        }
        res = bind(client_socket,(const struct sockaddr *)&client_addr,sizeof(client_addr));
        if(res < 0){
            perror("bind");
            exit(1);
        }

        res = connect(client_socket,(const struct sockaddr *)&server_addr,sizeof(server_addr));    
        if(res < 0){
            perror("connect");
            exit(1);
        }
        client_ev.data.fd = client_socket;
        client_ev.events = EPOLLIN;
        res = epoll_ctl(epfd, EPOLL_CTL_ADD,client_socket,&client_ev);
        if(res < 0){
            perror("connect");
            exit(1);
        }
    }
    
    DEBUG_INFO("%s Connect %s:%d count=%d",
    pd->ip_str,
    pd->server_ip,
    pd->port,
    pd->client_count);
    while(1){
        int ready = epoll_wait(epfd,pd->evlist,sizeof(pd->evlist)/sizeof(pd->evlist[0]),-1);
        if(ready == -1){
            if(errno == EINTR){
                continue;
            }
            perror("epoll_wait");
            exit(1);
        }
        DEBUG_INFO("ready %d",ready);
        for(int i=0;i<ready;i++){
            int fd = pd->evlist[i].data.fd;
            DEBUG_INFO("fd = %d:events:%s %s %s",fd,
                        (pd->evlist[i].events & EPOLLIN)?"EPOLLIN":"",
                        (pd->evlist[i].events & EPOLLHUP)?"EPOLLHUP":"",
                        (pd->evlist[i].events & EPOLLERR)?"EPOLLERR":"");
            int read_len = read(fd,buf,sizeof(buf) - 1);
            if(read_len == -1){
                if(errno == EINTR){
                    continue;
                }else{
                    perror("Read");
                    epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
                    close(fd);
                    continue;
                }
            }
            DEBUG_INFO("read_len: %d",read_len);
            if(read_len == 0){
                DEBUG_INFO("client disconnected");
                close(fd);
                goto OUT;
            }
            buf[read_len] = '\0';
            DEBUG_INFO("buf = %s",buf);
        }
    }
OUT:
    DEBUG_INFO("bye bye");
    close(epfd);
    free(pd);
    return NULL;
}

int main(int argc, char **argv)
{
    int s;
    
    int res = 0;
    
    int client_count = 10;  
    
    char cmd_str[1024];
    int host_min_num = 12;
    int max_host_num = 2;//HOST_NUM
    int opt;
    char server_ip[20] = "192.168.0.5";

    static int thread_count = 1;
    int port = 6600;
    
    while((opt = getopt(argc,argv,"c:r:t:p:")) != -1){
        switch (opt)
        {
        case 'r':
            DEBUG_INFO("option:%c,server ip = %s",opt,optarg);
            memcpy(server_ip,optarg,strlen(optarg));
            server_ip[strlen(optarg)] = '\0';
            break;
        case 'c':
            DEBUG_INFO("option:%c client_count = %s",opt,optarg);
            client_count = atoi(optarg);
            break;
        case 'p':
            DEBUG_INFO("option:%c client_count = %s",opt,optarg);
            port = atoi(optarg);
            break;
        case 't':
            DEBUG_INFO("option:%c thread_count = %s",opt,optarg);
            thread_count = atoi(optarg);
            break;
        case ':':
            DEBUG_INFO("option:%c needs a value",opt);    
            exit(0);   
            break;
        case '?':
            DEBUG_INFO("unkown option:%c needs a value",optopt);       
            break;
        default:
            break;
        }
    }
    DEBUG_INFO("client_count = %d",client_count);
    DEBUG_INFO("server_ip = %s",server_ip);
    DEBUG_INFO("thread_count = %d",thread_count);

    //创建40个ip地址192.168.0.12 - 192.168.0.51
    for(int host_index = host_min_num; host_index < host_min_num + thread_count;host_index++){
        struct private_data *pd = (struct private_data*)malloc(sizeof(struct private_data));
        if(pd == NULL){
            perror("malloc");
            exit(0);
        }
        snprintf(pd->ip_str, sizeof(pd->ip_str),"192.168.0.%d",host_index);
        DEBUG_INFO("pd->ip_str = %s",pd->ip_str);
        snprintf(cmd_str, sizeof(cmd_str),"sudo ip addr add %s/24 dev ens33 1>/dev/null 2>&1",pd->ip_str);        
        DEBUG_INFO("cmd_str = %s",cmd_str);
        system(cmd_str);
        pd->client_count = client_count;
        pd->port = port;
        memcpy(pd->server_ip, server_ip, sizeof(server_ip));
        res = pthread_create(&pd->thread_id, NULL,client_thread,pd);
        if(res != 0){
            perror("pthread_create");
            exit(1);
        }
    }
    while(1){
        sleep(1);
    }
    return 0;
}

编译并运行:

sudo ./_build_/client -c 4 -r 192.168.0.5 -t 3

参数说明:

-c:每个线程创建的客户端数量

-r:远程服务器IP地址

-t:创建的线程数量 

服务器调试工具:

在服务器上看到了12个客户端

3个线程,没个线程4个客户端。

下次,写一个能够接收很多客户端的服务器

例如

sudo ./_build_/client -c 25000 -r 192.168.0.5 -t 40

40个线程,每个线程25000个客户端。

小结

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/713279.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux使用grep命令查询nginx的进程情况时总是出现 grep --color=auto nginx

问题&#xff1a; 每次使用ps aux | grep 服务名 命令查询某个服务的进程时&#xff0c;总会出现一条grep --colorauto 服务名 例如&#xff1a; ps aux | grep nginx # 会出现图片中的情况解答&#xff1a; 这是因为grep 也是一条命令&#xff0c;它在输出时&#xff0c;会…

《谈事说理》之《“脱节”的顶层设计》续集

欢迎收看本期《谈事说理》之《“脱节”的顶层设计》续集。当事人再次来到节目现场&#xff0c;为我们讲述近期事件的新发展。他与父母苦心经营的企业是否出现转机&#xff1f;我们一起来听听他的故事。 回忆事件经过&#xff0c;他的企业遭遇灭顶 当事人季博文&#xff08;化名…

Express-基础语法

介绍 Express是基于Node.js开发的第三方模块包&#xff0c;使用 Express&#xff0c;我们可以方便、快速的创建 Web 网站的服务器或 API 接口的服务器 参考 https://www.expressjs.com.cn/ 基本用法 安装 npm install express4.17.1 后面追加版本号&#xff0c;不写安装最新…

Docker 安装 MySQL 并使用 Navicat 连接

本文目录 1. 拉取 MySQL 镜像2. 创建并运行一个 MySQL 容器3. 验证MySQL容器是否创建并运行成功3.1 进入 MySQL 容器3.2 进入 MySQL3.3 查看 host 和 user 4. MySQL 开启远程访问权限4.1 切换数据库4.2 给 root 用户分配远程访问权限4.3 强制刷新权限 5. 服务器配置 3306 的开放…

iOS开发中的APP内活动之通用链接

首先&#xff0c;我们先来了解下APP内活动&#xff0c;这是苹果官方给的说明&#xff1a;https://developer.apple.com/cn/help/app-store-connect/offer-in-app-events/overview-of-in-app-events简单来说&#xff0c;就是我们在苹果后台开发者后台里填写关于自己APP内的一些具…

《MySQL》对表进行操作(DDL语句)

文章目录 &#x1f4a1;创建表&#x1f4a1;修改表&#x1f4a1;删除表 在了解下列语句前&#xff0c;先掌握一下指令 # 查看表内容 desc [表名] # 详细查看表内容 show create table [表名] \G&#x1f4a1;创建表 # 创建表 create table [表名] ([字段1] [类型1],[字段2] […

嵌入式 - UART使用进阶

UART – Advanced Features 概要 / Overview 最简单直接的使用UART接口的方式&#xff0c;是在轮循操作中来设置和处理UART接口。 轮询式UART的问题是轮询方式本身就是低效率的。 如果我们的UART被配置为115200的波特率和8N1&#xff0c;那么传输一个字符需要多长时间&#xff…

一款支持AI思维导图的AI助手——ChatMindAI

写在前面 Hello大家好&#xff0c; 我是【麟-小白】&#xff0c;一位软件工程专业的学生&#xff0c;喜好计算机知识。希望大家能够一起学习进步呀&#xff01;本人是一名在读大学生&#xff0c;专业水平有限&#xff0c;如发现错误或不足之处&#xff0c;请多多指正&#xff0…

“智慧水利”怎么建?这份智慧水利整体解决方案 值得参?

2021年&#xff0c;补齐水利信息化突出短板&#xff0c;提升强监管支撑能力。 2025 年&#xff0c;全面提升水利数字化、网络化水平&#xff0c;明显提升重点领域智能化水平。 2035 年&#xff0c;全面支撑水治理体系和治理能力现代化。 构建天空地一体化水利感知网&#xf…

python怎么使用Pillow库来添加图片水印

当使用Pillow库来添加图片水印时&#xff0c;下面是一个示例代码&#xff1a; 请确保在运行示例代码之前&#xff0c;已经安装了Pillow库&#xff08;可以使用pip install pillow命令进行安装&#xff09;。示例代码中&#xff0c;打开原始图片、创建透明的水印图层、绘制水印文…

计模式篇(Java):桥接模式

上一篇&#xff1a;计模式篇(Java)&#xff1a;适配器模式 九、桥接模式 需求示例 当我们对不同手机类型的不同品牌实现操作编程&#xff0c;如图&#xff1a; 那么它对应的类图就是 传统方式解决需求分析&#xff1a; 扩展性问题&#xff0c;如果需要在增加手机的样式&#x…

什么是业务逻辑攻击 (BLA),大家为什么一定要要关注它?

想象一下&#xff1a;您的开发团队刚推出了一款令人惊叹的全新应用程序&#xff0c;它具有顶级的API安全性&#xff0c;通过客户端保护对其进行了强化&#xff0c;甚至还设置了针对机器人攻击的防御措施。你感到这款产品很有安全保障&#xff0c;自己的团队出色地完成了工作。 …

SAP从入门到放弃系列之生产执行-领用与消耗概述

文章目录导航 一、生产物资领用1.1、概述1.2、项目中自开发领料功能方式一&#xff1a;创建预留的方式方式二&#xff1a;创建UB订单方式 二、生产投料 一、生产物资领用 1.1、概述 生产的备料方式因为企业的行业特点以及企业对材料库存管理的流程设置&#xff0c;企业使用的…

MATLAB | 高斯变量概率密度函数的理论与实际对比

一、仿真实验目的 生成一组高斯变量&#xff0c;并基于生成的数据统计其概率密度函数&#xff0c;与理论高斯概率密度函数进行对比&#xff0c;观察生成的高斯变量的概率分布。 二、解决思路 &#xff08;1&#xff09;利用randn函数生成高斯随机变量 &#xff08;2&#xf…

利用Visual Studio 2022 导出目标dll API接口

利用Visual Studio 2022 导出目标dll API接口 操作路径: 指令如下: dumpbin /exports /out:C:\\Users\\Administrator\\Desktop\\PlantSimCore.txt C:\\Users\\Administrator\\Desktop\\PlantSimCore.dll dumpbin /exports /out:C:\\Users\\Administrator\\Desktop\\

Ubuntu18.04安装Qt5.14.2

一、安装 第一步&#xff1a; 官网Index of /archive/qt 下载安装包&#xff0c; 或者国内网址下载 https://mirrors.tuna.tsinghua.edu.cn/qt/archive/qt/5.9/5.9.0/ 我安装的是QT5.14.2 中的 qt-opensource-linux-x64-5.14.2.run ; 第二步&#xff1a;ctrlT 打开终端输入命…

Python的Logging模块

1.日志的相关概念&#x1f343; 日志是指记录系统或应用程序运行状态、事件和错误信息的文件或数据。在计算机系统中&#xff0c;日志通常用于故障排除、性能分析、安全审计等方面。日志可以记录各种信息&#xff0c;如系统启动和关闭时间、应用程序的运行状态、用户登录和操作…

数字化就是做系统?广州数字化转型服务公司推荐

数字化转型是一个与数字化技术密切相关的概念&#xff0c;很多不了解数字化转型的企业往往会认为数字化转型就是提升企业的数字化技术应用水平和不断增加数字化系统的功能&#xff0c;也就是将数字化转型认为是做系统。但实际上&#xff0c;开利网络认为&#xff0c;数字化转型…

使用vtk在一个窗口中创建多个几何体

引言 该示例为官网上的例子。在一个窗口中创建了多个不同的几何体。 其效果如下&#xff1a; 示例 开发环境 使用QtCreator4.11.2,Qt5.14.2。使用的vtk9.2的库及其头文件。创建空项目。 示例代码 其pro文件中的内容&#xff1a; QT core#greaterThan(QT_MAJOR_V…