Что может привести к блокировке неблокирующего сокета в `recv`? - PullRequest
0 голосов
/ 21 февраля 2020

У меня установлен сокет TCP / IP для неблокирования, который блокирует в любом случае. На сокет ссылаются только в одном потоке. Этот код работает на Windows (с несколькими заменами вызовов), но не на Linux. У меня есть код, который выглядит следующим образом (Не обращайте внимания на приведение в стиле C - это было написано долго go. Кроме того, я немного урезал его, поэтому дайте мне знать, если я случайно урезал шаг Скорее всего, я действительно делаю этот шаг. Фактический код находится на другом компьютере, поэтому я не могу скопировать и вставить.):

// In the real code, these are class members. I'm not bonkers
int mSocket;
sockaddr_in mAddress;

void CreateSocket(
    unsigned int ipAddress,
    unsigned short port)
{        
    // Omitting my error checking in this question for brevity because everything comes back valid
    mSocket = socket(AF_INET, SOCK_STREAM, 0);  // Not -1

    int oldFlags = fctnl(mSocket, F_GETFL, 0);  // Not -1
    fcntl(mSocket, F_SETFL, oldFlags | O_NONBLOCK);  // Not -1

    mAddress.sin_family = AF_INET;
    mAddress.sin_addr.s_addr = ipAddress;  // address is valid
    mAddress.sin_port = htons((u_short)port);  // port is not 0 and allowed on firewall
    memset(mAddress.sin_zero, 0, sizeof(mAddress.sin_zero));

    // <Connect attempt loop starts here>
    connect(mSocket, (sockaddr*)&mAddress, sizeof(mAddress));  // Not -1 to exit loop
    // <Connect attempt loop ends here>
    // Connection is now successful ('connect' returned a value other than -1)
}

// ... Stuff happens ...

// ... Then this is called because 'select' call shows read data available ...
void AttemptReceive(
    MyReturnBufferTypeThatsNotImportant &returnedBytes)
{
    // Read socket
    const size_t bufferSize = 4096;
    char buffer[bufferSize];
    int result = 0;

    do {
        // Debugging code: sanity checks
        int socketFlags = fcntl(mSocket, F_GETFL, 0);  // Not -1
        printf("result=%d\n", result);
        printf("O_NONBLOCK? %d\n", socketFlags & O_NONBLOCK);  // Always prints "O_NONBLOCK? 2048"

        result = recv(mSocket, buffer, bufferSize, 0);  // NEVER -1 or 0 after hundreds to thousands of calls, then suddenly blocks

        // ... Save off and package read data into user format for output to caller ...
    } while (result == bufferSize);
}

Я считаю, потому что AttemptReceive вызывается в ответ на выберите, чтобы в сокете было ровно столько байтов, сколько кратно размеру буфера (4096). Я несколько подтвердил это с помощью операторов printf, поэтому он никогда не блокируется при первом l oop проходе. Каждый раз, когда возникает эта ошибка, последние две строки, которые должны быть напечатаны перед блоками потоков:

result=4096
O_NONBLOCK? 2048

Изменение строки recv на recv(mSocket, buffer, bufferSize, MSG_DONTWAIT); фактически «исправляет» проблему (внезапно recv иногда возвращает -1 с errno EWOULDBLOCK / EAGAIN (оба равны друг другу в моей ОС)), но я боюсь, что просто накладываю пластырь на так называемую рану. Любые идеи?

PS адрес "localhost", но я не думаю, что это имеет значение.

Примечание: я использую старый компилятор (не по выбору), g ++ 4.4. 7-23 из 2010. Это может иметь какое-то отношение к проблеме.

1 Ответ

0 голосов
/ 25 февраля 2020

socket() автоматически устанавливает O_RDWR на сокете с моей операционной системой и компилятором, но похоже, что O_RDWR случайно был сброшен на соответствующем сокете при запуске программы (что каким-то образом позволило ему прочитать хорошо, если есть данные для чтения, но блокировать в противном случае). Исправление этой ошибки привело к тому, что сокет прекратил блокировку. Очевидно, что O_RDWR и O_NONBLOCK необходимы, чтобы избежать блокировки сокетов, по крайней мере, в моей операционной системе и компиляторе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...