您的位置:首页 > 其它

WinSock中关于阻塞接收/发送超时的一个BUG

2013-05-10 09:53 357 查看
在阻塞模型中,recvfrom和recv函数默认都是永久阻塞的,即没有数据到来和不发生错误的情况下函数的调用不会返回,但是可以调用setsockopt来设置阻塞时间。设置了合适的阻塞时间,可以让接收函数超时返回。

int setsockopt(
  __in          SOCKET s,
  __in          int level,
  __in          int optname,
  __in          const char* optval,
  __in          int optlen
);

SO_RCVTIMEOintReceives time-out in milliseconds (available in the Microsoft implementation of Windows Sockets 2).
SO_SNDTIMEOintSends time-out in milliseconds (available in the Microsoft implementation of Windows Sockets 2).
SO_RECVTIMEO即可控制接收函数的超时时间。例代码如下:

#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>
#include <time.h>

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

int main()
{
    WSADATA wd;

    WSAStartup(MAKEWORD(2, 2), &wd);

    SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    sockaddr_in sin;
    DWORD dwTime = 1000;

    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = htons(0);

    if (SOCKET_ERROR == bind(sock, (sockaddr *)&sin, sizeof(sin)) ||
        SOCKET_ERROR == setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&dwTime, sizeof(dwTime)))
    {
        return 0;
    }

    DWORD dwIndex = 0;

    while (TRUE)
    {
        char szBuffer[10000];
        int len = sizeof(sin);
        int nRet = recvfrom(sock, szBuffer, sizeof(szBuffer), 0, (sockaddr *)&sin, &len);

        //本应至少每一秒输出一次,但是将系统时间更改到之前的时间后,此处不再输出
        printf("%lu::%lu:%d\n", dwIndex++, time(NULL), nRet);
    }

    closesocket(sock);

    return 0;
}


设定了超时时间后,recvfrom会定期返回并输出消息,如果这时候更改了系统时间,比如把系统时间改为昨天,那么recvfrom就不会立即返回了。把系统时间向后改比如改为明天则没有这个问题,但是再改回来又会出问题。推测是内部实现使用了绝对时间,而非相对时间才导致了这个问题。

阻塞IO模型中的UDP发送、TCP接收、TCP发送应该也存在类似的问题,不过我没有进行测试。linux平台不存在这个问题。

ps:我认为linux平台的做法是对的,这里应当使用相对时间。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: