当前位置: 首页 > news >正文

linux网络编程 | c | epoll实现IO多路转接服务器

epoll实现IO多路转接服务器

可通过以下视频学习

06-opell函数实现的多路IO转接_哔哩哔哩_bilibili

通过响应式–多路IO转接实现

文章目录

  • epoll实现IO多路转接服务器
    • 1.思路&功能
      • 核心思路
    • 2.代码实现
      • multi_epoll_sever.c
      • 运行图

1.思路&功能

**功能:**客户端输入小写字符串,服务器转成大写返回给客户端

思路:

核心思路

  1. socket()、bind()、listen()
  2. epoll_create创建红黑树,它的返回值就是树的根节点
  3. epoll_ctl将listenfd添加到树上
  4. 循环epoll_wait进行监听,它的返回值是满足监听的总个数,所以以它的返回值为遍历上限去判断事件
  5. 如果它返回的数组中data.fd等于lfd,那么就accept去连接客户端 并将新的cfd加入树中
  6. 如果不是lfd,就说明有读事件发生,就去判断读到的返回值,<0是出错 ==0是客户端关闭(这两个都要去将该cfd从树中移除 并close),>0就处理数据然后写回

2.代码实现

multi_epoll_sever.c

#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
#include <fcntl.h>#define OPEN_MAX 5000
#define SERVE_PORT 9527int main()
{// 所需要的变量int lfd, cfd, efd, ret, wait_ret, i, sockfd, len;char buf[1024];// 地址结构体struct sockaddr_in serve_addr, client_addr;socklen_t client_addr_len;serve_addr.sin_family = AF_INET;                // IPV4serve_addr.sin_port = htons(SERVE_PORT);        // 绑定端口serve_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定ip(ANY系统自动分配)// 创建socketlfd = socket(AF_INET, SOCK_STREAM, 0);if (lfd < 0){perror("socket error");exit(1);}// bind绑定bind(lfd, (struct sockaddr *)&serve_addr, sizeof(serve_addr));// 设置上限listen(lfd, 128);// 创建红黑树efd = epoll_create(1); // efd就是树的根节点// 将lfd挂在树上// epoll结构体                       ep是epoll_wait所需的数组(存放满足事件的fd)struct epoll_event tep, ep[128]; //  tep是epoll_ctl的参数(传监听的事件)tep.events = EPOLLIN;tep.data.fd = lfd;ret = epoll_ctl(efd, EPOLL_CTL_ADD, lfd, &tep);if (ret < 0){perror("epoll_ctl error");exit(1);}// 循环去epoll_wait进行监听while (1){wait_ret = epoll_wait(efd, ep, 128, -1); // wait_ret就是实际满足事件的总个数// 以wait_ret为上限去遍历事件for (i = 0; i < wait_ret; i++)	{// sockfd用于接收满足事件的fdsockfd = ep[i].data.fd;// 如果等于lfd,那就说明有客户端要来连接,就去acceptif (sockfd == lfd)	{client_addr_len = sizeof(client_addr);cfd = accept(lfd, (struct sockaddr *)&client_addr, &client_addr_len);// 将cfd设置为非阻塞int flag = fcntl(cfd, F_GETFL);flag |= O_NONBLOCK;fcntl(cfd, F_SETFL, flag);// 把新的cfd加入树中tep.events = EPOLLIN | EPOLLET;tep.data.fd = cfd;ret = epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &tep);if (ret < 0){perror("epoll_ctl cfd error");exit(1);}}// 如果不是lfd,那就说明有读事件发生(读数据)else{len = read(sockfd, buf, sizeof(buf));if (len == 0) // 说明对方关闭连接(从树上摘下 & close){epoll_ctl(efd, EPOLL_CTL_DEL, sockfd, NULL);close(sockfd);}else if (len == -1){perror("read error");exit(1);}else // 读写数据{for (i = 0; i < len; i++)buf[i] = toupper(buf[i]);write(sockfd, buf, len);write(STDIN_FILENO, buf, len);}}}}return 0;
}
gcc multi_epoll_sever.c -o multi_epoll_sever
//也可以使用和其他多路IO一样的封装的warp.h warp.c的版本
//就不贴warp.c和warp.c了,大家有需要可以去select和poll的博客中查看
#include"warp.h"
#define SERV_PORT 9527
#define OPEN_MAX 20
int main(int argc,char * argv[])
{int i,listenfd,connfd,efd,res,nready,sockfd;int n,num=0;char buf[4096],str[INET_ADDRSTRLEN];socklen_t clien;listenfd=Socket(AF_INET,SOCK_STREAM,0);	int opt=1;setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));struct sockaddr_in servaddr,cliaddr;struct epoll_event tep,ep[OPEN_MAX];servaddr.sin_family=AF_INET;servaddr.sin_port=htons(SERV_PORT);servaddr.sin_addr.s_addr=htonl(INADDR_ANY);Bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));Listen(listenfd,128);efd=epoll_create(OPEN_MAX);if(efd==-1)perr_exit("epoll_create error");tep.events=EPOLLIN;tep.data.fd=listenfd;res=epoll_ctl(efd,EPOLL_CTL_ADD,listenfd,&tep);if(res==-1)perr_exit("epoll_ctl error");for(;;){nready=epoll_wait(efd,ep,OPEN_MAX,-1);if(nready==-1)perr_exit("epoll_wait error");for(i=0;i<nready;i++){if(!ep[i].events&EPOLLIN)continue;if(ep[i].data.fd==listenfd){clien=sizeof(cliaddr);connfd=Accept(listenfd,(struct sockaddr *)&cliaddr,&clien);printf("received from %s at PORT %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)),ntohs(cliaddr.sin_port));printf("cfd %d---client %d\n",connfd,++num);tep.events=EPOLLIN;tep.data.fd=connfd;res=epoll_ctl(efd,EPOLL_CTL_ADD,connfd,&tep);if(res==-1)perr_exit("epoll_ctl error");}else{sockfd=ep[i].data.fd;n=Read(sockfd,buf,4096);if(n==0){res=epoll_ctl(efd,EPOLL_CTL_DEL,sockfd,NULL);if(res==-1)perr_exit("epoll_ctl error");Close(sockfd);printf("client[%d] closed connection\n",sockfd);}else if(n<0){perror("read n<0 error:");res=epoll_ctl(efd,EPOLL_CTL_DEL,sockfd,NULL);Close(sockfd);}else{for(i=0;i<n;i++)buf[i]=toupper(buf[i]);Write(STDOUT_FILENO,buf,n);Write(sockfd,buf,n);}}}}Close(listenfd);Close(efd);return 0;
}

运行图

两个客户端访问服务器端

image-20241213104728787

http://www.lryc.cn/news/502823.html

相关文章:

  • Source Insight的使用经验汇总
  • VSCode 报错:rust-analyzer requires glibc >= 2.28 in latest build
  • Android Link to Death 使用
  • 【C++游记】string的使用和模拟实现
  • DockerUI info存在未授权访问漏洞
  • SQL,查询每天最接近指定时间的记录
  • ElasticSearch如何做性能优化?
  • 【Linux】虚拟空间布局模型地址回填数据段合并(万字详解)
  • const和修饰指针的几种用法
  • mybatis事务的自动提交与手动提交
  • 网络安全协议之比较(SSH、PKI、SET、SSL)
  • Vue的生命周期方法
  • ISP和IQ调试(一)
  • c# TaskScheduler
  • 可视化数据
  • 【Redis】Redis缓存击穿
  • 厦门凯酷全科技有限公司深耕抖音电商运营
  • 六西格玛DMAIC在企业得项目管理中有什么作用
  • vscode借助插件调试OpenFoam的正确的.vscode配置文件
  • SpringBoot整合JWT(JSON Web Token)生成token与验证
  • 把帕拉丁需要的.rom文件转成.bin
  • Nginx 缓存那些事儿:原理、配置和最佳实践
  • vue发展史
  • 基于Java和Vue开发的校园跑腿软件校园跑腿小程序系统源码
  • MySQL(五)--- 事务
  • llm chat场景下的数据同步
  • 机器学习经典算法
  • Scala中的泛型
  • 数据分析特征标准化方法及其Python实现
  • UnityShaderLab 实现程序化形状(一)