UDP使用Epoll 实现
#include <sys/socket.h> #include <sys/epoll.h> #include <netinet/in.h> #include <arpa/inet.h> #include <fcntl.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <string.h> #include <strings.h> #define MAXLINE 10 #define OPEN_MAX 100 #define LISTENQ 20 #define SERV_PORT 5566 #define INFTIM 1000 #define MAX_EVENT 1000 void setnonblocking(int sock) { int opts; opts=fcntl(sock,F_GETFL); if(opts<0) { perror("fcntl(sock,GETFL)"); exit(1); } opts = opts|O_NONBLOCK; if(fcntl(sock,F_SETFL,opts)<0) { perror("fcntl(sock,SETFL,opts)"); exit(1); } } int main() { int i, maxi, listenfd, connfd, sockfd,epfd,nfds; int ret; ssize_t n; char line[MAXLINE]; socklen_t clilen; char szAddr[256]="\0"; //声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件 struct epoll_event ev,events[20]; //生成用epoll专用文件描述符 epfd=epoll_create(256);printf("epoll_create(256) return %d\n", epfd); struct sockaddr_in clientaddr; struct sockaddr_in serveraddr; listenfd = socket(AF_INET, SOCK_DGRAM, 0); socklen_t addrlen = sizeof(clientaddr); //把socket设置为非阻塞方式 setnonblocking(listenfd);
//fcntl(listenfd, F_SETFL, fcntl(gUdpRecvFB[i].recv_socket, F_GETFD, 0) | O_NONBLOCK); //设置与要处理的事件相关的文件描述符 ev.data.fd=listenfd; //设置要处理的事件类型 ev.events=EPOLLIN|EPOLLET; //注册epoll事件 ret=epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); printf("epoll_ctl return %d\n",ret); bzero(&serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; char *local_addr="127.0.0.1"; inet_aton(local_addr,&(serveraddr.sin_addr)); serveraddr.sin_addr.s_addr=inet_addr(local_addr); serveraddr.sin_port=htons(SERV_PORT); bind(listenfd,(struct sockaddr *)&serveraddr, sizeof(serveraddr)); memset(line,0,MAXLINE); for ( ; ; ) { //等待epoll事件的发生 //nfds=epoll_wait(epfd,events,MAX_EVENT,-1); nfds=epoll_wait(epfd,events,MAX_EVENT,0); //处理所发生的所有事件 for(i=0;i<nfds;++i) { if(events[i].events&EPOLLIN) { if ( (sockfd = events[i].data.fd) < 0) continue; if ((n = recvfrom(sockfd, line, MAXLINE,0,(sockaddr*)&clientaddr,&addrlen)) < 0) { if (errno == ECONNRESET) { close(sockfd); events[i].data.fd = -1; } else printf("readline error\n"); } else if (n == 0) { perror("connfd=0\n"); close(sockfd); events[i].data.fd = -1; } char* p = (char *)&clientaddr.sin_addr; sprintf(szAddr, "%d.%d.%d.%d", *p, *(p+1), *(p+2), *(p+3)); printf("recv %s from ip:%s\n",line,szAddr); //设置用于写操作的文件描述符 ev.data.fd=sockfd; //设置用于注测的写操作事件 ev.events=EPOLLOUT|EPOLLET; //修改sockfd上要处理的事件为EPOLLOUT epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); } else if(events[i].events&EPOLLOUT) { if(events[i].data.fd == -1) continue; sockfd = events[i].data.fd; write(sockfd, line, n); printf("send %s\n",line); //设置用于读操作的文件描述符 ev.data.fd=sockfd; //设置用于注测的读操作事件 ev.events=EPOLLIN|EPOLLET; char buf[256]="test"; sendto(sockfd,buf,strlen(buf),0,(sockaddr*)&clientaddr,addrlen); //修改sockfd上要处理的事件为EPOLIN epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); } } } }
在Linux系统中,epoll
是一种高效的I/O多路复用技术,它允许程序同时监控多个文件描述符(如socket),等待其中的一个或多个变为可读、可写或有异常事件发生,而无需为每个描述符设置单独的阻塞调用。这对于需要处理大量并发连接的应用,特别是高性能的网络服务,如使用UDP协议的应用,非常有用。下面是一个简化的示例,
步骤 1: 包含必要的头文件和初始化
首先,确保你的程序包含了必要的头文件,并初始化了相关变量。
1#include <sys/epoll.h>
2#include <sys/socket.h>
3#include <netinet/in.h>
4#include <arpa/inet.h>
5#include <unistd.h>
6#include <fcntl.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <errno.h>
10#include <string.h>
11
12#define MAX_EVENTS 10
13#define BUFFER_SIZE 1024
14
15int main() {
16 int epoll_fd, udp_sockfd;
17 struct sockaddr_in server_addr, client_addr;
18 socklen_t client_len;
19 char buffer[BUFFER_SIZE];
20 struct epoll_event events[MAX_EVENTS];
21 int nfds;
22
23 // 创建UDP socket
24 udp_sockfd = socket(AF_INET, SOCK_DGRAM, 0);
25 if (udp_sockfd == -1) {
26 perror("Socket creation failed");
27 exit(EXIT_FAILURE);
28 }
29
30 // 配置服务器地址
31 memset(&server_addr, 0, sizeof(server_addr));
32 server_addr.sin_family = AF_INET;
33 server_addr.sin_port = htons(12345); // 示例端口
34 server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
35
36 // 绑定socket到地址
37 if (bind(udp_sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1) {
38 perror("Bind failed");
39 close(udp_sockfd);
40 exit(EXIT_FAILURE);
41 }
42
43 // 设置为非阻塞模式
44 if (fcntl(udp_sockfd, F_SETFL, O_NONBLOCK) == -1) {
45 perror("Failed to set non-blocking");
46 close(udp_sockfd);
47 exit(EXIT_FAILURE);
48 }
49
50 // 创建epoll实例
51 epoll_fd = epoll_create1(0);
52 if (epoll_fd == -1) {
53 perror("Epoll create failed");
54 close(udp_sockfd);
55 exit(EXIT_FAILURE);
56 }
57
58 // 添加socket到epoll监控列表
59 struct epoll_event event;
60 event.events = EPOLLIN; // 监听读事件
61 event.data.fd = udp_sockfd;
62 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, udp_sockfd, &event) == -1) {
63 perror("Epoll_ctl add failed");
64 close(udp_sockfd);
65 close(epoll_fd);
66 exit(EXIT_FAILURE);
67 }
68}
步骤 2: 事件循环
接下来,在事件循环中处理epoll
返回的事件。
1while (1) {
2 nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
3 if (nfds == -1) {
4 perror("Epoll wait failed");
5 break;
6 }
7
8 for (int i = 0; i < nfds; ++i) {
9 if (events[i].data.fd == udp_sockfd) {
10 // UDP socket有数据可读
11 client_len = sizeof(client_addr);
12 ssize_t n = recvfrom(udp_sockfd, buffer, BUFFER_SIZE, 0, (struct sockaddr*)&client_addr, &client_len);
13 if (n > 0) {
14 printf("Received packet from %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
15 printf("Data: %.*s\n", (int)n, buffer);
16
17 // 处理接收到的数据,这里简单地将数据回显给客户端
18 sendto(udp_sockfd, buffer, n, 0, (struct sockaddr*)&client_addr, client_len);
19 } else if (n == -1 && errno != EAGAIN && errno != EWOULDBLOCK) {
20 perror("recvfrom error");
21 }
22 }
23 }
24}
步骤 3: 清理资源
在程序结束时,记得清理打开的文件描述符和关闭socket。
1close(udp_sockfd);
2close(epoll_fd);
实际应用中可能需要根据具体需求进行相应的错误处理和功能扩展。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!