在阻塞模型中,recvfrom和recv函数默认都是永久阻塞的,即没有数据到来和不发生错误的情况下函数的调用不会返回,但是可以调用setsockopt来设置阻塞时间。设置了合适的阻塞时间,可以让接收函数超时返回。
- int setsockopt(
- __in SOCKET s,
- __in int level,
- __in int optname,
- __in const char* optval,
- __in int optlen
- );
SO_RCVTIMEO | int | Receives time-out in milliseconds (available in the Microsoft implementation of Windows Sockets 2). |
SO_SNDTIMEO | int | Sends 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平台的做法是对的,这里应当使用相对时间。