2014-10-21 2 views
0

У меня возникли вопросы об использовании fcntl и sigaction для получения пакета UDP асинхронно. В моей программе у меня есть два источника трафика UDP, которые я хотел бы отслеживать. Я установил два сокета для трафика и использовал учебник this, чтобы установить дескриптор файла для запуска sigaction всякий раз, когда я получаю UDP-пакет.Получение пакетов UDP асинхронно из нескольких дескрипторов файлов

Это нормально работает только с одним источником, но когда я добавляю другой источник, он запускает только один из обработчиков всякий раз, когда дескриптор файла получает пакет.

Вот короткая программа демонстрирует поведение:

#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; 
} 

я могу скомпилировать с (вы можете скомпилировать с помощью GCC тоже):

г ++ -c test.cpp

г ++ - o test test.o

Я использую g ++ 4.6.3 на Ubuntu 12.04 LTS.

Когда я запускаю эту программу с двумя источниками данных UDP, b_handler запускается, когда в дескрипторах файлов имеется доступный пакет. Поэтому он будет печатать «b полученный размер: -1» всякий раз, когда a_handler должен получить пакет. a_handler никогда не вызван.

Я подозреваю, что это связано с тем, что getpid() вернет одинаковое значение для обоих из них, поэтому один из обработчиков sigaction будет перезаписан.

Есть ли способ, чтобы эти два обработчика запускались независимо друг от друга?

Спасибо за помощь.

+2

Почему вы не можете использовать выбор/опрос и друзей? Это нормальный способ мультиплексирования нескольких входов. Вы можете сделать очень мало в обработчике сигналов. – Max

+0

Это должно быть так, как я это делаю, и буду делать это в будущем. Я в основном делал это так, чтобы попробовать что-то новое, и мне было любопытно, есть ли способ преодолеть эту ошибку. –

ответ

1

Используйте два разных сигнала, скажем, SIGIO и SIGUSR1.

fcntl(descriptor, SETSIG, signal_desired); 
+0

Спасибо, я думаю, что работал, я не знал об сигналах USR. –

Смежные вопросы