Epoll 事件(EPOLLLT)仅在 udp 套接字上触发一次

Epoll events(EPOLLLT) only triggered once on udp socket

从网上说,如果epoll使用默认模式(level trigger)监听文件描述符,当fd(文件描述符)准备好读取并且fd关联的buffer数据没有被完全消耗时,epoll会继续触发,直到所有数据都被消耗掉,但是,当我使用 epoll(LT 模式)侦听 udp 套接字进行测试时,当多个字符出现时,epoll 只触发一次。 过程如下:

第一步:创建epoll,udp socket fd,然后让epoll监听socket写事件。

第2步:发送多个字符("abc")到udp套接字

第三步:每次触发epoll,从udp socket中读取1个字符。

本以为udp socket接收3个字符时epoll触发3次,结果epoll只触发一次。 这是我的代码:

#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define BUFFER_SIZE 512
#define log(fmt, arg...) printf(""fmt, ##arg)

void main(){
    int fd1,efd, fds, i, fd;
    int ret, addr_len;
    struct epoll_event g_event;             
    struct epoll_event *epoll_events_ptr; 
    char buffer[BUFFER_SIZE] = {0};
    struct sockaddr_in addr1;

    fd1 = socket(AF_INET, SOCK_DGRAM, 0);   
    if (fd1 == -1) {
        log("create socket fail \r\n");
        return ;
    }     

    addr1.sin_family = AF_INET;             
    addr1.sin_addr.s_addr = INADDR_ANY; 
    addr1.sin_port = htons(3500);    
    addr_len = sizeof(struct sockaddr_in);

    if (0 != bind(fd1, (struct sockaddr *)&addr1, sizeof(struct sockaddr_in))) { 
        log("bind local listening addr fail,errno : %d \r\n", errno);
        goto err;
    }


    efd = epoll_create1(0);                 
    if (efd == -1) {
        log("create epoll fail \r\n");
        goto err;
    }
    log("create epoll instance success \r\n");

    epoll_events_ptr = (struct epoll_event *)calloc(2, sizeof(struct epoll_event));
    if (epoll_events_ptr == NULL) {
        log("calloc fail \r\n");
        goto err;
    }

    g_event.data.fd = fd1; 
    g_event.events = EPOLLIN;   
    epoll_ctl(efd, EPOLL_CTL_ADD, fd1, &g_event);          

    while(1) {
        fds = epoll_wait(efd, epoll_events_ptr, 2, -1); 
        for (i = 0; i<fds; i++)
        {    
            if (epoll_events_ptr[i].events & EPOLLIN)
            {   
                ret = recv(fd1, buffer, 1, MSG_DONTWAIT);
                if(ret != -1)
                log("recv msg : %s \n", buffer);
            }
            memset(buffer, 0, BUFFER_SIZE);
        }        
    }   

err:
    close(fd1);
    if(epoll_events_ptr) 
        free(epoll_events_ptr);

    return ;
}

enter image description here

您将 UDP 视为一种流式传输协议,即 TCP。它不是。它是一个数据报协议。如果您将 UDP 数据报读入太小而无法接收的缓冲区,则数据报的剩余部分将被 丢弃 。下次不会留在缓冲区中。

因此,一次读取一个字符在 UDP 中毫无意义,更不用说在任何协议中都极其低效了。

注意你不需要 memset(),而且这个:

log("recv msg : %s \n", buffer);

无效。应该是:

log("recv msg : %.*s \n", ret, buffer);

您不能假定接收到的数据以 null 结尾。