简体   繁体   English

多客户端 TCP 服务器 C++ 问题

[英]Multi Client TCP server C++ issue

I have made Mutlti-Client sever that can receive and send messages to the sever and the sever can send back the messages to clients but the problem I'm having is client A will not be able to receive the message from client B unless Client A sends a message I want the clients to receive messages anytime I also want to be to type commands on the sever without stopping the flow of sending to clients and receiving message from clients我已经制作了可以接收消息并将消息发送到服务器的 Mutlti-Client 服务器,并且服务器可以将消息发送回客户端,但我遇到的问题是客户端 A 将无法接收来自客户端 B 的消息,除非客户端 A发送消息我希望客户端随时接收消息我还希望在服务器上键入命令而不停止向客户端发送和从客户端接收消息的流程

I have tried moving the std::getline(std::cin, ClientInput);我试过移动std::getline(std::cin, ClientInput); to different place in the code so clients could be able to see message but I have notice in the code the while loop does not continue because of it is there a way to get user input with out stopping the loop so can send and receive messages到代码中的不同位置,以便客户端可以看到消息,但我在代码中注意到 while 循环不会继续,因为它有一种方法可以在不停止循环的情况下获取用户输入,因此可以发送和接收消息

This the Client code这是客户端代码

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





This is the sever code这是服务器代码

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

Moving the std::getline(std::cin, ClientInput) anywhere within loop in client code will not help you becase getline() is a blocking call.在客户端代码的循环内移动 std::getline(std::cin, ClientInput) 对您没有帮助,因为 getline() 是一个阻塞调用。 In this case you should use threads in client code.在这种情况下,您应该在客户端代码中使用线程。

You need to create a new thread in client code with while loop.您需要使用 while 循环在客户端代码中创建一个新线程。 In this thread you will incoming messages from another client.在此线程中,您将从另一个客户端传入消息。

Main thread in client code will handle user input from getline() and sending messages via send function within while loop.客户端代码中的主线程将处理来自 getline() 的用户输入,并在 while 循环中通过 send function 发送消息。

For example in your client code you can create thread like this:例如,在您的客户端代码中,您可以像这样创建线程:

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

And thread will will process messages in function processMessage like this:线程将处理 function 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);
         }
    }
 }

Be aware that non- blocking sockets exists also (So thread will not stop at recv or send function).请注意,非阻塞 sockets 也存在(因此线程不会在接收或发送函数处停止)。 Function names for non-blocking socket are the same but you must set the socket itself to non-blocking.非阻塞套接字的 Function 名称相同,但您必须将套接字本身设置为非阻塞。

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

You can read about this more here您可以在此处阅读更多信息

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM