цикл выбора сокетов linux C ++

0

У меня небольшие проблемы с сокетами, когда я не получаю данные, кроме первого цикла, он время от времени отключается. Если я закрываю и снова открываю сокет каждый цикл, хотя я, кажется, правильно получаю данные. Есть идеи о том, почему?

Пример цикла без закрытия:

int socketHandle = socket(AF_INET,SOCK_DGRAM,0);

sockaddr_in serverAddr;

serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr(/*UDP IP ADDRESS*/);
serverAddr.sin_port = htons(/*UDP PORT*/);

struct timeval tv;
fd_set rfds;

FD_ZERO(&rfds);
FD_SET(socketHandle, &rfds);

tv.tv_usec = 0.0;
int recVal = 0;
int sockLen = sizeof(serverAddr);
bind(socketHandle, (struct sockaddr*)&serverAddr, (socklen_t)sockLen);

bool timePassed = false;
time_t startListenTime = time(NULL);

tv.tv_sec = maxUpdateTime;

while(true)
{
    recVal = select(socketHandle + 1, &rfds, NULL, NULL, &tv);
    switch(recVal)
    {
        case(0):
        {
            //Timeout
            break;
        }
        case(-1):
        {
            //Error
             break;
        }
        default:
        {
            /*Packet Data Type*/ pkt;
            if(recvfrom(socketHandle, &pkt, sizeof(/*Packet Data Type*/), 0, (sockaddr*)&serverAddr, (socklen_t*)&sockLen) < 0)
            {
                //Failed to Recieve Data
                break;
            }
            else
            {
                //Recieved Data!!
            }
            break;
        }
    }
}

Пример цикла с закрытием:

while(true)
{
    int socketHandle = socket(AF_INET,SOCK_DGRAM,0);

    sockaddr_in serverAddr;

    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr(/*UDP IP ADDRESS*/);
    serverAddr.sin_port = htons(/*UDP PORT*/);

    struct timeval tv;
    fd_set rfds;

    FD_ZERO(&rfds);
    FD_SET(socketHandle, &rfds);

    tv.tv_usec = 0.0;
    int recVal = 0;
    int sockLen = sizeof(serverAddr);
    bind(socketHandle, (struct sockaddr*)&serverAddr, (socklen_t)sockLen);


    bool timePassed = false;
    time_t startListenTime = time(NULL);

    tv.tv_sec = maxUpdateTime;

    recVal = select(socketHandle + 1, &rfds, NULL, NULL, &tv);
    switch(recVal)
    {
        case(0):
        {
            //Timeout
            break;
        }
        case(-1):
        {
            //Error
             break;
        }
        default:
        {
            /*Packet Datastructure*/ pkt;
            if(recvfrom(socketHandle, &pkt, sizeof(/*Packet Datastructure*/), 0, (sockaddr*)&serverAddr, (socklen_t*)&sockLen) < 0)
            {
                //Failed to read packet
                break;
            }
            else
            {
                //Read Packet!!
            }
            break;
        }
    }
    close(socketHandle);
}
  • 2
    переместите FD_ZERO () и FD_SET () внутри цикла вправо относительно select ().
  • 1
    См. Этот mkssoftware.com/docs/man3/select.3.asp о том, что дескрипторы изменены, чтобы показать, какие дескрипторы готовы для ввода / вывода.
Показать ещё 2 комментария
Теги:
select
sockets

2 ответа

2
Лучший ответ

Функция select() использует указанную маску дескриптора файла, чтобы определить, какие файловые дескрипторы отслеживать событие (чтение, запись и т.д.). Когда дескриптор файла доступен для операции ввода-вывода (чтение, запись), функция select() изменяет дескрипторы, чтобы указать, какие из файлов готовы для данного действия ввода-вывода.

См. Эту статью о функции выбора и макросах/функциях, используемых с файловыми дескрипторами.

Старые программы типа Unix часто обрабатывали дескриптор файла как битовую маску и просто проверяли биты. Однако фактическая реализация файлового дескриптора может варьироваться в зависимости от компилятора, поэтому лучше всего использовать стандартные макросы/функции дескриптора файла для установки, очистки и тестирования различных файловых дескрипторов.

Поэтому при использовании функции select() вам необходимо использовать FD_ZERO() и FD_SET() чтобы вы установили конкретные дескрипторы файлов, которые вы хотите для этого конкретного вызова функции select(). Когда select() вернется, она укажет, какие из дескрипторов файлов, которые были назначены, действительно готовы к использованию для операций ввода-вывода (чтение, запись и т.д.).

Таким образом, ваш код будет выглядеть примерно так:

while(true)
{
    fd_set rfds;

    FD_ZERO(&rfds);
    FD_SET(socketHandle, &rfds);
    recVal = select(socketHandle + 1, &rfds, NULL, NULL, &tv);
    switch(recVal)
    {
        case(0):
        {
            //Timeout
            break;
        }
        case(-1):
        {
            //Error
             break;
        }
        default:
        {
            /*Packet Data Type*/ pkt;
            if(recvfrom(socketHandle, &pkt, sizeof(/*Packet Data Type*/), 0, (sockaddr*)&serverAddr, (socklen_t*)&sockLen) < 0)
            {
                //Failed to Recieve Data
                break;
            }
            else
            {
                //Recieved Data!!
            }
            break;
        }
    }

Однако вам действительно нужно использовать FD_ISSET() чтобы проверить, какие конкретные дескрипторы файлов готовы к использованию. В вашем случае у вас есть только одна, но в ситуации, когда имеется несколько дескрипторов, которые вы хотели бы использовать также FD_ISSET().

  • 0
    Ага! Большое спасибо за быстрый ответ и объяснение.
1

Когда select возвращает что-либо, кроме -1 считается успешным, а fd -1 изменяются, чтобы сказать, какие из них готовы или имеют ошибку.

Из спецификации POSIX:

После успешного завершения функция pselect() или select() должна модифицировать объекты, на которые указывают аргументы readfds, writefds и errorfds, чтобы указать, какие дескрипторы файла готовы для чтения, готовы для записи или имеют ожидающее условие ошибки, соответственно, и возвращает общее количество готовых дескрипторов во всех выходных наборах.

Если происходит тайм-аут, тогда он возвращает ноль, потому что ни один из дескрипторов не был готов, и никакие биты не будут установлены в наборах fd.

Поэтому, когда время разговора не установлено, в rfds нет битов, и так далее в следующем цикле при вызове select вы просите его ждать пустого набора, который никогда не вернет положительное значение, потому что если вы ждете нулевых FD, тогда вы никогда не получите ненулевое количество готовых FD!

Вы должны помнить, что rfds является как входным параметром, так и выходным параметром, поэтому убедитесь, что он настроен правильно перед каждым вызовом для select.

  • 0
    Хороший момент об истечении времени ожидания вернет дескрипторы файлов.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню