为什么windows udp接收套接字的超时总是比SO_RCVTIMEO设置的时间长500毫秒?

时间:2018-01-02 01:18:54

标签: c++ sockets

易于重现,这是我正在做的伪代码:

  1. 设置UDP套接字
  2. 将超时设置为值(Timeout set
  3. 检查我设置的超时(Timeout checked
  4. 尝试在该套接字上接收(没有流量时)。
  5. 超时需要多长时间。 (Time until Timeout
  6. 当我这样做时,我得到以下输出:

    Timeout set: 0.1s | Timeout checked: 0.1s | Time until timeout: 0.6s | difference: 0.5s
    Timeout set: 0.2s | Timeout checked: 0.2s | Time until timeout: 0.7s | difference: 0.5s
    Timeout set: 0.4s | Timeout checked: 0.4s | Time until timeout: 0.9s | difference: 0.5s
    Timeout set: 0.8s | Timeout checked: 0.8s | Time until timeout: 1.3s | difference: 0.5s
    Timeout set: 1.6s | Timeout checked: 1.6s | Time until timeout: 2.1s | difference: 0.5s
    Timeout set: 3.2s | Timeout checked: 3.2s | Time until timeout: 3.7s | difference: 0.5s
    

    为什么windows udp套接字超时总是比setsockopt中设置的时间长500毫秒?

    查看setsockopt here我无法在涉及SO_RCVTIMEO的部分中看到有关原因的信息。

    要重现的代码:

    #include "stdafx.h"
    #include "winsock2.h"
    #include <chrono>
    #include <iostream>
    
    int main() {
        WORD wVersionRequested;
        WSADATA wsaData;
    
        wVersionRequested = MAKEWORD(2, 2);
        int err = WSAStartup(wVersionRequested, &wsaData);
        if (err != 0) {
            printf("WSAStartup failed with error: %d\n", err);
            while (true);
        }
    
        sockaddr_in socketAddress = { 0 };
        socketAddress.sin_family = PF_INET;
        socketAddress.sin_port = htons(1010);
        socketAddress.sin_addr.s_addr = INADDR_ANY;
    
        // Create the socket
        SOCKET mSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if (!mSocket) { 
            printf("Socket failed with error code : %d", WSAGetLastError());
            while (true);
        }
    
        //Bind
        if (bind(mSocket, (struct sockaddr *)&socketAddress, sizeof(socketAddress)) == SOCKET_ERROR) {
            printf("Bind failed with error code : %d", WSAGetLastError());
            while (true);
        }
    
        // Receive nothing over several different set timeouts
        for (double timeout = 0.1; timeout < 4.0; timeout *= 2) {
    
            // Set timeout
            DWORD lBuffer[2] = { 0, 0 };
            int lSize;
            lBuffer[0] = static_cast<DWORD>(1000.0 * timeout);
            lSize = sizeof(DWORD);
            if (setsockopt(mSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)lBuffer, lSize) != 0) {
                printf("Set socket option failed with error code : %d", WSAGetLastError());
                while (true);
            }
    
            // Check that we get what we set.
            DWORD lBufferout[2] = { 0, 0 };
            if (getsockopt(mSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)lBufferout, &lSize) != 0) {
                printf("Set socket option failed with error code : %d", WSAGetLastError());
                while (true);
            }
    
            // Receive and time
            char buffer[50];
            sockaddr_in senderAddr;
            int senderAddrSize = sizeof(senderAddr);
    
            auto s = std::chrono::steady_clock::now();
    
            int transferred = recvfrom(mSocket, (char*)buffer, 50, 0,
                (sockaddr*)&senderAddr, &senderAddrSize);
    
            auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - s).count() / 1000.0;
    
            std::cout << "Timeout set: " << timeout << "s | Timeout checked: " << lBufferout[0] / 1000.0 << "s | Time until timeout: " << duration << "s | difference: " << duration - timeout << "s\n";
        }
    
        while (true);
    
        return 0;
    }
    

    注意:此代码期望端口1010上没有流量。如果不是这样,请更改该号码。

1 个答案:

答案 0 :(得分:0)

陈述here:

  

SO_RCVTIMEO上的无证件最低限额约为500mS。

这可能是通过始终向SO_RCVTIMEO的任何值集添加500毫秒来实现的。