首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Winsock关闭插座

Winsock关闭插座
EN

Stack Overflow用户
提问于 2022-03-21 02:02:45
回答 1查看 366关注 0票数 0

因此,我一直在UE4中创建一个winsock服务器/客户机。我可以让客户端连接到服务器,但是,一旦客户端发送第一条消息,它似乎关闭了套接字,从而防止了向服务器发送任何进一步的消息。在发送数据时,服务器似乎也在做同样的事情。

客户端

代码语言:javascript
复制
// Convert IP & port to standard lib
    const std::string IP = std::string(TCHAR_TO_UTF8((*GameInstance->GetIPAddress())));
    const std::string PORT = std::string(TCHAR_TO_UTF8(*GameInstance->GetPort()));
    // Set the version of WSA we are using
    auto Version = MAKEWORD(2, 2);
    WSAData WSData;
    struct addrinfo* Result = nullptr, * ptr = nullptr, hints;

    int iResult;                // Store Initializing results
    std::string message;        // Define a message to send to the server

    UE_LOG(LogTemp, Log, TEXT("Starting Client"));

    // Initialize WinSock
   
    iResult = WSAStartup(Version, &WSData);         // Start winsock
    if(iResult != 0)
    {
        UE_LOG(LogTemp, Error, TEXT("Failed to initialize winsock"));
        return ECreateConnectionFlag::WINSOCK_FAILED;
    }
    
    UE_LOG(LogTemp, Log, TEXT("Initialized WinSock"));

    // Setup hints
    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    UE_LOG(LogTemp, Log, TEXT("Connecting"));

    // Get the address details
    iResult = getaddrinfo(IP.c_str(), PORT.c_str(), &hints, &Result);
    if(iResult != 0)
    {
        UE_LOG(LogTemp, Error, TEXT("Error getting address info from the server"));
        WSACleanup();
        return 0;
    }

    // Connect the player
    for(ptr = Result; ptr != nullptr; ptr->ai_next)
    {
        GameInstance->SetPlayerSocket(socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol));
        if(GameInstance->GetLoggedInPlayer().PlayerSocket == INVALID_SOCKET)
        {
            UE_LOG(LogTemp, Error, TEXT("Failed to create socket"));
            WSACleanup();
            return 0;
        }

        iResult = connect(GameInstance->GetLoggedInPlayer().PlayerSocket, ptr->ai_addr, ptr->ai_addrlen);
        if(iResult == SOCKET_ERROR)
        {
            closesocket(GameInstance->GetLoggedInPlayer().PlayerSocket);
            GameInstance->SetPlayerSocket(INVALID_SOCKET);
            continue;
        }
        break;
    }

    freeaddrinfo(Result);               // Release Address information as it's no longer required

    // Ensure the socket is valid
    if(GameInstance->GetLoggedInPlayer().PlayerSocket == INVALID_SOCKET)
    {
        UE_LOG(LogTemp, Error, TEXT("Unable to connect to server..."));
        WSACleanup();
        return 0;
    }

    

    // WE ARE CONNECTED

    /* CONNECT AND SEND USERNAME */
    FString SignInMessage = FString("Username-" + GameInstance->GetLoggedInPlayer().Username);
    std::string ConnectionMessage = std::string(TCHAR_TO_UTF8(*SignInMessage));
    iResult = send(GameInstance->GetLoggedInPlayer().PlayerSocket, ConnectionMessage.c_str(), (int)strlen(ConnectionMessage.c_str()), 0);
    if(iResult <= 0)
    {
        int error = WSAGetLastError();
        UE_LOG(LogTemp, Error, TEXT("Failed to send message: %d"), error);
        return 0;
    }
    
    
    while(bRunThread)
    {
        UE_LOG(LogTemp, Log, TEXT("Receiving Data"));
        /* DISCONNECT FROM SERVER */
        const std::string msg = "Hello World";
        iResult = send(GameInstance->GetLoggedInPlayer().PlayerSocket, msg.c_str(), (int)strlen(msg.c_str()), 0);
        if(iResult <= 0)
        {
            int error = WSAGetLastError();
            UE_LOG(LogTemp, Error, TEXT("Failed to send message: %d"), error);
            return 0;
        }
        FPlatformProcess::Sleep(1.0f);
    }

    /* DISCONNECT FROM SERVER */
    const std::string DisconnectMsg = "Disconnect";
    iResult = send(GameInstance->GetLoggedInPlayer().PlayerSocket, DisconnectMsg.c_str(), (int)strlen(DisconnectMsg.c_str()), 0);
    if(iResult <= 0)
    {
        int error = WSAGetLastError();
        UE_LOG(LogTemp, Error, TEXT("Failed to send message: %d"), error);
        return 0;
    }

    UE_LOG(LogTemp, Warning, TEXT("Client Disconnected"));
    closesocket(GameInstance->GetLoggedInPlayer().PlayerSocket);
    WSACleanup();
    return 0;
}

服务器

代码语言:javascript
复制
    WSAData wsa;                
    struct addrinfo hints;                          // Server Hint details
    struct addrinfo* server = NULL;                 // Address info of the server
    SOCKET serverSocket = INVALID_SOCKET;           // Server Listening Socket

    PlayerArray* Players = new PlayerArray();           // Reference to all the players in the server
    LobbyArray* Lobbies = new LobbyArray();             // Reference to all the lobbies in the server


    // Initialize the winsock library
    std::cout << "Initializing WinSock..." << std::endl;
    int WSA_Init = WSAStartup(MAKEWORD(2, 2), &wsa);
    if (WSA_Init != 0)
    {
        std::cerr << "Error Initializing Winsock";
        WSACleanup();
        return;
    }
    else
    {
        std::cout << "Winsock Initialized" << std::endl;
    }

    // Setup Hints
    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    // Setup the server
    std::cout << "Setting up Server" << std::endl;
    getaddrinfo(IP_ADDRESS, PORT, &hints, &server);

    // Create the listening socket
    std::cout << "Creating Listening socket" << std::endl;
    serverSocket = socket(server->ai_family, server->ai_socktype, server->ai_protocol);
    if (serverSocket == INVALID_SOCKET)
    {
        std::cerr << "Failed creating listening socket" << std::endl;
        WSACleanup();
        return;
    }
    else
    {
        std::cout << "Created listen socket" << std::endl;
    }

    // Set the socket to be TCP
    setsockopt(serverSocket, IPPROTO_TCP, TCP_NODELAY, &OPTION_VALUE, sizeof(int));

    // Bind the socket
    std::cout << "Binding Socket..." << std::endl;
    bind(serverSocket, server->ai_addr, (int)server->ai_addrlen);

    // Start the server
    std::cout << "Server has started & is listening..." << std::endl;
    listen(serverSocket, SOMAXCONN);

    while (true)
    {

        std::cout << "Players Connected: " << Players->Count() << std::endl;
        SOCKET Incoming = INVALID_SOCKET;               // Define a socket for anything incoming
        Incoming = accept(serverSocket, NULL, NULL);        // Accept the incoming message from the socket

        // If the socket is not valid than continue through the loop
        if (Incoming == INVALID_SOCKET)
        {
            std::cout << "Invalid Socket" << std::endl;
            continue;
        }
        else
        {
            std::cout << "Valid Socket" << std::endl;
        }
            

        char tempmsg[DFT_BUFLEN] = "";                  // Define a temp msg to store the message from the client
        int received = recv(Incoming, tempmsg, DFT_BUFLEN, 0);          // Receive a message from the client
        
        std::string convertedMessage = tempmsg;

        // Check that the received message is from a valid socket
        if (received != SOCKET_ERROR)
        {
            
            std::string message = tempmsg;              // Assing the temp message to a string to split
            if (convertedMessage == "Disconnect")
            {
                Players->RemovePlayer(Incoming);
                std::cout << "Player Disconnected..." << std::endl;
                continue;
            }
            else
            {
                std::cout << tempmsg << std::endl;                      // === DEBUG ===
                // Split the string
                char* next_split;
                char* split_string = strtok_s(tempmsg, "-", &next_split);
                std::string FirstMsg = split_string;
                if (FirstMsg == "Username")
                {
                    std::cout << next_split << " Has joined the server" << std::endl;           // Server message
                    // Get the player that we want to set the username to

                    // Create the player and add it to the server list
                    Player* NewPlayer = new Player();
                    Players->AddPlayer(NewPlayer);

                    NewPlayer->SetUsername(next_split);         // Set the usernames
                    continue;
                    

                }
                else if (split_string == "Lobby")
                {
                    if (next_split == "Create")
                    {
                        Lobby* NewLobby = Lobbies->CreateLobby();               // Create a new lobby
                        Player* SocketPlayer = Players->GetPlayerBySocket(Incoming);        // Get the player creating it by socket

                        // ensure that the player is valid, if so add the player to the lobby
                        // Otherwise send an error message to the console.
                        if (SocketPlayer != nullptr)            
                        {
                            NewLobby->AddPlayerToLobby(SocketPlayer);
                        }
                        else
                        {
                            std::cerr << "Failed Locate player to add to lobby" << std::endl;
                        }
                    }
                    else if (next_split == "Destroy")
                    {
                        // TODO: Destroy Specific lobby
                    }

                    continue;
                }
                else
                {
                    std::cout << "Error Reading Message" << std::endl;
                }
            }
        }
        else
        {
            std::cerr << "Socket Error when recieving message" << std::endl;
        }

        

    }

    // Clean up the server
    delete Players;
    delete Lobbies;
    closesocket(serverSocket);
    WSACleanup();



    return;

断开连接后的控制台输出

EN

回答 1

Stack Overflow用户

发布于 2022-03-21 04:15:44

您正在犯一个根本的TCP错误。TCP是一个流协议,它唯一的权威是

  • 您发送的字节将以相同的顺序接收。
  • 他们只会收到一次

但是TCP中没有“消息”或“记录”。您可以发送100字节的消息,另一端可以接收

  • 一条100字节消息
  • 25 4字节消息
  • 100 1字节消息
  • 1 25,1 12,1 3和1 60 (希望我的数学是正确的)

因此,在接收逻辑中,您必须这样做

代码语言:javascript
复制
  char buffer[1000]; // or whatever

  int length = ????;
  char* bptr = buffer;
  while(length > 0){

      int recvLen = recv(sock, bptr, length,0);
      if (recvLen < 1){
            // error - disconnect or other failure
           break;
      }
      bptr += recvLen;
      length -= recvLen;
  }

继续提取数据,直到你收到全部信息

但这意味着您需要提前知道消息的长度。所以不管是

  • 先发送一个已知大小的长度
  • 发送固定长度的消息

或者您可以有一个可识别的终止序列--即10字节的FF意味着消息的结束(例如,请参阅HTTP末尾的crlfcrlf )

第一个选项是最健壮的(发送长度然后是数据)。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71552102

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档