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

时间:2018-06-20 00:41:57

标签: sockets udp epoll

他们从在线资源中得知,如果epoll使用默认模式(级别触发器)监听文件描述符,则当fd(文件描述符)准备好读取且与fd相关联的缓冲区数据未完全使用时,epoll将继续触发直到所有数据被消耗,但是,当我在udp套接字上使用epoll(LT模式)进行测试时,当出现多个字符时,epoll仅触发一次。 流程如下:

步骤1:创建epoll,udp套接字fd,然后使epoll监听套接字上的写事件。

步骤2:将多个字符(“ abc”)发送到udp套接字

第3步:每次epoll触发,然后从udp套接字读取1个字符。

我期望epoll触发3次,因为udp套接字接收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

1 个答案:

答案 0 :(得分:0)

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

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

NB您不需要memset(),并且这样:

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

无效。应该是:

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

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