epoll实现同时承载100w客户端的数量

概念

先表明,这里是让epoll能够同时承受100w的连接,不针对业务处理。

对于百万并发的业务处理,其前提条件就是要同时承受住100w的连接。

程序源码

  epoll的源码直接给出来

/*
    支持百万并发的 reactor
    1.其主要限制在于Linux系统的限制,需要修改一些参数

    测试: 3个标准
    1.wrk       --> qps 一秒钟能处理的请求量
    2.并发连接量 --> 
    3.iperf     --> 测试带宽(硬件能力)

    // gcc reactor.c -oreactor -mcmodel=medium -g
*/

#include <sys/epoll.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>

#define PORT 2480
#define MAX_EVENTS 1024

#define MAX_BUFFER_SIZE 1024

// 事件结构 -- 封装 读写缓冲区,读写事件处理方法
typedef int (*RCALLBACK)(int fd);

typedef struct _CONN_ITEM_{

    int fd;
    int rLen;
    int wLen;
    char readBuffer[MAX_BUFFER_SIZE];
    char writeBuffer[MAX_BUFFER_SIZE];

    // 函数指针 -- 这里我们根据 epoll返回的特性分为读事件和写事件
    RCALLBACK send_cb;
    RCALLBACK recv_cb;

}CONN_ITEM, LPTIEM;

CONN_ITEM connLists[1048576] = {0};
int epfd;

// 读事件
int send_cb(int fd){

    int Len = connLists[fd].wLen;
    int count = send(fd, connLists[fd].readBuffer, Len, 0);


    struct epoll_event evnet;
    evnet.data.fd = fd;
    evnet.events = EPOLLIN;
    epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &evnet);

}

// 写事件
int recv_cb(int fd){

    char *buffer = connLists[fd].readBuffer;
    int rLen = connLists[fd].rLen;

    int count = recv(fd, buffer + rLen, MAX_BUFFER_SIZE - rLen, 0);
    if(0 == count){
        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
        close(fd);
    }
    else
    {
        connLists[fd].rLen += count;

        // 回写数据
        memcpy(connLists[fd].writeBuffer, connLists[fd].readBuffer, connLists[fd].rLen);
        connLists[fd].rLen -= 0;
        connLists[fd].wLen = connLists[fd].rLen;

        // 将事件修改为触发写事件
        struct epoll_event event;
        event.data.fd = fd;
        event.events = EPOLLOUT;               
        epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &event);
    }
}


// 连接事件 - 属于读事件
int accept_cb(int fd){

    int ret = 0;

    struct sockaddr_in clienAddr;
    socklen_t len = sizeof(clienAddr);
    int connfd = accept(fd, (struct sockaddr*)&clienAddr, &len);
    if(connfd){
        // 将新的 fd加入到epoll中
        struct epoll_event event;
        event.data.fd = connfd;
        event.events = EPOLLIN;
        connLists[connfd].send_cb = send_cb;
        connLists[connfd].recv_cb = recv_cb;

        connLists[connfd].rLen = 0;
        connLists[connfd].wLen = 0;
        memset(connLists[connfd].readBuffer, 0, MAX_BUFFER_SIZE);
        memset(connLists[connfd].writeBuffer, 0, MAX_BUFFER_SIZE);

        ret = epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &event);
        if(-1 == ret){
            perror("epoll_ctl");
        }
    }

}

int main(){

    int ret = 0;
    int i = 0;

    // TCP
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sockfd){
        perror("socket");
        return -1;
    }

    // addr
    struct sockaddr_in serverAddr;
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serverAddr.sin_port = htons(PORT);

    ret = bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(struct sockaddr_in));
    if(-1 == ret){
        perror("bind");
        return -1;
    }

    // 这个地方的第二个参数,表示未连接的个数能容纳几个,防止syn泛洪
    listen(sockfd, 10);

    // 建立 epoll,参数以无意义(内核>=2.6.8) > 0
    epfd =  epoll_create(10);
    if(-1 == ret){
        perror("epoll_create");
        return -1;
    }

    // 将sockfd加入到 epoll中
    struct epoll_event event;
    event.data.fd = sockfd;
    event.events = EPOLLIN;           // 指定监控的事件
    connLists[sockfd].fd = sockfd;
    //connLists[sockfd].accept_cb = accept_cb;
    connLists[sockfd].recv_cb = accept_cb;
    connLists[sockfd].send_cb = send_cb;
    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event);
    if(-1 == ret){
        perror("epoll_ctl");
        return -1;
    }

    // 不断的监视消息 - 可能会触发多次
    struct epoll_event events[MAX_EVENTS];
    while(1){

        int ready = epoll_wait(epfd, events, MAX_EVENTS, -1);
        if(-1 == ret){
            perror("epoll_wait");
            return -1;
        }

        for(i = 0; i < ready; ++i){
            
            int fd = events[i].data.fd;
            if(events[i].events & EPOLLIN){
                // 读事件
                ret = connLists[fd].recv_cb(fd);
            }
            else if(events[i].events & EPOLLOUT){
                // 写事件
                ret = connLists[fd].send_cb(fd);
            }
        }
    }
    
    close(epfd);
    close(sockfd);

    return 0;
}



测试收发数据:

 测试程序

        逻辑,这里我们没有那么多IP进行测试,只能不断的建立连接,断开连接。在实际的应用过程中也是这样的,在后续的工作中,需要测试并发量的情况下,一下代码程序依旧适用。

正片开始

多试几次,发现每次都在一千左右崩溃掉了。

为什么会出现这种情况?

        其实不难理解,我们能够知道一个socket会对于一个fd(伪文件),对于一个进程而言,能够建立的连接数量是有限的,我们可以进行修改。

ulimit -n 1048576(一百万) --> 1024 *1024

修改完测试:

这里的情况是,在Linux中默认允许分配端口大概是2万多,我们改下配置文件。

客户端需要做更改,客户端的端口是随机分配的,到两万左右就嗝屁了。(这个根据内核版本决定)。

增加一行:

fs.file-max = 1048576 一个进程能打开的最哒的文件描述符的大小

server端直接崩了,出现这种情况大概率是内存不足的问题。换句话说,当协议盏暂用内存的比例到达一定比例的时候,进程会被操作系统强制终止。我们需要调整配置...

sudo modprobe ip_conntrack

调整完再测试下:

现在能跑到五十多万,显示连接被拒绝,说明服务端断掉了,我们继续调整配置。

接下来就是不断的调整这些数值...实在跑不上去,有可能是网络问题或者虚拟机内存问题,直接把虚拟机内存再加大。

再调整...mmp今天一定把他弄上去

调试小技巧:

现在真的有点烦了,愣是上不去...(淡定、淡定)

经验总结:

客户端尽量把缓冲区调小,服务端缓冲区要适当。

缓冲区越小,建立连接的速度越慢。

总要破百了,呜呜...感动落泪

程序员都是孤独虫....

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

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

相关文章

基于SpringBoot+JSP+Mysql宠物领养网站+协同过滤算法推荐宠物(Java毕业设计)

大家好&#xff0c;我是DeBug&#xff0c;很高兴你能来阅读&#xff01;作为一名热爱编程的程序员&#xff0c;我希望通过这些教学笔记与大家分享我的编程经验和知识。在这里&#xff0c;我将会结合实际项目经验&#xff0c;分享编程技巧、最佳实践以及解决问题的方法。无论你是…

大厂算法指南:优选算法 ——双指针篇(上)

大厂算法指南&#xff1a;优选算法 ——双指针篇&#xff08;上&#xff09; 前言&#xff1a;双指针简介一、[283.移动零](https://leetcode.cn/problems/move-zeroes/)1.1 算法思想&#xff08;快排的思想&#xff1a;数组划分区间 - 数组分两块&#xff09;1.2 算法流程1.3 …

neuq-acm预备队训练week 8 P8794 [蓝桥杯 2022 国 A] 环境治理

题目描述 输入格式 输出格式 输出一行包含一个整数表示答案。 输入输出样例 解题思路 最短路二分 AC代码 #include<bits/stdc.h> using namespace std; long long temp,n, Q; long long f[105][105],min_f[105][105],cut[105],dis[105][105];//cut为减少多少&#x…

在 Qt Creator 中编写 Doxygen 风格的注释

2023年12月10日&#xff0c;周日上午 如何生成Doxygen 风格的注释 在需要Doxygen 风格注释的函数上方输入 /**&#xff0c;然后按下 Enter 键。Qt Creator 将自动为你生成一个注释模板。 输入&#xff0c;Qt Creator会自动帮你补全Doxygen标签 不得不说&#xff0c;写了Doxyge…

【HarmonyOS开发】详解常见容器的使用

声明式UI提供了以下8种常见布局&#xff0c;开发者可根据实际应用场景选择合适的布局进行页面开发。 布局 应用场景 线性布局&#xff08;Row、Column&#xff09; 如果布局内子元素超过1个&#xff0c;且能够以某种方式线性排列时优先考虑此布局。 层叠布局&#xff08;St…

使用alpine镜像部署go应用时踩的坑

使用alpine镜像部署go应用时踩的坑 关于交叉编译 实际上我在ubuntu的交叉编译出来的exe并不能在alpine上运行&#xff0c;这边采取拉镜像编译复制出来的做法&#xff0c;部署再用干净的alpine 拉取golang:alpine踩坑 在Dockerhub上可以找到&#xff1a; 然而拉取的alpine中…

Cpolar配置外网访问和Dashy

Dashy是一个开源的自托管的导航页配置服务,具有易于使用的可视化编辑器、状态检查、小工具和主题等功能。你可以将自己常用的一些网站聚合起来放在一起,形成自己的导航页。一款功能超强大,颜值爆表的可定制专属导航页工具 结合cpolar内网工具,我们实现无需部署到公网服务器…

周周清(2)----踩坑日记

周一&#xff1a; 1.之前换了一个jdk&#xff0c;然后又改了很多东西&#xff0c;很乱&#xff0c;以至于很多项目都不能直接运行了&#xff0c;所以今天就将ideal删除并且更新版本到2022.3.3&#xff0c;并且重新将ideal里面的配置环境变量&#xff0c;以及jdk下载安装配置&a…

【论文阅读笔记】NeRF+Mip-NeRF+Instant-NGP

目录 前言NeRF神经辐射场体渲染连续体渲染体渲染离散化 方法位置编码分层采样体渲染推导公式&#xff08;1&#xff09;到公式&#xff08;2&#xff09;部分代码解读相机变换&#xff08;重要&#xff01;&#xff09; Mip-NerfTo do Instant-NGPTo do 前言 NeRF是NeRF系列的…

【unity小技巧】实现枪武器随镜头手臂摇摆效果

文章目录 前言方法一、改变武器位置方法二、改变武器旋转结语完结 前言 如果我们视角移动转向&#xff0c;武器如果不跟着进行摇摆&#xff0c;会感觉我们的动作很生硬&#xff0c;特别是射击类游戏&#xff0c;如下 实现武器摇摆这里主要分享两种实现方法&#xff0c;一种是…

git学习笔记03(小滴课堂)

详解分支的基本操作 创建分支&#xff1a; 查看分支&#xff1a; 切换分支&#xff1a; git branch 中星号是当前分支。 idea中也更新了。 提交上去。 我们新建个分支&#xff1a; 我们新建分支是复制当前分支&#xff0c;而不是直接复制的主分支。 我们切换回主分支&#xf…

Docker 入门

Docker 入门 基础 不同操作系统下其安装包、运行环境是都不相同的&#xff01;如果是手动安装&#xff0c;必须手动解决安装包不同、环境不同的、配置不同的问题 而使用Docker&#xff0c;这些完全不用考虑。就是因为Docker会自动搜索并下载MySQL。注意&#xff1a;这里下载…

TeeChart.NET 2023.11.17 Crack

.NET 的 TeeChart 图表控件提供了一个出色的通用组件套件&#xff0c;可满足无数的图表需求&#xff0c;也针对重要的垂直领域&#xff0c;例如金融、科学和统计领域。 数据可视化 数十种完全可定制的交互式图表类型、地图和仪表指示器&#xff0c;以及完整的功能集&#xff0c…

【Spring Boot 源码学习】ApplicationListener 详解

Spring Boot 源码学习系列 ApplicationListener 详解 引言往期内容主要内容1. 初识 ApplicationListener2. 加载 ApplicationListener3. 响应应用程序事件 总结 引言 书接前文《初识 SpringApplication》&#xff0c;我们从 Spring Boot 的启动类 SpringApplication 上入手&am…

使用WebyogSQLyog使用数据库

数据库 实现数据持久化到本地&#xff1a; 使用完整的管理系统统一管理&#xff0c; 数据库&#xff08;DateBase&#xff09;&#xff1a; 为了方便数据存储和管理&#xff08;增删改查&#xff09;&#xff0c;将数据按照特定的规则存储起来 安装WebyogSQLyog -- 创建数…

Elasticsearch:向量数据库的真相

通过工作示例了解什么是向量数据库、它们如何实现 “相似性” 搜索以及它们可以在明显的 LLM 空间之外的哪些地方使用。除非你一直生活在岩石下&#xff0c;否则你可能听说过诸如生成式人工智能和大型语言模型&#xff08;LLM&#xff09;之类的术语。 除此之外&#xff0c;你很…

nodejs+vue+微信小程序+python+PHP个性化服装搭配系统APP-计算机毕业设计推荐 android

考虑到实际生活中在个性化服装搭配方面的需要以及对该系统认真的分析,将app权限按管理员和用户这两类涉及用户划分。 (a) 管理员&#xff1b;管理员使用本系统涉到的功能主要有个人中心、用户管理、个性穿搭管理、我的衣橱管理、服饰分类管理、我的收藏管理、系统管理等功能。 …

大华摄像头windows、linuxJavaSDK开发使用

文章目录 简介环境要求库加载问题及解决方法大华摄像头Java SDK&#xff0c;完成摄像头设备登录、视频录像目录结构windows 的c代码Linux的C代码项目结构 登录云台控制录像调用的接口注意码云地址 简介 本文档主要介绍 SDK 接口参考信息&#xff0c;包括主要功能、接口函数和回…

小模型学习(1)-人脸识别

【写作背景】因为最近一直在研究大模型&#xff0c;在与客户进行交流时&#xff0c;如果要将大模型的变革性能力讲清楚&#xff0c;就一定要能将AI小模型的一些原理和效果讲清楚&#xff0c;进而形成对比。当然这不是一件简单的事情&#xff0c;一方面大模型分析问题的的本质原…

Flask和Vue框架实现WebSocket消息通信

1 安装环境 1.1 安装Flask环境 主要的安装包 Flask、Flask-SocketIO&#xff0c;注意Python版本要求3.6 # Flask-SocketIO参考地址 https://flask-socketio.readthedocs.io/en/latest/ https://github.com/miguelgrinberg/flask-socketio更新基础环境 # 更新pip python -m …