C++ 从多个文件描述符异步接收UDP数据包

C++ 从多个文件描述符异步接收UDP数据包,c++,c,linux,sockets,asynchronous,C++,C,Linux,Sockets,Asynchronous,我对使用fcntl和sigaction异步接收UDP数据包有一些疑问。在我的程序中,我有两个UDP通信源,我想监视它们。我为流量设置了两个套接字,并使用教程设置了文件描述符,以便在收到UDP数据包时触发sigaction 这只适用于一个源,但当我添加另一个源时,每当任一文件描述符接收到数据包时,它将只触发其中一个处理程序 下面是一个演示该行为的简短程序: #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include <stdio.h>

我对使用fcntl和sigaction异步接收UDP数据包有一些疑问。在我的程序中,我有两个UDP通信源,我想监视它们。我为流量设置了两个套接字,并使用教程设置了文件描述符,以便在收到UDP数据包时触发sigaction

这只适用于一个源,但当我添加另一个源时,每当任一文件描述符接收到数据包时,它将只触发其中一个处理程序

下面是一个演示该行为的简短程序:

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int done;
int a_fd;
int b_fd;

int recv_dgram(int fd, char* dgram, int size)
{
    struct sockaddr_in addr;
    int fromlen = sizeof(addr);
    return recvfrom(fd, dgram, size, 0, (struct sockaddr*)&addr, (socklen_t*)&fromlen);
}

void a_handler(int signum)
{
    char dgram[256];
    int size = recv_dgram(a_fd, dgram, 256);
    printf("a recieve size: %d\n", size);
}

void b_handler(int signum)
{
    char dgram[256];
    int size = recv_dgram(b_fd, dgram, 256);
    printf("b recieve size: %d\n", size);
}

void sig_handle(int signum)
{
    done = 1;
}

int init_fd(int port, const char* group, const char* interface)
{
    int fd = socket(AF_INET, SOCK_DGRAM, 0);
    if(fd < 0) {
        return -1;
    }

    int reuse = 1;
    if(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char*)&reuse, sizeof(reuse)) < 0) {
        close(fd);
        return -1;
    }

    if(fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
        close(fd);
        return -1;
    }

    struct sockaddr_in addr;
    memset((char*)&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;
    if(bind(fd, (struct sockaddr*)&addr, sizeof(addr))) {
        close(fd);
        return -1;
    }

    struct ip_mreq mcast_group;
    mcast_group.imr_multiaddr.s_addr = inet_addr(group);
    mcast_group.imr_interface.s_addr = inet_addr(interface);
    if(setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&mcast_group, sizeof(mcast_group))) {
        close(fd);
        return -1;
    }

    return fd;
}

int main(int argc, const char* argv[])
{
    done = 0;
    signal(SIGINT, sig_handle);
    signal(SIGTERM, sig_handle);

    // make sockets and sigactions
    a_fd = init_fd([a port], [a multicast group], [a interface]);
    if(a_fd < 0) {
        return -1;
    }

    pid_t pid = getpid();

    int a_flags = fcntl(a_fd, F_GETFL);
    fcntl(a_fd, F_SETFL, a_flags | O_ASYNC);

    struct sigaction a_sa;
    a_sa.sa_flags = 0;
    a_sa.sa_handler = a_handler;
    sigemptyset(&a_sa.sa_mask);

    sigaction(SIGIO, &a_sa, NULL);
    fcntl(a_fd, F_SETOWN, pid);
    fcntl(a_fd, F_SETSIG, SIGIO);

    b_fd = init_fd([b port], [b multicast group], [b interface]);
    if(b_fd < 0) {
        close(a_fd);
        return -1;
    }

    int b_flags = fcntl(b_fd, F_GETFL);
    fcntl(b_fd, F_SETFL, b_flags | O_ASYNC);

    struct sigaction b_sa;
    b_sa.sa_flags = 0;
    b_sa.sa_handler = b_handler;
    sigemptyset(&b_sa.sa_mask);

    sigaction(SIGIO, &b_sa, NULL);
    fcntl(b_fd, F_SETOWN, pid);
    fcntl(b_fd, F_SETSIG, SIGIO);

    printf("start\n");
    while(!done) { pause(); }
    printf("done\n");

    close(a_fd);
    close(b_fd);
    return 0;
}
\ifndef\u GNU\u源代码
#定义GNU源
#恩迪夫
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
#包括
int完成;
INTA_fd;
int b_fd;
int recv_dgram(int fd,char*dgram,int size)
{
地址中的结构sockaddr\u;
int fromlen=sizeof(addr);
返回recvfrom(fd,dgram,size,0,(struct sockaddr*)和addr,(socklen_t*)和fromlen);
}
无效a_处理程序(int signum)
{
char-dgram[256];
int size=recv_dgram(a_fd,dgram,256);
printf(“接收大小:%d\n”,大小);
}
无效b_处理程序(整数符号)
{
char-dgram[256];
int size=recv_dgram(b_fd,dgram,256);
printf(“b接收大小:%d\n”,大小);
}
无效信号手柄(内部信号)
{
完成=1;
}
int init_fd(int端口,常量字符*组,常量字符*接口)
{
int fd=插座(AF INET,SOCK DGRAM,0);
如果(fd<0){
返回-1;
}
int=1;
if(setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char*)和reuse,sizeof(reuse))<0){
关闭(fd);
返回-1;
}
如果(fcntl(fd,F_设置FL,O_非块)<0){
关闭(fd);
返回-1;
}
地址中的结构sockaddr\u;
memset((char*)&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
地址SINU port=htons(端口);
addr.sin\u addr.s\u addr=INADDR\u ANY;
if(bind(fd,(struct sockaddr*)&addr,sizeof(addr))){
关闭(fd);
返回-1;
}
结构ip_mreq mcast_组;
mcast_group.imr_multiaddr.s_addr=inet_addr(组);
mcast_group.imr_interface.s_addr=inet_addr(接口);
if(setsockopt(fd、IPPROTO\u IP、IP\u ADD\u MEMBERSHIP,(char*)和mcast\u组、sizeof(mcast\u组))){
关闭(fd);
返回-1;
}
返回fd;
}
int main(int argc,const char*argv[]
{
完成=0;
信号(SIGINT、sig_手柄);
信号(SIGTERM、sig_手柄);
//进行套接字和sig操作
a_fd=init_fd([端口],[多播组],[接口]);
如果(a_fd<0){
返回-1;
}
pid_t pid=getpid();
int a_flags=fcntl(a_fd,F_GETFL);
fcntl(a|U fd、F|U SETFL、a|U标志| O|U异步);
结构sigaction a_sa;
a_sa.sa_flags=0;
a_sa.sa_handler=a_handler;
sigemptyset(&a_sa.sa_mask);
sigaction(SIGIO和a_sa,空);
fcntl(a_fd、F_SETOWN、pid);
fcntl(a_fd、F_SETSIG、SIGIO);
b_fd=init_fd([b端口],[b多播组],[b接口]);
如果(b_fd<0){
关闭(a_fd);
返回-1;
}
int b_flags=fcntl(b_fd,F_GETFL);
fcntl(b|u fd、F|u SETFL、b|u标志| O|u ASYNC);
结构sigaction b_sa;
b_sa.sa_标志=0;
b_sa.sa_handler=b_handler;
sigemptyset(&b_sa.sa_mask);
sigaction(SIGIO和b_sa,NULL);
fcntl(b_fd、F_设置、pid);
fcntl(b_fd、F_SETSIG、SIGIO);
printf(“开始\n”);
而(!done){pause();}
printf(“完成”\n);
关闭(a_fd);
关闭(b_fd);
返回0;
}
我可以使用(您也可以使用gcc)编译此文件:

g++-c test.cpp

g++-o测试test.o

我在Ubuntu 12.04 LTS上使用g++4.6.3

当我使用两个UDP数据源运行这个程序时,当任一文件描述符有可用数据包时,就会触发b_处理程序。因此,每当一个_处理程序收到一个数据包时,它都会打印“b received size:-1”。_处理程序永远不会被调用

我怀疑这是因为getpid()将为它们返回相同的值,因此其中一个sigaction处理程序将被覆盖

有没有办法让这两个处理程序独立触发


感谢您的帮助。

使用两种不同的信号,例如SIGIO和SIGUSR1

fcntl(descriptor, SETSIG, signal_desired);

为什么不能使用选择/投票和好友?这是多路复用多个输入的正常方式。在信号处理器中,你可以安全地做很少的事情。这应该是我实现这一点的方式,我将来也会这样做。我这样做主要是为了尝试一些新的东西,我很好奇是否有办法绕过这个陷阱。