簡體   English   中英

在Windows上將select()與超時一起使用時的隨機奇怪行為

[英]Randomly strange behaviour when using select() with timeout on Windows

我正在使用一種工具與游戲服務器通信。 要建立與游戲服務器的連接,我要發送一個登錄數據包,然后從那里繼續。 我還使用了一個功能相同的工具,但該工具是由其他人用C#編寫的,並帶有預制庫。 在使用了幾個小時后,這個應用程序出現了一些stackoverflow異常的問題,並且移植到Linux上也沒有太大的樂趣,因此我決定使用C ++從頭開始編寫自己的應用程序。

我的腳本幾乎如下所示:

while (!connected) {
     if (connectCounter == 0)
        std::cout << "Trying to connect..." << std::flush;
    else
        std::cout << "." << std::flush; // add point

    connectCounter++;

    int selectSize = 0;
    struct timeval timeout;
    timeout.tv_sec = 5;
    timeout.tv_usec = 0;
    fd_set fds;
    FD_ZERO(&fds);
    FD_SET(mysocket, &fds);

    selectSize = select(mysocket + 1, &fds, 0, 0, &timeout);
    if (selectSize == 1) {
        // we might now be logged in, check routines
        connected = true;
     }
} 

現在,在這兩個應用程序中,一個隨機出現在我身上的“錯誤”,一個是由其他人用C#編寫的,另一個是由我自己編寫的。 我可能應該提到我以前從未有過這種行為,但是自從格式化計算機以來,我第一次看到這個問題。

問題:Gameserver離線了幾個小時,計算機可能是全新啟動的。 Gameserver仍然關閉,我啟動了該應用程序。 現在,它嘗試登錄,但由於游戲服務器仍處於離線狀態而無法成功。 現在,它寫“嘗試連接”。 由於超時設置,它應該等待5秒,然后在每次嘗試失敗后加1點。 而是逐點觸發,無需等待超時。 這在應用程序,別人編寫的C#應用​​程序和我自己的應用程序中都會發生。 在這兩個應用程序中,它都是隨機發生的,而不是每次啟動應用程序時都發生。 正如我提到的,在格式化計算機之前,我從未遇到過這個問題。 我也將此應用程序移植到了我的linux服務器上,並且在linux上沒有遇到這種行為。 我的一個朋友也使用這兩種應用程序,從未向我報告過此類問題。

這對我來說太奇怪了,我不知道原因。 從我得到的結果來看,它實際上與代碼無關,因為它發生在兩個完全不同的應用程序中,並且只有重新安裝Windows后才能得知。

編輯1:現在我發現了一些有趣的東西,我在Windows和Linux上添加了以下代碼:

selectSize = select(mysocket + 1, &fds, 0, 0, &timeout);
std::cout << selectSize << std::cout;

有趣的是,在Windows上,我的控制台現在將輸出: Trying to connect...0.1.0.1.0.1.0.1

重新啟動應用程序並輸出Trying to connect...0.0.0.0.0.1在linux上,它始終返回Trying to connect...0.0.0.0.0 ,永遠不會出現誤報。

仍然只在Windows上發生。 甚至不知道C#應用程序中的家伙使用的方法是什么,但是在重新安裝Windows之后,隨機發生相同的問題。

編輯2:我想我找到了問題。

在設置超時和select()之前,我正在使用我的登錄數據包執行sendto()。 我想無論出於什么原因都會有返回的輸入,因此在某些情況下selectSize可能會更改為1。 在Linux上運行時,這是否有可能在Windows上引起問題?

引用“ the” POSIX規范(在線副本)

當對O_NONBLOCK清除的輸入函數的調用不會阻塞時,無論該函數是否成功傳輸數據,都應認為描述符已准備就緒,可以讀取。 (該函數可能返回數據, 文件結束指示或錯誤,而不是一個錯誤,表明該數據已被阻止,並且在每種情況下,都應將描述符視為已准備好進行讀取。

所以我想,以解決您的代碼說你必須另外檢查是否文件描述符“讀就緒”沒有任何錯誤或EOF指示。

要檢查套接字是否已連接,應檢查其可寫性,而不是可讀性。 更改

selectSize = select(mysocket + 1, &fds, 0, 0, &timeout);

selectSize = select(mysocket + 1, 0, &fds, 0, &timeout);

好的,看來我終於找到了對最初問題的至少部分答案,為什么在Windows中斷我的應用程序時linux給我一個有效的結果。 從我在Windows平台上閱讀的內容中,select()返回WSAECONNECTRESET而不是阻止或超時,請參見: WinSock Recvfrom()現在返回WSAECONNRESET而不是阻止或超時

因此,這似乎是應用程序在Linux上運行良好(出於我的目的)的原因,其中select()似乎仍返回超時,而Windows返回該錯誤並在一定程度上中斷了我的應用程序。

解決方案:所以我終於找到了解決方法。 特別感謝提醒我使用Wireshark的那個人。 起初,我在將登錄數據包發送到gameserver時(離線時)將其應該為0的select()賦值為1,這是完全隨機的,但實際上我發現有時我會遇到“ ICMP端口無法訪問”的問題,導致select()返回1而不是0(請參見上面的鏈接)顯然,我只希望當實際的登錄響應來自服務器時,select()返回1。 在linux上,這是開箱即用的,不會引起任何問題。 對於Windows,我通過在select()函數之前添加以下代碼找到了一個簡單的修復程序:

#define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR, 12)
DWORD lpcbBytesReturned = 0;
BOOL lpvInBuffer = FALSE;

WSAIoctl(mysocket, SIO_UDP_CONNRESET, &lpvInBuffer, sizeof(lpvInBuffer), NULL, 0, &lpcbBytesReturned, NULL, NULL);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM