Многопользовательский TCP-сервер C ++ проблема - PullRequest
0 голосов
/ 03 апреля 2020

Я сделал сервер Mutlti-Client, который может получать и отправлять сообщения на сервер, и сервер может отправлять сообщения клиентам, но у меня проблема в том, что клиент A не сможет получить сообщение от клиента B если только клиент А не отправит сообщение, я хочу, чтобы клиенты получали сообщения в любое время, я также хочу набирать команды на сервере, не останавливая поток отправки клиентам и получения сообщений от клиентов

Я пытался переместить std::getline(std::cin, ClientInput); в другое место в коде, чтобы клиенты могли видеть сообщение, но я заметил в коде, пока l oop не продолжается, потому что есть способ получить ввод пользователя без остановки l oop так можно отправлять и получать сообщения

Это код клиента

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

1 Ответ

0 голосов
/ 03 апреля 2020

Перемещение std :: getline (std :: cin, ClientInput) в любое место в пределах l oop в коде клиента не поможет вам, потому что getline () является блокирующим вызовом. В этом случае вам следует использовать потоки в клиентском коде.

Вам необходимо создать новый поток в клиентском коде с помощью while l oop. В этой теме вы будете получать входящие сообщения от другого клиента.

Основной поток в коде клиента будет обрабатывать ввод пользователя из getline () и отправлять сообщения через функцию send в течение времени, пока l oop.

Например, в своем клиентском коде вы можете создать поток следующим образом:

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

Be Помните, что существуют неблокирующие сокеты (поэтому поток не остановится на функции recv или send). Имена функций для неблокирующего сокета одинаковы, но вы должны установить сам неблокируемый сокет.

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

Подробнее об этом можно прочитать здесь

...