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

c语言——网络编程【多路文件IO实现 poll、epoll模型总结】内附代码

1.poll 模型

poll 函数原型 

函数原型:int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能描述:监视fds中的描述符是否激活
参数描述:参数 fds:是一个struct pollfd 结构体数组,该数组中存放了多个想要监视的描述符该结构体结构如下struct pollfd {int   fd;         /* 想要监视的描述符 */short events;     /* 确定fd描述,到底以何种形式进行监视 */POLLIN:监视fd描述是否可读POLLOUT:监视fd描述符是否可写                 short revents;    /* returned events */一般会随着结构体初始化为0,同步为0当 revents == 0的时候,表示fd这个描述符没有激活当 revents == POLLIN的时候,表示fd这个描述符可读激活了当 revents == POLLOUT的时候,表示fd这个描述可写激活了};参数 nfds:fds这个数组的长度参数 timeout:设置的最大监视时长注意:    单位是毫秒0表示不阻塞-1表示一直阻塞  实例struct pollfd arr[5] = {0}arr[0].fd = 0arr[0].events = POLLINpoll(arr) 表示监视 标准输入流是否可读

 poll_server模型 完整代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
#include <poll.h>typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;void insert_fd(struct pollfd* arr,int* len,struct pollfd fd){arr[*len] = fd;(*len)++;
}void remove_fd(struct pollfd* arr,int* len,int fd){int i = 0;for(i=0;i<*len;i++){if(fd == arr[i].fd){break;}}for(int j=i;j<*len-1;j++){arr[j] = arr[j+1];}(*len)--;
}int main(int argc, const char *argv[])
{if(argc != 2){printf("请输入端口号\n");return 1;}// 准备一个用来存放所有连接上来的客户端描述符的数组int client_arr[64] = {0};int client_arr_len = 0;// ./server 8888int port = atoi(argv[1]);// 将字符串 8888 转换成int类型port// 创建服务器套接字int server = socket(AF_INET,SOCK_STREAM,0);// 准备网络地址结构体:struct sockaddr_inaddr_in_t addr = {0};addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr("0.0.0.0");// 为套接字绑定ip 和 portif(bind(server,(addr_t*)&addr,sizeof(addr)) == -1){perror("bind");return 1;}// 监听listen(server,10);struct pollfd arr[20] = {0};int len = 0;// 0 和 server 放到 arr 里面去struct pollfd poll_stdin = {.fd=0,.events=POLLIN,.revents=0};struct pollfd poll_server = {server,POLLIN,0};insert_fd(arr,&len,poll_stdin);insert_fd(arr,&len,poll_server);while(1){poll(arr,len,-1);for(int i=0;i<len;i++){if(arr[i].revents == 0){continue;} // 该描述符没激活,直接看下一个描述符int fd = arr[i].fd;if(fd == server){printf("有新客户端连接\n");int client = accept(server,0,0);struct pollfd poll_client = {client,POLLIN,0};insert_fd(arr,&len,poll_client);}else if(fd == 0){char buf[64] = "";scanf("%63s",buf);while(getchar()!=10);printf("键盘输入数据:%s\n",buf);}else{// 剩下的都是客户端描述符了char buf[64] = "";int res = read(fd,buf,64);if(res == 0){remove_fd(arr,&len,fd);close(fd);}else{printf("%d#客户端发来消息:%s\n",fd,buf);}}}}return 0;
}

2.epoll模型 

epoll函数原型 

函数原型:int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout);
功能描述:监视 epfd 里面的描述符是否被激活
参数描述:参数 epfd:epoll_wait的监视列表epfd其实是一个文件描述符,该描述符所描述的文件,才是epoll_wait的监视列表所以我们要做的就是,将所有想要被epoll监视的描述符,都放到 epfd 文件中去参数 events:用来存放所有激活了的描述符的数组,是一个结构体数组,结构如下struct epoll_event {uint32_t     events;    /* 在poll_wait函数里面,这个数据表示激活的描述符是以何种形式激活的 */epoll_data_t data;      /* 该数据里面存放了具体的激活的描述符是谁 */};typedef union epoll_data {void    *ptr;int      fd;    /* 具体的激活的描述符 */uint32_t u32;uint64_t u64;} epoll_data_t;其实epoll_wait 针对当前数组只做一件事将激活的描述符到底是谁,以及该描述符是如何激活的,存放到 events这个数组里面去参数 maxevents:想要监视的描述符的数量参数 timout:设定poll_wait最大阻塞时长0:表示不阻塞-1:表示永久阻塞单位为毫秒返回值:返回激活的描述符的数量实例struct epoll_event arr[5]epoll_wait(epfd,arr) 假设此时 epfd 里面有一个 0 和 一个 server此时 epoll_wait 监视到 server激活了arr就会发生改变,改变如下arr[0].events == EPOLLINarr[0].data.fd == server

 如何创建epfd

int epoll_create(int size);创建一个只能监视size个描述符的监视列表
int epoll_create1(int flags);flags一般传 EPOLL_CLOEXEC表示创建一个动态的监视列表,该监视列表的长度会随着想要监视的描述符数量的改变而改变
所以,一般来说,我们想要创建一个监视列表,直接写int epfd = epoll_create1(EPOLL_CLOEXEC);返回值:创建出来的监视列表的描述符

 如何向epfd中存入想要监视的描述符

函数原型:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
功能描述:根据op(第2个参数)的不同,针对描述符做出不同的操作
参数描述:参数 epfd:监视列表自己的描述符参数 op:具体的操作EPOLL_CTL_ADD:将fd描述符,添加到epfd监视列表里面去EPOLL_CTL_DEL:将fd描述符,从epfd中移除EPOLL_CTL_MOD:更改fd描述的监视方式参数 event: 注意,这里是一个结构体地址,用来明确想要监视的描述符fd,以何种形式去监视struct epoll_event {uint32_t     events;    /* 在poll_ctl函数里面,用来确定描述符fd的监视方式 */EPOLLIN :监视描述符是否可读EPOLLOUT:监视描述符是否可写epoll_data_t data;     };typedef union epoll_data {void    *ptr;int      fd;    /* 要求和epoll_ctl函数里面第3个参数fd写的一模一样 */uint32_t u32;uint64_t u64;} epoll_data_t;

epoll_server 完整代码 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <sys/socket.h>typedef struct sockaddr_in addr_in_t;int main(int argc, const char *argv[]) {if (argc != 2) {printf("请输入端口号\n");return 1;}int port = atoi(argv[1]);int server = socket(AF_INET, SOCK_STREAM, 0);if (server == -1) {perror("socket");return 1;}addr_in_t addr = {0};addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = inet_addr("0.0.0.0"); // 或者使用其他有效的IP地址if (bind(server, (struct sockaddr*)&addr, sizeof(addr)) == -1) {perror("bind");return 1;}// 监听listen(server, 10);// 创建一个可动态扩容的epoll文件描述符int epfd = epoll_create1(EPOLL_CLOEXEC);if (epfd == -1) {perror("epoll_create1");return 1;}// 将server和stdin添加到epfd里面去struct epoll_event server_event, stdin_event;server_event.data.fd = server;server_event.events = EPOLLIN;stdin_event.data.fd = 0;stdin_event.events = EPOLLIN;if (epoll_ctl(epfd, EPOLL_CTL_ADD, server, &server_event) == -1) {perror("epoll_ctl for server");return 1;}if (epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &stdin_event) == -1) {perror("epoll_ctl for stdin");return 1;}int fd_count = 50; // 这个变量似乎没有被后续代码使用while (1) {struct epoll_event active_fds[50] = {0};int len = epoll_wait(epfd, active_fds, 50, -1);if (len == -1) {perror("epoll_wait");return 1;}// 经过epoll_wait说明有len个描述符激活了for (int i = 0; i < len; i++) {int sock = active_fds[i].data.fd;if (sock == server) {// 激活的是服务器printf("有新客户端连接\n");int client = accept(server, NULL, NULL);if (client == -1) {perror("accept");continue;}struct epoll_event client_event;client_event.data.fd = client;client_event.events = EPOLLIN;if (epoll_ctl(epfd, EPOLL_CTL_ADD, client, &client_event) == -1) {perror("epoll_ctl for client");close(client);continue;}} else if (sock == 0) {// 激活的是标准输入流char buf[64] = {0};scanf("%63s", buf);while (getchar() != '\n'); // 清除输入缓冲区printf("键盘输入了:%s\n", buf);} else {// 激活的不是上面两个,只能是客户端了char buf[64] = {0};int res = read(sock, buf, 64);if (res == 0) {// 客户端断开连接printf("客户端断开连接\n");epoll_ctl(epfd, EPOLL_CTL_DEL, sock, NULL);close(sock);} else {printf("客户端发来消息:%s\n", buf);}}}}return 0;
}

 3.客户端测试代码

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>typedef struct sockaddr_in addr_in_t;
typedef struct sockaddr addr_t;
typedef struct sockaddr_un addr_un_t;enum Type{TYPE_REGIST,TYPE_LOGIN
};typedef struct Pack{enum Type type;char name[20];char pswd[20];
}pack_t;int main(int argc, const char *argv[])
{if(argc!=2){printf("输入端口号\n");return  1;}int port =atoi(argv[1]);//创建服务器套接字int client=socket(AF_INET,SOCK_STREAM,0);addr_in_t addr ={0};addr.sin_family=AF_INET;addr.sin_port=htons(port);addr.sin_addr.s_addr=inet_addr("192.168.126.181");
//连接服务器if (connect(client,(addr_t*)&addr,sizeof(addr))==-1){perror("connect");return 1;	}//while(1){char buf[64]="";printf("请输入");scanf("%63s",buf);while(getchar()!=10);write(client,buf,64);}return 0;
}


http://www.mrgr.cn/news/90125.html

相关文章:

  • java-list深入理解(流程图)
  • 【嵌入式 Linux 音视频+ AI 实战项目】瑞芯微 Rockchip 系列 RK3588-基于深度学习的人脸门禁+ IPC 智能安防监控系统
  • c#中Thread.Join()方法的经典示例
  • 初次体验Tauri和Sycamore (2)
  • SOA(面向服务架构)全面解析
  • 软件测评实验室CNAS认证能力验证什么时机做?如何查询能力验证相关信息?
  • 大语言模型RAG,transformer和mamba
  • 使用LLaMA Factory踩坑记录
  • SQL自学,mysql从入门到精通 --- 第 7 天,表的联合
  • 机器学习 - 线性回归(最大后验估计)
  • WEB小项目——鼠标划入丝滑显示下划线
  • Wpf美化按钮,输入框,下拉框,dataGrid
  • SpringBoot3 + Jedis5 + Redis集群 如何通过scan方法分页获取所有keys
  • zy.21
  • DeepSeek从入门到精通:全面掌握AI大模型的核心能力
  • GPU — 8 卡 GPU 服务器与 NVLink/NVSwitch 互联技术
  • 攻防世界32 very_easy_sql【SSRF/SQL时间盲注】
  • 【MQ】Spring3 中 RabbitMQ 的使用与常见场景
  • 【MQ】RabbitMQ 高可用延时功能的探究
  • 0 Rust与Qt集成实践指南(CXX-Qt)
  • 使用Redis实现业务信息缓存(缓存详解,缓存更新策略,缓存三大问题)
  • 【学Rust写CAD】5 三维转换矩阵解析及应用示例
  • MySQL数据库 - 阶段性体系总结
  • SQL自学,mysql从入门到精通 --- 第 1 天,系统环境搭建,mysql部署
  • 9.JVM-方法区
  • Java/Kotlin 使用 Chrome 无头浏览器