【问题标题】:Multi Client TCP server C++ issue多客户端 TCP 服务器 C++ 问题
【发布时间】:2020-07-15 09:54:51
【问题描述】:

我已经制作了可以接收消息并将消息发送到服务器的 Mutlti-Client 服务器,并且服务器可以将消息发送回客户端,但我遇到的问题是客户端 A 将无法接收来自客户端 B 的消息除非客户端 A 发送消息我希望客户端随时接收消息我还希望在服务器上键入命令而不停止向客户端发送和从客户端接收消息的流程

我尝试将 std::getline(std::cin, ClientInput); 移动到代码中的不同位置,以便客户端可以看到消息,但我注意到代码中的 while 循环不会继续,因为它有办法让用户输入out 停止循环,以便可以发送和接收消息

这是客户端代码

int Client::OnCreate()
{
    WSADATA wsaData;
    SOCKET ConnectSocket = INVALID_SOCKET;
    struct addrinfo *result = NULL, *ptr = NULL, hints;
    char sendbuf[] = "this is a test";
    std::vector<SOCKET> ConnectedUser;
    char recvbuf[DEFAULT_BUFLEN];
    int iResult;

    int recvbuflen = DEFAULT_BUFLEN;

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        system("pause");
        return 1;
    }

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

    // Resolve the server address and port
    iResult = getaddrinfo("localhost", DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();
        system("pause");
        return 1;
    }

    // Attempt to connect to an address until one succeeds
    for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {

        // Create a SOCKET for connecting to server
        ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
        if (ConnectSocket == INVALID_SOCKET) {
            printf("socket failed with error: %ld\n", WSAGetLastError());
            WSACleanup();
            system("pause");
            return 1;
        }

        // Connect to server.
        iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
        if (iResult == SOCKET_ERROR) {
            closesocket(ConnectSocket);
            ConnectSocket = INVALID_SOCKET;
            continue;
        }
        break;
    }

    freeaddrinfo(result);

    if (ConnectSocket == INVALID_SOCKET) {
        printf("Unable to connect to server!\n");
        WSACleanup();
        system("pause");
        return 1;
    }

    // Send an initial buffer
    std::string ClientInput;

    std::cout << "please send a message " << std::endl;
    std::getline(std::cin, ClientInput);

    do {
        if (ClientInput.size() > 0)
        {
            //send user input to sever 
            iResult = send(ConnectSocket, ClientInput.c_str(), ClientInput.size(), 0);

            if (iResult == SOCKET_ERROR) {
                printf("send failed with error: %d\n", WSAGetLastError());
                closesocket(ConnectSocket);
                WSACleanup();
                system("pause");
                return 1;
            }

            printf("Bytes Sent: %d\n", iResult);

            if (iResult != SOCKET_ERROR)
            {
                //receive user input from sever 
                int iUserResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
                if (iUserResult > 0)
                {
                    std::cout << "Client receive this message from sever \t" << std::string(recvbuf, 0, iResult) << std::endl;
                    printf("Bytes received: %d\n", iUserResult);
                }

            }

            //will disconnect from the sever
            if (iResult == SOCKET_ERROR)
            {
                printf("send failed with error: %d\n", WSAGetLastError());
                closesocket(ConnectSocket);
                WSACleanup();
                system("pause");

            }
            //will disconnect from the sever if user types in logoff
            if (ClientInput == "logoff")
            {
                closesocket(ConnectSocket);
                WSACleanup();
                printf("Connection closing goodbye sever\n");
                system("pause");

            }
            //close connection if the no user input 
            if (iResult == 0) {
                printf("Connection closed\n");
                iResult = shutdown(ConnectSocket, SD_SEND);
            }

        }

        std::getline(std::cin, ClientInput);
    } while (ClientInput.size() > 0);

    // cleanup
    closesocket(ConnectSocket);

    system("pause");
    WSACleanup();
    return 0;
}``` 





这是服务器代码

std::vector<SOCKET> ConnectedUser;
std::vector<int> UserInt;
unsigned __stdcall ClientSession(void *data)
{
    char recvbuf[DEFAULT_BUFLEN];

        int recvbufleng = DEFAULT_BUFLEN;
        int iResult;
        int iSendResult;

        SOCKET ClientSocket = (SOCKET)data;

        struct addrinfo *result = NULL, 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;
        do
        {   
            iResult = recv(ClientSocket, recvbuf, recvbufleng, 0);
            ///iResult = recv(ClientSocket, recvbuf, sizeof(Player), 0);
            std::cout << iResult << std::endl;

            if (iResult > 0) {

                for (int i = 0; i < ConnectedUser.size(); i++) {
                    std::cout << "send message to "<< ConnectedUser[i] << std::endl;
                    //iSendResult = send(ClientSocket, recvbuf, iResult, 0);
                    iSendResult = send(ConnectedUser[i], recvbuf, iResult, 0);
                    //ConnectedUser.clear();
                    //iSendResult = sendto(ConnectedUser[i], recvbuf, iResult, 0, (sockaddr*)&hints, sizeof(hints));

                }

                std::cout << "ClientSocket" << ClientSocket << std::endl;

                if (iSendResult == SOCKET_ERROR)
                {
                    printf("send failed with error: %d \n", WSAGetLastError());
                    closesocket(ClientSocket);
                    WSACleanup();
                    return 1;
                }
                printf("Received Message %.*s\n", iResult, recvbuf);
            }
            else if (iResult == 0)
            {
                printf("connection closing.... \n");
                std::cout << "user logged off" << ClientSocket << std::endl;                
            }
            else
            {
                printf("recv failed with error: %d \n", WSAGetLastError());
                closesocket(ClientSocket);
                WSACleanup();
                return 1;
            }       
        } while (iResult > 0);

        iResult = shutdown(ClientSocket, SD_SEND);

        if (iResult == SOCKET_ERROR) {
            printf("shutdown failed with error: %d \n", WSAGetLastError());
            closesocket(ClientSocket);
            WSACleanup();
            return 1;
        }

        closesocket(ClientSocket);
}

int Sever::OnCreate()
{
    WSADATA wsaData;

        int iResult;
        SOCKET ClientSocket = INVALID_SOCKET;

        //send to 
        char recvbuf[DEFAULT_BUFLEN];
        int recvbufleng = DEFAULT_BUFLEN;

        int iSendResult;

        struct addrinfo *result = NULL, 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;
        iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);

        //create the winsocket
        if (iResult != 0) {
            printf("WSAStartup failed: %d\n, iResult");

            return 1;

        }
        iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);

        if (iResult != 0) {
            printf("getaddrinfo failed: %d\n", iResult);
            WSACleanup();
            return 1;
        }

        SOCKET ListenSocket = INVALID_SOCKET;

        ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);

        if (ListenSocket == INVALID_SOCKET) {
            printf("error at socket(): %d\n", WSAGetLastError());
            freeaddrinfo(result);
            WSACleanup();
            return 1;
        }

        iResult = bind(ListenSocket, result->ai_addr, int(result->ai_addrlen));

        if (iResult == SOCKET_ERROR) {
            printf("bind failed with error: %d \n", WSAGetLastError());
            freeaddrinfo(result);
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        }

        freeaddrinfo(result);

        if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
            printf("Listen failed with error: %d \n", WSAGetLastError());
            closesocket(ListenSocket);
            WSACleanup();
            return 1;
        }

        // Create the master file descriptor set and zero it
        fd_set master;
        FD_ZERO(&master);

        FD_SET(ListenSocket, &master);

        fd_set copy = master;
        int socketCount = select(0, &copy, nullptr, nullptr, nullptr);
        std::cout << " socketCount "<< socketCount << std::endl;

        //// Send an initial buffer
        //if (socketCount == 0) {
        //  std::string SeverInput;
        //  std::cout << "input Server command" << std::endl;
        //  std::getline(std::cin, SeverInput);
        //  
        //  if (SeverInput == "exit")
        //  {
        //      closesocket(ClientSocket);
        //      WSACleanup();
        //      printf("Connection closing goodbye sever\n");
        //  }
        //}

        for (int i = 0; i < socketCount; i++)
        {
            SOCKET sock = copy.fd_array[i];

            if (sock == ListenSocket)
            {
                //// Accept a new connection
                while ((ClientSocket = accept(ListenSocket, NULL, NULL))) {
                    if (ClientSocket == INVALID_SOCKET) {
                        printf("Accept failed with error: %d \n", WSAGetLastError());
                        closesocket(ListenSocket);
                        WSACleanup();
                        return 1;
                    }               

                /*  Messtotal.push_back(ClientSocket + "\n");*/


                    // Add the new connection to the list of connected clients
                    FD_SET(ClientSocket, &master);

                    ConnectedUser.push_back(ClientSocket);

                    std::cout << "client:" << ClientSocket <<" has arrived on sever" <<std::endl;


                    // Create a new thread for the accepted client (also pass the accepted client socket).
                    unsigned threadID;
                    HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0, &ClientSession, (void*)ClientSocket, 0, &threadID);
                }
            }

        }

        WSACleanup();
        printf("Server shutting down ");
        return 0;
```}

【问题讨论】:

  • 我不确定我是否理解您的问题:当 Cilent B 尚未发送任何消息时,您期望来自 Cilent B 的什么样的消息?

标签: c++ windows networking tcp network-programming


【解决方案1】:

在客户端代码的循环内移动 std::getline(std::cin, ClientInput) 对您没有帮助,因为 getline() 是一个阻塞调用。在这种情况下,您应该在客户端代码中使用线程。

您需要使用 while 循环在客户端代码中创建一个新线程。在此线程中,您将收到来自另一个客户端的消息。

客户端代码中的主线程将处理来自 getline() 的用户输入,并通过 while 循环内的 send 函数发送消息。

例如,在您的客户端代码中,您可以像这样创建线程:

m_hTrhead = CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)processMessage, (LPVOID)ConnectSocket, CREATE_SUSPENDED, 0);

if (m_hTrhead == NULL)
{
    printf("Failed to create thread for receiving messages , error code : %ld \n", GetLastError());
    return false;
}

线程会像这样处理函数 processMessage 中的消息:

DWORD WINAPI Socket::processMessage(LPVOID lpParam)
 {
      SOCKET ConnectSocket= reinterpret_cast <SOCKET>(lpParam);
      while (WSAGetLastError() != WSAECONNRESET)
      {
         int iUserResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
         if (iUserResult > 0)
         {
           std::cout << "Client receive this message from sever \t" << std::string(recvbuf, 0, iResult) << std::endl;
         printf("Bytes received: %d\n", iUserResult);
         }
    }
 }

请注意,非阻塞套接字也存在(因此线程不会在 recv 或 send 函数处停止)。非阻塞套接字的函数名称相同,但您必须将套接字本身设置为非阻塞。

u_long mode = 1;  // enable non-blocking socket
ioctlsocket(sock, FIONBIO, &mode);

您可以在here了解更多信息

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-29
    • 2012-07-04
    • 1970-01-01
    • 2022-01-22
    • 2011-08-14
    • 1970-01-01
    • 2019-04-18
    相关资源
    最近更新 更多