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

news/2024/4/26 0:02:17/文章来源:https://blog.csdn.net/yueni_zhao/article/details/131520239

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

所以要想实现百万连接:

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

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

实验说明 

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>
#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 ens33static char buf[1024 + 1];#define DEFAULT_CLIENT_COUNT 20#define HOST_NUM 40struct 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;
};static volatile int thread_finished_count = 0;static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;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);pthread_mutex_lock(&mutex);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");DEBUG_INFO("connect res = %d",res);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);}}pthread_mutex_unlock(&mutex);DEBUG_INFO("%s Connect %s:%d count=%d",pd->ip_str,pd->server_ip,pd->port,pd->client_count);thread_finished_count++;DEBUG_INFO("thread_finished_count = %d", thread_finished_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_NUMint 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.51for(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;
}

实验一:单客户端

创建tcp server,并处于监听状态

运行测试程序,并将参数设置为1个线程、单个连接 ,并设置tcp server端的IP地址。

./_build_/client -c 1 -t 1 -r 192.168.0.5

 运行结果:

 

实验二:多客户端

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.luyixian.cn/news_show_325619.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AIGC - Easy Diffusion (Stable Diffusion) 图像生成工具的环境配置

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://blog.csdn.net/caroline_wendy/article/details/131524075 版本v2.5.41 Stable Diffusion 图像生成工具是一种基于深度学习的技术&#xff0c;可以从随机噪声中生成高质量的图像&#x…

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

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

自学网络安全究竟该从何学起?

一、为什么选择网络安全&#xff1f; 这几年随着我国《国家网络空间安全战略》《网络安全法》《网络安全等级保护2.0》等一系列政策/法规/标准的持续落地&#xff0c;网络安全行业地位、薪资随之水涨船高。 未来3-5年&#xff0c;是安全行业的黄金发展期&#xff0c;提前踏入行…

MATLAB | 拉普拉斯分布/拉普拉斯噪声的生成

一、实验目标 生成拉普拉斯分布的噪声&#xff0c;并分析它的概率密度函数 二、解决思路 &#xff08;1&#xff09;拉普拉斯分布可以由指数分布生成 拉普拉斯的概率密度函数为 f ( x ; μ , λ ) 1 2 λ e − ∣ x − μ ∣ λ f(x;\mu,\lambda)\frac{1}{2 \lambda} e^{…

简单的手机记事本怎么把英文翻译成中文?

手机记事本是人们常用的辅助工具之一&#xff0c;在使用手机记事本记录内容的时候&#xff0c;除了我们平时使用较多的中文之外&#xff0c;也有人会记录一些英文内容。想要将手机记事本中的英文内容翻译成中文内容应该如何操作呢&#xff1f;以iPhone手机端敬业签记事本软件为…

GIS杂记(二):Arcgis对采样点进行裁剪,获取指定区域内的采样点

有时候需要对栅格数据进行采样处理&#xff0c;如果采样点过多则会使得采样时间过长&#xff0c;今天在进行数据采样时&#xff0c;使用了1km*1km的渔网建立的采样点&#xff0c;大概有1百万个点&#xff0c;程序运行时间大概4个小时&#xff0c;但是其中有绝大部分数据都是空值…

Css 基础:选择器,三大特性

1.emmet的 快速格式化代码 配置 "editor.formatOnType": true, "editor.formatOnSave": true 2.基础选择器 3.复合选择器 4.单行文本垂直居中原理 5.css背景 6.CSS三大特性 层叠性&#xff1a;相同选择器设置相同样式&#xff0c;发生在样式冲突时&#xf…

【PC】CPU与GPU

文章目录 CPU与主板CPU是什么主板是什么功能 GPU与显卡GPU是什么显卡是什么功能 CPU与GPU的关系 ALU&#xff1a; 算术单元&#xff08;Arithmetic Unit&#xff09;&#xff1a;算术单元执行基本的算术运算&#xff0c;如加法、减法、乘法和除法。它能够对整数、浮点数和定点数…

Web服务器群集:LVS+Keepalived高可用群集

目录 一、理论 1.Keepalived 2.VRRP协议&#xff08;虚拟路由冗余协议&#xff09; 3.部署LVSKeepalived 高可用群集 二、实验 1.LVSKeepalived 高可用群集 三、问题 1.备服务器网卡启动报错 四、总结 一、理论 1.Keepalived &#xff08;1&#xff09;简介 Keepal…

Redis高可用群集---搭建(主从、哨兵、Cluster)

目录 Redis 高可用集群Redis 主从复制Redis 哨兵模式Redis 集群模式 Redis 高可用集群 在web服务器中&#xff0c;高可用是指服务器可以正常访问的时间&#xff0c;衡量的标准是在多长时间内可以提供正常服务&#xff08;99.9%、99.99%、99.999%等等&#xff09;。 但是在Redi…

MySQL原理探索——22 MySQL有哪些“饮鸩止渴”提高性能的方法

不知道你在实际运维过程中有没有碰到这样的情景&#xff1a;业务高峰期&#xff0c;生产环境的 MySQL 压力太大&#xff0c;没法正常响应&#xff0c;需要短期内、临时性地提升一些性能。 我做项目的时候&#xff0c;就偶尔会碰上这种场景。用户的开发负责人说&#xff0c;不管…

Spring中bean使用方法

Spring框架是一个非常重要的开发工具&#xff0c;它提供了丰富的功能和模块&#xff0c;其中核心的概念之一就是Spring Bean。Spring Bean是Spring IoC容器中的一个对象&#xff0c;它负责管理一个Java对象的生命周期以及依赖注入。下面我将通过互联网场景下的相关背景内容来阐…

4通道AD采集子卡模块有哪些推荐?

FMC134是一款4通道3.2GSPS&#xff08;2通道6.4GSPS&#xff09;采样率12位AD采集FMC子卡模块&#xff0c;该板卡为FMC标准&#xff0c;符合VITA57.4规范&#xff0c;可以作为一个理想的IO模块耦合至FPGA前端&#xff0c;16通道的JESD204B接口通过FMC连接器连接至FPGA的高速串行…

分布式系统监控zabbix安装部署及自定义监控

系列文章目录 文章目录 系列文章目录一、zabbix1.zabbix的基本概述2.zabbix 是一个基于 Web 界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案。3.zabbix 监控原理4.Zabbix 6.0 功能组件5. zabbix的监控对象6.zabbix的常用术语7.zabbix进程详解8.zabbix的监控框…

安装配置云计算模板机

安装虚拟机模板机 一、在VMware上安装Centos虚拟机二、修改虚拟机的ip、网关、DNS三、更换yum源3.1 更换本地yum源3.2 更换国内互联网Yum源 四、 安装net-tools和bash-completion五、 关闭防火墙和SELinux 学习云计算后续需要多个虚拟机&#xff0c;作为云计算集群的节点&#…

【近场社交项目】数据库系统期末设计——需求分析部分

【近场社交项目】数据库系统设计——需求分析&#x1f60e; 前言&#x1f64c;1.需求求分析(用户部分为例&#xff09;1.2用户数据字典1.2.1用户信息表&#xff08;数据结构&#xff09;&#xff1a;数据项间的关系和结构定义&#xff1a; 1.2.2.个人资料表&#xff08;数据结构…

【React组件通讯的三种方式】

React组件通讯的三种方式 父组件传递数据给子组件子组件传递数据给父组件 React组件之间的通讯分为三种&#xff1a; 父组件 →子组件子组件 →父组件兄弟组件 父组件传递数据给子组件 步骤&#xff1a; 父组件提供要传递的state数据给子组件标签添加属性&#xff0c;值为st…

LeetCode·每日一题·445. 两数相加 II·模拟

作者&#xff1a;小迅 链接&#xff1a;https://leetcode.cn/problems/add-two-numbers-ii/solutions/2328613/mo-ni-zhu-shi-chao-ji-xiang-xi-by-xun-ge-67qx/ 来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 著作权归作者所有。商业转载请联系作者获得授权&#xff…

使用家庭宽带和摄像头,实现公网直播

那天去逛商场看到有个营业厅&#xff0c;本想进去问问有没有存话费送话费的活动&#xff0c;结果被忽悠办了一个19.9升千兆宽带加送一个路由器的业务。 网络环境验证 听他们说现在家庭宽带都是有公网IPV6地址的&#xff0c;立马用电脑试了下确实有IPV6地址。 赶紧随便写了几行…

记录使用ffmpeg把mp4转换成m3u8

背景:公司需要上一些视频资源,平均每一个都在600m以上,经过考虑以后采取视频分片以后上传到oss上进行加速播放的流程.这里记录一下使用ffmpeg进行转换视频格式的过程中的一些命令. 准备工作: 下载ffmpeg到本地,以及配置ffmpeg到环境变量中,这里就不多说了. 使用的时候先打开…