2009-12-01 23 views
14

Я пытаюсь настроить блокирующий сокет на таймаут после 16 мс попыток recvfrom() на порту. Платформа - это Windows. Я просмотрел множество примеров в Интернете, и кажется, что это очень просто, я просто не могу заставить его работать. Любая помощь будет оценена!Установить таймаут для winsock recvfrom

#include <winsock2.h> 
#include <string> 

#pragma comment(lib, "ws2_32.lib") 

#define PORT_NUM 8001 

int main(void) 
{ 
    std::string localIP; 
    sockaddr_in localAddr; 
    sockaddr_in remoteAddr; 
    hostent* localhost; 
    char buffer[1024]; 
    WSADATA wsData; 

    int result = WSAStartup(MAKEWORD(2,2), &wsData); // winsock version 2 

    localhost = gethostbyname(""); 
    localIP = inet_ntoa(*(in_addr*)*localhost->h_addr_list); 

    localAddr.sin_family  = AF_INET; 
    localAddr.sin_port   = htons(PORT_NUM);    // Set Port Number 
    localAddr.sin_addr.s_addr = inet_addr(localIP.c_str()); // Set IP Address 

    int mHandle = WSASocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, 0); 

    if(mHandle == INVALID_SOCKET) 
    return 1; 


    if(bind(mHandle, (SOCKADDR*)&localAddr, sizeof(localAddr)) == SOCKET_ERROR) 
    return 1; 

    timeval tv; 
    tv.tv_sec = 0; 
    tv.tv_usec = 1600; 

    // Set Timeout for recv call 
    if(setsockopt(mHandle, SOL_SOCKET, SO_RCVTIMEO, 
       reinterpret_cast<char*>(&tv), sizeof(timeval))) 
    return 1; 

    int length = sizeof(remoteAddr); 

    // <-- Blocks here forever 
    recvfrom(mHandle, buffer, 1024, 0, (SOCKADDR*)&remoteAddr, &length); 

    return 0; 
} 

/* I've also tried passing the time like so: 
int ms = 16; 

if(setsockopt(mHandle, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<char*>(&ms), sizeof(int))) 
    return 1; */ 
+0

SO_RCVTIMEO не очень портативен - на какой платформе вы используете? – Dipstick

+8

Возможно, вы захотите использовать функцию 'select' – laura

+0

Эй, спасибо! Да, я посмотрел в избранное и получил его на работу! –

ответ

21

Я заглянул в функцию выбора, и, поскольку лаура сказала, что я должен это сделать, и заставил ее работать очень легко! Благодаря!

fd_set fds ; 
int n ; 
struct timeval tv ; 

// Set up the file descriptor set. 
FD_ZERO(&fds) ; 
FD_SET(mHandle, &fds) ; 

// Set up the struct timeval for the timeout. 
tv.tv_sec = 10 ; 
tv.tv_usec = 0 ; 

// Wait until timeout or data received. 
n = select (mHandle, &fds, NULL, NULL, &tv) ; 
if (n == 0) 
{ 
    printf("Timeout..\n"); 
    return 0 ; 
} 
else if(n == -1) 
{ 
    printf("Error..\n"); 
    return 1; 
} 

int length = sizeof(remoteAddr); 

recvfrom(mHandle, buffer, 1024, 0, (SOCKADDR*)&remoteAddr, &length); 
+0

Итак, я понимаю, что вы отправляете только один пакет при каждом запуске приложения. Предположим, что вы отправляете тысячи пакетов внутри цикла, этот код может сработать! Как вы решите эту проблему? –

+0

Это лучший ответ. Другой ответ - SO_RCVTIMEO как DWORD - будет тайм-аут recvfrom, но у него есть несколько проблем: 1) если тайм-аут происходит, сокет больше не находится в допустимом состоянии. 2) Я не смог найти документы Windows, объясняющие, как включить тайм-аут ... но поскольку ваш сокет будет недействительным, я думаю, вам все равно придется создать новый. –

1

Я угадываю Windows из вызова WSASocket(). Если это так, вы пропустите таймаут неправильно.

MSDN говорит, что SO_RCVTIMEO принимает параметр int, определяющий тайм-аут в мс.

+0

Я также пробовал его, передавая его следующим образом: int ms = 16; if (setsockopt (mHandle, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast (& ms), sizeof (int))) return 1; У меня все тот же результат. –

+0

Спасибо, это именно то, что мне нужно для системы сокетов BSD, которая не принимает SO_RCVTIMEO –

+0

. Он также говорит: «Если время ожидания блокировки приема истекает, соединение находится в неопределенном состоянии и должен быть закрыт. Если сокет создан с использованием функции WSASocket, тогда параметр dwFlags должен иметь атрибут WSA_FLAG_OVERLAPPED, установленный для того, чтобы таймаут функционировал должным образом. В противном случае тайм-аут никогда не вступает в силу. ", поэтому я не уверен, что он должен использоваться регулярно ... – Marki555

10

Windows: значение времени ожидания является DWORD в миллисекундах, адрес передается setsockopt() является Const символ *

LINUX: значение тайм-аута является структура формата: первый формат, адрес передается setsockopt() является Const пустота *

Источник: http://forums.codeguru.com/showthread.php?t=353217

13

Я попробовал его, передавая его как folloing

 int iTimeout = 1600; 
    iRet = setsockopt(pSapManager->m_cSocket, 
         SOL_SOCKET, 
         SO_RCVTIMEO, 
         /* 
         reinterpret_cast<char*>(&tv), 
         sizeof(timeval)); 
         */ 
         (const char *)&iTimeout, 
         sizeof(iTimeout)); 

и запустить его !!

+0

Привет, добро пожаловать в SO. Попробуйте объяснить свой ответ словами, а не только кодом.Это будет проще для всех, чтобы учиться именно так. Кроме того, поскольку вы отвечаете на старый вопрос, как ваш ответ отличается от других ответов? –

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