тайм-аут для блокировки сокета TCP не работает

Обратите внимание: используется платформа Windows, а не Linux.

У меня есть блокирующий клиентский сокет TCP. После подключения к удаленному серверу я устанавливаю тайм-аут чтения (так как удаленный сервер нестабилен, состояние сети плохое) и затем получаю данные.

Иногда функция recv() никогда не возвращается, и моя программа мертва.

Код выглядит следующим образом:

// set timeout
{
    int millisec = 1000;
    if(setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, (char*)&millisec, sizeof(int))) {
        MessageBox(0, "setsockopt fail", "", 0);
    }
}

unsigned begin_t = time(0);
int r = recv(sock_, ptr, static_cast<int>(size), 0);
unsigned end_t = time(0);

if(end_t - begin_t > 2) {
    MessageBox(0, "over 2 sec", "", 0); // This MessageBox popups some time
}

Я установил тайм-аут сокета на 1 секунду прямо перед функцией recv(). Теоретически recv() никогда не займет больше 1 секунды. Но иногда это все равно занимает более 3 секунд, после чего появляется MessageBox.

Почему таймаут иногда не работает?


person aj3423    schedule 15.05.2019    source источник
comment
time() имеет точность в секундах, поэтому он не будет очень точным для кода синхронизации. Вместо этого используйте GetTickCount/64() или QueryPerformanceCounter().   -  person Remy Lebeau    schedule 15.05.2019
comment
Может быть, мне не нужно считать затраты времени, потому что иногда они никогда не возвращаются.   -  person aj3423    schedule 15.05.2019
comment
Просто невозможно, чтобы recv() мог заблокироваться навсегда, когда действует небесконечный тайм-аут SO_RCVTIMEO. Значит, происходит что-то еще. Кроме того, просто к вашему сведению, еще один способ реализовать тайм-аут чтения — вызвать select() перед вызовом recv().   -  person Remy Lebeau    schedule 15.05.2019
comment
Есть ли у сокета атрибут OVERLAPPED?   -  person Drake Wu    schedule 16.05.2019


Ответы (1)


SO_RCVTIMEO не поддерживается в блокирующий сокет.

Если блокирующий входящий вызов истекает, соединение находится в неопределенном состоянии и должно быть закрыто. Если сокет создается с помощью функции WSASocket, то параметр dwFlags должен иметь установленный атрибут WSA_FLAG_OVERLAPPED, чтобы тайм-аут работал правильно. В противном случае тайм-аут никогда не вступит в силу.

Использование WSASocket с WSA_FLAG_OVERLAPPED. Или socket() (по умолчанию для режима WSA_FLAG_OVERLAPPED)

person Drake Wu    schedule 16.05.2019
comment
Ключ точно WSA_FLAG_OVERLAPPED, мой код вызывает setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE,..., WSA_FLAG_OVERLAPPED отключен, если установлен SO_OPENTYPE. Подробности можно найти на ней: https://github.com/yhirose/cpp-httplib/issues/150 - person aj3423; 17.05.2019