Keegz510
Keegz510

Reputation: 1

Winsock closing socket

So I've been creating a winsock server/client in UE4. I can get the client to connect to the server however once the client sends the first message it seems to close the socket, preventing any further messages to be sent to the server. It also seems like the server is doing the same thing when sending data.

client

// 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;
}

server

    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;

Console output after disconnecting

Upvotes: 0

Views: 919

Answers (1)

pm100
pm100

Reputation: 50210

You are making a fundamental TCP mistake. TCP is a stream protocol., its only gurantees are

  • the bytes you send will be received in the same order they were sent
  • they will be received only once

BUT there are no 'messages' or 'records' in TCP. You can send a 100 byte message and the other end can receive

  • one 100 byte message
  • 25 4 byte messages
  • 100 1 byte messages
  • one 25, one 12, one 3, and one 60 (hope my math is correct)

So in the receive logic you must do this

  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;
  }

Ie keep pulling data till you have the whole message

BUT this means you need to know the messages length in advance. So either

  • send a well known sized length first
  • send fixed length messages

Or you can have a recognizable termination sequence- ie 10 byte of FF means end of message (see crlfcrlf at end of HTTP get for example)

The first option is the most robust (send length then data)

Upvotes: 2

Related Questions