winsock2 tcp 在第二次运行可执行文件时无法接收

问题描述

我正在开发跨平台的服务器和客户端。客户端是 Unity3d 应用程序,客户端是用 C++ 编写的。它在 Linux 和 Mac 中运行良好,但在 Windows 中不起作用。所以我认为我在使用 Winsock2 时做错了。

这很奇怪。我第一次编译它,它工作正常。当我关闭可执行文件并再次运行它时,服务器端在一段时间后无法接收(发送/接收大约 5~10 条消息后)。它只是挂在 recv 函数上。用wireshark检查了它。客户端发送了消息。但是服务器无法接收。一旦发生这种情况,即使再次建立连接,它也会停止工作。

我强制关闭应用程序。这可能不会正确关闭套接字。但是当用户只是关闭可执行文件时,我没有看到很好地关闭它的方法

这是一个可重现的代码

#include <chrono>
#undef UNICODE
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <atomic>
#include <fstream>
#include <iterator>
#include <string>
#include <thread>
#include <iostream>
#include <vector>

std::vector<char> receive_buffer;
int client_;
int server_fd;
int port_ = 8080;
bool terminateRequested_ = false;
bool connected_=false;
char tempBuffer[1024];

inline bool processRequests() {
    std::cout<<"recv \n";
    int recv_size = recv(client_,&receive_buffer[0],1024,0);
    std::cout<<"recv "<<recv_size<<" \n";
    if (recv_size <= 0) {
        std::cout<<"recv Failed \n\n";
        return false;
    }
    tempBuffer[1023] = 'e';

    send(client_,&tempBuffer[0],0);
}

inline bool waitForReadEvent(int timeout) {
    fd_set sdset;
    struct timeval tv;

    tv.tv_sec = timeout;
    tv.tv_usec = 0;
    FD_ZERO(&sdset);
    FD_SET(server_fd,&sdset);
    return select(server_fd + 1,&sdset,NULL,&tv) > 0;
}

inline void loop() {
    WSADATA wsaData;
    int iResult;

    struct addrinfo *result = NULL;
    struct addrinfo hints;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2),&wsaData);

    // holds address info for socket to connect to
    struct addrinfo *ptr = NULL;

    int opt = 1000000000;
    ZeroMemory(&hints,sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    std::string portInString = std::to_string(port_);

    // Resolve the server address and port
    iResult = getaddrinfo(NULL,portInString.c_str(),&hints,&result);
    if (iResult != 0) {
        printf("getaddrinfo Failed with error: %d\n",iResult);
        WSACleanup();
        return;
    }

    // Create a SOCKET for connecting to server
    server_fd =
            socket(result->ai_family,result->ai_socktype,result->ai_protocol);
    if (server_fd == INVALID_SOCKET) {
        printf("socket Failed with error: %ld\n",WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();
        return;
    }

    setsockopt(server_fd,SOL_SOCKET,SO_SNDBUF,(char *)&opt,sizeof(opt));
    setsockopt(server_fd,SO_RCVBUF,sizeof(opt));

    // Setup the TCP listening socket
    iResult = bind(server_fd,result->ai_addr,(int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        printf("bind Failed with error: %d\n",WSAGetLastError());
        closesocket(server_fd);
        WSACleanup();
        return;
    }

    iResult = listen(server_fd,SOMAXCONN);
    if (iResult == SOCKET_ERROR) {
        printf("listen Failed with error: %d\n",WSAGetLastError());
        closesocket(server_fd);
        WSACleanup();
        return;
    }


    while (!terminateRequested_) {
        if (waitForReadEvent(2.0)) {
            client_ = accept(server_fd,NULL);
            connected_ = true;
        }

        std::chrono::steady_clock::time_point lastChecked,current;
        lastChecked = std::chrono::steady_clock::Now();

        while (connected_) {

            if (processRequests()) {
                lastChecked = std::chrono::steady_clock::Now();
            } else {
                current = std::chrono::steady_clock::Now();

                if (std::chrono::duration_cast<std::chrono::seconds>(current - lastChecked).count() > 3.0) {
                    std::cout<<"The client has been disconnected for "<<std::chrono::duration_cast<std::chrono::seconds>(current - lastChecked).count()<<" seconds. Waiting for a new connection..."<<std::endl;
                    connected_ = false;
                }
            }
        }
    }
    closesocket(server_fd);
    WSACleanup();
}

int main() {
    receive_buffer.resize(1024);
    std::fill(receive_buffer.begin(),receive_buffer.end(),0);
    loop();
    return 0;
}

这是wireshark的输出

No.     Time           Source                Destination           Protocol Length Info
    110 4.532631       127.0.0.1             127.0.0.1             TCP      56     9910 → 8080 [PSH,ACK] Seq=133 Ack=455184963 Win=2609408 Len=12

Frame 110: 56 bytes on wire (448 bits),56 bytes captured (448 bits) on interface \Device\NPF_Loopback,id 0
Null/Loopback
Internet Protocol Version 4,Src: 127.0.0.1,Dst: 127.0.0.1
Transmission Control Protocol,Src Port: 9910,Dst Port: 8080,Seq: 133,Ack: 455184963,Len: 12

No.     Time           Source                Destination           Protocol Length Info
    111 4.532650       127.0.0.1             127.0.0.1             TCP      44     8080 → 9910 [ACK] Seq=455184963 Ack=145 Win=999997440 Len=0

Frame 111: 44 bytes on wire (352 bits),44 bytes captured (352 bits) on interface \Device\NPF_Loopback,Src Port: 8080,Dst Port: 9910,Seq: 455184963,Ack: 145,Len: 0

No.     Time           Source                Destination           Protocol Length Info
    112 4.532856       127.0.0.1             127.0.0.1             TCP      1068   8080 → 9910 [PSH,ACK] Seq=455184963 Ack=145 Win=999997440 Len=1024

Frame 112: 1068 bytes on wire (8544 bits),1068 bytes captured (8544 bits) on interface \Device\NPF_Loopback,Len: 1024

No.     Time           Source                Destination           Protocol Length Info
    113 4.532873       127.0.0.1             127.0.0.1             TCP      44     9910 → 8080 [ACK] Seq=145 Ack=455185987 Win=2608384 Len=0

Frame 113: 44 bytes on wire (352 bits),Seq: 145,Ack: 455185987,Len: 0

No.     Time           Source                Destination           Protocol Length Info
    114 4.540624       127.0.0.1             127.0.0.1             TCP      56     [TCP Retransmission] 9912 → 8080 [SYN] Seq=0 Win=65535 Len=0 MSS=65495 WS=256 SACK_PERM=1

Frame 114: 56 bytes on wire (448 bits),Src Port: 9912,Seq: 0,Len: 0

No.     Time           Source                Destination           Protocol Length Info
    115 4.540673       127.0.0.1             127.0.0.1             TCP      56     [TCP Port numbers reused] 8080 → 9912 [SYN,ACK] Seq=810974615 Ack=1 Win=65535 Len=0 MSS=65495 WS=16384 SACK_PERM=1

Frame 115: 56 bytes on wire (448 bits),Dst Port: 9912,Seq: 810974615,Ack: 1,Len: 0

No.     Time           Source                Destination           Protocol Length Info
    116 4.540693       127.0.0.1             127.0.0.1             TCP      44     9912 → 8080 [ACK] Seq=1 Ack=810974616 Win=327424 Len=0

Frame 116: 44 bytes on wire (352 bits),Seq: 1,Ack: 810974616,Len: 0

No.     Time           Source                Destination           Protocol Length Info
    117 4.546600       127.0.0.1             127.0.0.1             TCP      56     9912 → 8080 [PSH,ACK] Seq=1 Ack=810974616 Win=327424 Len=12

Frame 117: 56 bytes on wire (448 bits),Len: 12

No.     Time           Source                Destination           Protocol Length Info
    118 4.546614       127.0.0.1             127.0.0.1             TCP      44     8080 → 9912 [ACK] Seq=810974616 Ack=13 Win=999997440 Len=0

Frame 118: 44 bytes on wire (352 bits),Seq: 810974616,Ack: 13,Len: 0

No.     Time           Source                Destination           Protocol Length Info
    119 4.665321       127.0.0.1             127.0.0.1             TCP      56     [TCP Retransmission] 9909 → 8080 [SYN] Seq=0 Win=65535 Len=0 MSS=65495 WS=256 SACK_PERM=1

Frame 119: 56 bytes on wire (448 bits),Src Port: 9909,Len: 0

No.     Time           Source                Destination           Protocol Length Info
    120 4.665382       127.0.0.1             127.0.0.1             TCP      56     [TCP Port numbers reused] 8080 → 9909 [SYN,ACK] Seq=2078265275 Ack=1 Win=65535 Len=0 MSS=65495 WS=16384 SACK_PERM=1

Frame 120: 56 bytes on wire (448 bits),Dst Port: 9909,Seq: 2078265275,Len: 0

No.     Time           Source                Destination           Protocol Length Info
    121 4.665401       127.0.0.1             127.0.0.1             TCP      44     9909 → 8080 [ACK] Seq=1 Ack=2078265276 Win=327424 Len=0

Frame 121: 44 bytes on wire (352 bits),Ack: 2078265276,Len: 0

No.     Time           Source                Destination           Protocol Length Info
    122 6.055260       127.0.0.1             127.0.0.1             TCP      44     9909 → 8080 [FIN,ACK] Seq=1 Ack=2078265276 Win=327424 Len=0

Frame 122: 44 bytes on wire (352 bits),Len: 0

No.     Time           Source                Destination           Protocol Length Info
    123 6.055277       127.0.0.1             127.0.0.1             TCP      44     8080 → 9909 [ACK] Seq=2078265276 Ack=2 Win=999997440 Len=0

Frame 123: 44 bytes on wire (352 bits),Seq: 2078265276,Ack: 2,Len: 0

No.     Time           Source                Destination           Protocol Length Info
    124 6.420665       127.0.0.1             127.0.0.1             TCP      56     9915 → 8080 [SYN] Seq=0 Win=65535 Len=0 MSS=65495 WS=256 SACK_PERM=1

Frame 124: 56 bytes on wire (448 bits),Src Port: 9915,Len: 0

No.     Time           Source                Destination           Protocol Length Info
    125 6.420705       127.0.0.1             127.0.0.1             TCP      56     8080 → 9915 [SYN,ACK] Seq=0 Ack=1 Win=65535 Len=0 MSS=65495 WS=16384 SACK_PERM=1

Frame 125: 56 bytes on wire (448 bits),Dst Port: 9915,Len: 0

No.     Time           Source                Destination           Protocol Length Info
    126 6.420724       127.0.0.1             127.0.0.1             TCP      44     9915 → 8080 [ACK] Seq=1 Ack=1 Win=2619648 Len=0

Frame 126: 44 bytes on wire (352 bits),Len: 0

No.     Time           Source                Destination           Protocol Length Info
    127 6.445091       127.0.0.1             127.0.0.1             TCP      56     9915 → 8080 [PSH,ACK] Seq=1 Ack=1 Win=2619648 Len=12

Frame 127: 56 bytes on wire (448 bits),Len: 12

No.     Time           Source                Destination           Protocol Length Info
    128 6.445109       127.0.0.1             127.0.0.1             TCP      44     8080 → 9915 [ACK] Seq=1 Ack=13 Win=999997440 Len=0

Frame 128: 44 bytes on wire (352 bits),Len: 0

No.     Time           Source                Destination           Protocol Length Info
    129 7.948501       127.0.0.1             127.0.0.1             TCP      44     9915 → 8080 [FIN,ACK] Seq=13 Ack=1 Win=2619648 Len=0

Frame 129: 44 bytes on wire (352 bits),Seq: 13,Len: 0

解决方法

客户端正在创建一个新线程并在那里建立连接。 Atfer 将其移动到主线程,问题就消失了。我不确定这如何影响 tcp,但它现在可以在我所有的五台 Windows 机器上运行