blj28

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

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);

实际应用中可能需要根据具体需求进行相应的错误处理和功能扩展。

 
 
 
 
 

posted on   bailinjun  阅读(101)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示