我可以通过两者之间的服务器实现客户端 - 客户端通信吗?

标签 c sockets client-server

我想通过这个服务器从客户端 A 向客户端 B 发送消息。我不知道我怎么能得到那个?我能想到的一种方法是为每个客户端创建一个消息队列,如果有人向该客户端发送消息,则将消息添加到该队列,然后从该队列发送到相应的客户端?但我不确定如何实现这一点?谁能帮我这个 ?

任何时候都可以有 n 个客户端。在这种情况下,向所有客户端广播。

下面是我的服务器代码。我已经检查了用户的用户名和密码。 insidePortal()函数将负责向其他客户端发送消息。

#include<stdio.h>
#include <stdlib.h>
#include<string.h>    //strlen
#include<sys/socket.h>
#include<arpa/inet.h> //inet_addr
#include<unistd.h>    //write

int main(int argc , char *argv[])
{
    int socket_desc , client_sock , c , read_size, pid;
    struct sockaddr_in server , client;
    char client_message[2000], message_sent[2000], message_recieve[2000];

    //Create socket
    socket_desc = socket(AF_INET , SOCK_STREAM , 0);
    if (socket_desc == -1)
    {
        printf("Could not create socket");
    }
    puts("Socket created");

    //Prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons( 5550 );

    //Bind
    if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
    {
        //print the error message
        perror("bind failed. Error");
        return 1;
    }
    puts("bind done");

    //Listen
    listen(socket_desc , 3);

    while (1) {
        //Accept and incoming connection
        puts("Waiting for incoming connections...");
        c = sizeof(struct sockaddr_in);

        // accept connection from an incoming client
        client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
        if (client_sock < 0)
        {
            perror("accept failed");
            return 1;
        }

        /* Create child process */
        pid = fork();
      if (pid == 0) {
         /* This is the client process */
        close(socket_desc);
        puts("Connection accepted");

        char username[50], password[50];

        memset(message_recieve, 0, sizeof message_recieve);
        recv(client_sock , username , 2000 , 0);
        printf("username of the user: %s", username);

        memset(message_recieve, 0, sizeof message_recieve);
        recv(client_sock , password , 2000 , 0);
        printf("password of the user: %s", password);

        FILE *f = fopen("registeredUsers.txt", "r");
        if (f == NULL)
        {
            printf("Error opening file!\n");
            exit(1);
        }

        char uname[50], pass[50];
        int login = 0;
        while(fscanf(f, "%s %s\n", uname, pass) > 0) {
            printf("Helllo %s %s", uname, pass);
            if (strcmp(username, uname)==0 && strcmp(password, pass) == 0) {
                login = 1;
                break;
            } 
        }

        memset(message_sent, 0, sizeof message_sent);
        if (login == 1) {
            strcpy(message_sent,"\n Successfull Login\n");
            write(client_sock , message_sent , strlen(message_sent));   // Sends login status
            insidePortal(client_sock, username);
        } else {
            strcpy(message_sent,"\nOops, wrong username or password. Please try again.\n");
            write(client_sock , message_sent , strlen(message_sent));   // Sends login status
        }




        fclose(f);


        if(read_size == 0)
        {
            puts("Client disconnected");
            fflush(stdout);
        }
        else if(read_size == -1)
        {
         perror("recv failed");
        }
        exit(0);
    } else {
        close(client_sock);
    }
    }

    return 0;
}


void insidePortal(int client_sock, char username[50]) {

}

最佳答案

我过去写了一段很长的代码,它实现了与你类似的东西,但它是 CPP,如果你也想要,我会在这里发布,但基本上使用消息队列或多进程编程对我来说似乎有点没用, 如果我是你,我会按以下方式编程

客户端代码 -> 两个线程
服务器代码 -> 两个线程
Client有两个线程,三个函数,Connect/Send/Receive

  • Connect - 这个函数处理与服务器的连接,无论你是使用 TCP,它都会处理监听接受,或者如果你使用一些基于 UDP 的组合协议(protocol),它只会处理它 - 确保你有一些连接
  • Send - 这个函数发送一些数据到服务器
  • Receive这个函数从服务器接收数据

  • 流量Client将如下:
  • Client连接到 Main thread 上的服务器
  • 连接服务器后Client创建 Second thread
  • Main thread - Client进入一些循环,从用户那里读取数据作为输入,然后调用 Send函数并将其发送到服务器
  • Second thread - Client进入一些调用 Receive 的循环
    从服务器接收数据并在收到数据时打印

  • 处理 Client , 现在大约 ServerServer - 具有三个函数和一些称为链接的全局数据结构 - 列表(显然是链接列表)将由所有线程共享,WaitForConnection Receive SendToAll
  • WaitForConnection - 只需调用套接字 API 的“Accept”函数(如果您使用的是 TCP),或者如果您使用的是其他一些组成的协议(protocol),该函数只是阻止它的线程尝试等待传入连接,当某个连接到达时,此函数将连接注册到名为 Connection-List 的所有连接的全局链表中。 ,带有适当的套接字和客户端数据
  • SendToAll - 简单地迭代所有 Connection-List并且对于该列表中的每个连接,它都会发送一些传递的数据
  • Receive只是接收一些数据但设置了一些先超时 ,这对于Receive不会阻塞太久很重要!

  • 流量Server将如下:
  • Main thread创建 Second thread
  • Main thread进入某个循环并调用 WaitForConnection为了不断获得连接并将它们添加到Connection-List
  • Second thread进入一些循环遍历 Connection-List , 对于 Connection-List 中的每个连接, 在适当的套接字上调用接收,如果接收到一些数据,SendToAll使用接收到的数据调用,如果超时,则没有任何 react ,在继续该循环并执行下一个循环迭代之后
  • SendToAll将数据发送到 Connection-List 内的所有客户端

  • 这是一些很简单的与服务器的多客户端广播架构应该真的很容易实现!我希望这可以帮助你

    我提前道歉,因为这是我前一段时间写的代码,所以它里面有很多嵌套,还有很多评论!

    ------------------------- 客户代码 ----
    // Main.cpp
    #include <thread>
    #include <winsock2.h>
    #include <ws2tcpip.h>
    #include <iostream>
    
    #pragma comment (lib, "Ws2_32.lib")
    
    #define NAME_LENGTH    40
    #define DEFAULT_BUFLEN 512
    #define DEFAULT_PORT "8888"
    
    void Receive(SOCKET* pscktConnection);
    void Send(SOCKET* pscktConnection);
    
    void main()
    {
        // Variable definition
        int       nResult;
        int       nNameLength   = 0;
        char     pcNameBuffer[NAME_LENGTH];
        SOCKET    sckConnection = NULL;
        WSADATA   wsaData;
        addrinfo  addrAddressFormat;
        addrinfo* paddrServerAddress;
    
        // Code section
    
        // Initialize Winsock
        nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    
        // If winsock dll loading has failed
        if (nResult != 0)
        {
            std::cout << "Failed loading winsock DLL" << std::endl;
        }
        // DLL loaded successfully
        else
        {
             //Setup connection info
                ZeroMemory(&addrAddressFormat, sizeof(addrAddressFormat));
                addrAddressFormat.ai_family = AF_INET;
                addrAddressFormat.ai_socktype = SOCK_STREAM;
                addrAddressFormat.ai_protocol = IPPROTO_TCP;
    
            // Resolve the server address and port with the address setting into our final address
            nResult = getaddrinfo("10.0.0.5", DEFAULT_PORT, &addrAddressFormat, &paddrServerAddress);
    
            // Address resolving has failed
            if (nResult != 0)
            {
                std::cout << "Some error has occured during connection establishment" << std::endl;
            }
            else
            {
                // Request user for his name
                pcNameBuffer[0] = '\0';
                std::cout << "PLEASE ENTER YOUR NAME -> ";
                std::cin.getline(pcNameBuffer, NAME_LENGTH);
                std::cout << std::endl << std::endl ;
    
                // Creating the socket
                sckConnection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    
                // Connecting
                nResult = connect(sckConnection, paddrServerAddress->ai_addr, (int)paddrServerAddress->ai_addrlen);
    
    
                // Creating of the socket has failed
                if (nResult == SOCKET_ERROR)
                {
                    std::cout << "Creating of the socket has failed" << std::endl;
                }
                // Send server user's name
                else
                {
                    // Measure the name length
                    while (pcNameBuffer[nNameLength] != '\0')
                    {
                        ++nNameLength; 
                    }
    
                    // If invalid name
                    if (nNameLength == 0)
                    {
                        pcNameBuffer[0] = 'G';
                        pcNameBuffer[1] = 'U';
                        pcNameBuffer[2] = 'E';
                        pcNameBuffer[3] = 'S';
                        pcNameBuffer[4] = 'T';
                        pcNameBuffer[5] = '\0';
    
                        nNameLength = 6;
                    }
    
                    nResult = send(sckConnection, pcNameBuffer, nNameLength + 1, 0);
    
                    // An error has occured while sending server the user's name
                    if (nResult <= 0)
                    {
                        std::cout << "Some error has occured" << std::endl;             
                    }
                    // Good to go
                    else
                    {
                        std::thread Read(Receive, &sckConnection);
                        Send(&sckConnection);
                    }
                }
            }
        }
    
        // cleanup resources
        WSACleanup();
    }
    
    /*
    * [Description]: This method is used only to read messages from server and print them 
    * [Paramaters]:
    * pscktServerSocket - The address of the our connection socket
    * [Return Value]: none
    */
    void Receive(SOCKET* pscktConnection)
    {
        // Variable definition
        int  nReceivedBytes;
        int  nBufferLen = DEFAULT_BUFLEN;
        char pcBuffer[DEFAULT_BUFLEN];
    
        // Code section
    
        // Keep this operation running constantly
        while (true)
        {
            // Read from server -- NO TIME OUT NEEDED
            nReceivedBytes = recv((*pscktConnection), pcBuffer, nBufferLen, 0);
    
            // More than zero bytes received
            if (nReceivedBytes > 0)
            {
                // Set a zero termination to simulate a string
                pcBuffer[nReceivedBytes] = '\0';            
                std::cout << pcBuffer << std::endl;     
            }
            // Server has closed the connection probably
            else
            {
                // TODO - CLOSE CONNECTION          
            }
        }
    }
    
    
    /*
    * [Description]: This method is used only to send messages to the server
    * [Paramaters]:
    * pscktServerSocket - The address of the our connection socket 
    * [Return Value]: none
    */
    void Send(SOCKET* pscktConnection)
    {
        // Variable definition
        int  nSentBytes;
        int  nBufferLen = DEFAULT_BUFLEN;
        char pcBuffer[DEFAULT_BUFLEN];
    
        // Code section
    
        pcBuffer[0] = '\0';
    
        // Keep this operation running constantly
        while (true)
        {
            int nSentBytes = 0;
    
            // Read
            std::cin.getline(pcBuffer, nBufferLen);
    
            // Go through string untill backslash 0
            while (pcBuffer[nSentBytes] != '\0')
            {
                // Increase the number of bytes we want to send
                ++nSentBytes;
            }       
    
            pcBuffer[nSentBytes] = '\0';
    
            nSentBytes = send((*pscktConnection), pcBuffer, nSentBytes, 0);
    
            // An error has occured
            if (nSentBytes == SOCKET_ERROR)
            {
                // TODO - HANDLE ERROR;
            }
        }
    }
    

    `

    ------------------------- 服务器代码 ----
    // Source.cpp
    #include <thread>
    #include <winsock2.h>
    #include <ws2tcpip.h>
    #include <iostream>
    #include "Client.h"
    #include "Connections.h"
    
    #pragma comment (lib, "Ws2_32.lib")
    
    #define NAME_LENGTH    40
    #define DEFAULT_BUFLEN 512
    #define DEFAULT_PORT "8888"
    #define MAX_CONNECTIONS 5
    
    
    // Globals
    Connections* conAllConnections = Connections::getInstance();
    
    bool LoadServerSocket(SOCKET* pscktServerSocket);
    void Dispatcher(SOCKET* pscktServerSocket);
    void SendAll(ClientNode* pclndSenderAddress, char* pcMessageBuffer, int nLength);
    void HandleConnections();
    
    void main()
    {
        // Variable definition
        int       nResult;
        SOCKET    sckServerSocket = NULL;
        WSADATA   wsaData;
    
        // Code section
    
        // Initialize Winsock
        nResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    
        // If winsock dll loading has failed
        if (nResult != 0) 
        {
            std::cout << "Failed loading winsock DLL" << std::endl;
        }
        // DLL loaded successfully
        else
        {
            // If failed loading the server socket
            if (!LoadServerSocket(&sckServerSocket))
            {
                std::cout << "Failed loading the server socket!" << std::endl;
            }
            else
            {
                std::thread  dispatch(Dispatcher,&sckServerSocket);
                //dispatch.join();
                HandleConnections();
            }
        }
    
        // cleanup resources
        WSACleanup();
    }
    
    /*
    * [Description]: This method is used to load and bind server socket into some pointer.
    * [Paramaters]:
    * pscktServerSocket - a pointer variable that we would like to load our socket into the address this pointer
    * is pointing at
    * [Return Value]: A boolean indication of whether our socket was created successfully
    */
    bool LoadServerSocket(SOCKET* pscktServerSocket)
    {
        // Variable definition
        int       nResult;
        bool      bWasServerSocketCreated = false;
        bool      bWasAddressResolved     = false;
        addrinfo  addrAddressFormat;
        addrinfo* paddrFinalAddress       = NULL;
    
        // Code section
    
        // Fil addrAddressFormat with zeros, and set correct settings of our desired socket
        ZeroMemory(&addrAddressFormat, sizeof(addrAddressFormat));
        addrAddressFormat.ai_family = AF_INET;
        addrAddressFormat.ai_socktype = SOCK_STREAM;
        addrAddressFormat.ai_protocol = IPPROTO_TCP;
        //addrAddressFormat.ai_flags = AI_PASSIVE;
    
        // Resolve the server address and port with the address setting into our final address
        nResult = getaddrinfo("10.0.0.5", DEFAULT_PORT, &addrAddressFormat, &paddrFinalAddress);
    
        // If resolving of the address was successful
        if (nResult == 0)
        {
            // Set address resolving bool indication to true
            bWasAddressResolved = true;
    
            // Create server socket
            (*pscktServerSocket) = socket(paddrFinalAddress->ai_family,
                                          paddrFinalAddress->ai_socktype,
                                          paddrFinalAddress->ai_protocol);
    
            // Socket creating was successful
            if ((*pscktServerSocket) != INVALID_SOCKET)
            {
                // Set socket creation indication to true
                bWasServerSocketCreated = true;
    
                //  Bind our socket into our address
                nResult = bind((*pscktServerSocket),
                               paddrFinalAddress->ai_addr,
                               (int)paddrFinalAddress->ai_addrlen);
    
                // In case binding failed
                if (nResult == SOCKET_ERROR) 
                {
                    closesocket((*pscktServerSocket));
                    bWasServerSocketCreated = false;
                }
            }
        }
    
        // Freeing resources
        if (bWasAddressResolved)
        {
            freeaddrinfo(paddrFinalAddress);
        }
    
        return (bWasServerSocketCreated);
    }
    
    /*
    * [Description]: This uses the loaded server socket and handles incoming requests for connections
    * [Paramaters]:
    * pscktServerSocket - a pointer to the loaded server socket
    * [Return Value]: none
    */
    void Dispatcher(SOCKET* pscktServerSocket)
    {
        // Variable definition
        int     nResult;
        char    pcBuffer[NAME_LENGTH];
        DWORD   timeout     = 1500;
        SOCKET  sckClientSocket;
        Client  clntNewClient;
    
        // Code section
    
        // Keep this running constantly
        while (true)
        {
            // Keep this running as long as we have the sufficient amount of connections
            while (conAllConnections->getNumOfConnections() < MAX_CONNECTIONS)
            {
                // Attempt listening on the server socket
                nResult = listen((*pscktServerSocket), MAX_CONNECTIONS);
    
                // Listening was a failure
                if (nResult == SOCKET_ERROR)
                {
                    std::cout << "Failed listening with the server socket" << std::endl;
                    // HANDLE ERROR - TODO
                }
                // Listening was successful
                else
                {
                    std::cout << "Listening...." << std::endl;
                    // Accept a client socket
                    sckClientSocket = accept((*pscktServerSocket), NULL, NULL);
    
                    // Accepting was a failure
                    if (sckClientSocket == INVALID_SOCKET)
                    {
                        std::cout << "Client accepting has failed" << std::endl;
                        // HANDLE ERROR - TODO
                    }
                    // Client was added successfully
                    else
                    {
    
                        setsockopt(sckClientSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
    
                        nResult = recv(sckClientSocket, pcBuffer, NAME_LENGTH, 0);
    
                        // If received a valid username
                        if (nResult > 0)
                        {
                            timeout = 1;
    
                            std::cout << "New Client -> " << pcBuffer << std::endl;
                            clntNewClient.setClientSocket(sckClientSocket);
                            clntNewClient.setIsAdmin(false);
                            clntNewClient.setClientName(pcBuffer);
    
                            setsockopt(clntNewClient.getClientSocket(), SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
    
                            conAllConnections->Add(clntNewClient);
    
                            // Receive until the peer shuts down the connection         
                        }
                    }
                }
            }
        }
    }
    
    /*
    * [Description]: This method forwards a message to all other clients but the client who sent it
    * [Paramaters]: 
    * pclndSenderAddress - The address of the client node who sent the  
    * pcMessageBuffer- a pointer to the message buffer
    * nLength - the length of the message
    * [Return Value]: none
    */
    void SendAll(ClientNode* pclndSenderAddress, char* pcMessageBuffer, int nLength)
    {
        // Variable definition
        int         nError;
        int         nResult;
        Client      clntCurrentClient;
        ClientNode* pclndCurrentNode;
        ClientNode* pclndNextNode;
    
        // Code section
    
        // Set first node
        pclndCurrentNode = conAllConnections->getFirst();
    
        // Go through all connections
        while (pclndCurrentNode != NULL)
        {
            // Save the next node in this phase of the code in order to avoid corruption of memory
            // in case node would be deleted from dynamic allocated memory and we wont be able to call the next val upon it
            pclndNextNode = pclndCurrentNode->getNext();
    
            // Compare addresses, we do not want to forward the message to the sender
            if (pclndCurrentNode != pclndSenderAddress)
            {
    
                clntCurrentClient = pclndCurrentNode->getClient();
    
                // Forward the message
                nResult = send(clntCurrentClient.getClientSocket(), pcMessageBuffer, nLength, 0);
    
                // An error has occured
                if (nResult == SOCKET_ERROR)
                {
                    nError = WSAGetLastError();
    
                    // TODO -- handle later
                }   
            }
    
            // Forward current node
            pclndCurrentNode = pclndNextNode;
        }
    }
    
    /*
    * [Description]: This method handles and receives messages from our clients and forwards them
    * [Paramaters]: none
    * [Return Value]: none
    */
    void HandleConnections()
    {
        // Variable definition
        int         nIndex;
        int         nError;
        int         nRecvLen;
        int         nNameLen;
        int         nRecvbuflen = DEFAULT_BUFLEN;
        char        pcBuffer[DEFAULT_BUFLEN + NAME_LENGTH + 3];
        Client      clntCurrentClient;
        ClientNode* pclndCurrentNode;
        ClientNode* pclndNextNode;
    
        // Code section
    
        // Keep this going constantly
        while (true)
        {
            pclndCurrentNode = conAllConnections->getFirst();
    
            // Go through all connections
            while (pclndCurrentNode != NULL)
            {
                clntCurrentClient = pclndCurrentNode->getClient();
    
                // Save the next node in this phase of the code in order to avoid corruption of memory
                // in case node would be deleted from dynamic allocated memory and we wont be able to call the next val upon it
                pclndNextNode = pclndCurrentNode->getNext();
    
                // Attempt receiving data from client
                nRecvLen = recv(clntCurrentClient.getClientSocket(), pcBuffer, nRecvbuflen, 0);
    
                // An error has occured
                if (nRecvLen <= 0)
                {
                    nError = WSAGetLastError();
    
                    // if not a timeout error
                    if (nError != 10060)
                    {   
                        std::cout << "Client removed" << std::endl;
    
                        // Socket error, remove connection
                        conAllConnections->Remove(pclndCurrentNode);
                    }
                }
                // No error has occured
                else
                {
                    //// The purpose of this part of the code is only to place a [CLIENTNAME]
                    //// prefix within the begining of each message
                    ////--------------------------------////
    
                    // Get client's name length
                    nNameLen = clntCurrentClient.getNameLength();
    
                    nIndex = nRecvLen - 1;
                    // Copy the message some offset forward -- offset is (namn length + 4) 
                    while (nIndex >= 0)
                    {
                        // Copy letter (namelen + 4) times forward
                        pcBuffer[nIndex + nNameLen + 4] = pcBuffer[nIndex];
    
                        // Reduce nIndex
                        --nIndex;
                    }
    
                    pcBuffer[0] = '[';
    
                    nIndex = 0;
    
                    // Place clients name within message
                    while (nIndex < nNameLen)
                    {
                        // + 1 for offset
                        pcBuffer[nIndex + 1] = (clntCurrentClient.getClientName())[nIndex];
    
                        // Increase nIndex
                        ++nIndex;
                    }
    
                    pcBuffer[nIndex + 1] = ']';
                    pcBuffer[nIndex + 2] = ':';
                    pcBuffer[nIndex + 3] = ' ';
    
                    ////--------------------------------////
                    //// No longer adding a prefix code
    
                    SendAll(pclndCurrentNode, pcBuffer, nRecvLen + nNameLen + 4);
                }
    
                // Forward current node
                pclndCurrentNode = pclndNextNode;
            }
        }
    }
    ////////////////////////////////////////////////////
    // Connections.h
    #ifndef CONNECTIONS_H
    #define CONNECTIONS_H
    #include "ClientNode.h"
    
    
    class Connections
    {
        private:
    
            // Data members
            static Connections* _Instance;
            int         nNumOfConnections;
            ClientNode* pclndFirst;
    
            // Ctor
            Connections();
    
        public:
    
            // Methods
            void        Add(Client clntNewClient);
            void        Remove(ClientNode* pclndClientToRemove);
            int         getNumOfConnections();
            ClientNode* getFirst();
    
            // Static methods
            static Connections* getInstance();
    };
    
    #endif
    ////////////////////////////////////////////////////
    // Connections.cpp
    #include "Connections.h"
    
    // Set instance to null
    Connections* Connections::_Instance = NULL;
    
    /* ------- PRIVATE CTOR -------
    * [Description]: This method is the constructor of the Connections
    * [Paramaters]: none
    * [Return Value]: none
    */
    Connections::Connections()
    {
        this->nNumOfConnections = 0;
        this->pclndFirst = NULL;
    }
    
    /*
    * [Description]: This method returns the amount of connections currently within our linked list
    * [Paramaters]: none
    * [Return Value]: The amount of connections
    */
    int Connections::getNumOfConnections(){
    
        return (this->nNumOfConnections);
    }
    
    /*
    * [Description]: This method returns a pointer to the first client node within our connection list
    * [Paramaters]: none
    * [Return Value]: A pointer to the first client node
    */
    ClientNode* Connections::getFirst()
    {
        return (this->pclndFirst);
    }
    
    /*
    * [Description]: This method adds a new client to the linkedlist of clients
    * [Paramaters]: 
    * clntNewClient - The new client struct
    * [Return Value]: none
    */
    void Connections::Add(Client clntNewClient)
    {
        // Create a new client node
        ClientNode* pclndNewClientNode = new ClientNode;
    
        // Set the client node's client
        pclndNewClientNode->setClient(clntNewClient);
    
        // Set the client node's next client pointer to point at the currenly first address
        pclndNewClientNode->setNext(this->getFirst());
    
        // Set the first client pointer to point at the new client node's address ( Push it within the linked list )
        this->pclndFirst = pclndNewClientNode;
    
        // Increase the number of connection
        ++(this->nNumOfConnections);
    }
    
    /*
    * [Description]: This method removes a client from our linked list of connections
    * [Paramaters]: 
    * pclndClientToRemove - The address of the client node we wish to remove
    * [Return Value]: none
    */
    void Connections::Remove(ClientNode* pclndClientToRemove){
    
        // Variable definition
        int         nIndex;
        ClientNode* pclndCurrentNode;
    
        // Code section
    
        pclndCurrentNode = this->getFirst();
    
        // Checking if we need to remove the first node
        if (pclndCurrentNode == pclndClientToRemove)
        {
            // Jump over deleted node
            this->pclndFirst = pclndClientToRemove->getNext();
    
            // Free memory
            delete pclndClientToRemove;
    
            // Decrease amount of connections
            --(this->nNumOfConnections);
        }
        // We do not need to remove the first one
        else
        {
            // Go through all ClientNodes addresses
            for (nIndex = 0; nIndex < (this->nNumOfConnections - 1); ++nIndex)
            {
                // If the next node is the node we wish to delete
                if (pclndCurrentNode->getNext() == pclndClientToRemove)
                {
                    // Set the current node next node to be the next node of the node we wish to delete
                    pclndCurrentNode->setNext(pclndClientToRemove->getNext());
    
                    // free dynamically allocated memory
                    delete pclndClientToRemove;
    
                    // break outside the loop
                    break;
    
                    // Decrease amount of connections
                    --(this->nNumOfConnections);
                }
                // Next node is not the node we whish to delete
                else
                {
                    // Move to the next node
                    pclndCurrentNode = pclndCurrentNode->getNext();
                }
            }
        }
    }
    
    /*
    * [Description]: This method returns the only instance of Connections (SINGLETON PATTERN)
    * [Paramaters]: none
    * [Return Value]: A pointer to the single instance of connection
    */
    Connections* Connections::getInstance(){
    
        // If instance was not instantiated yet
        if (_Instance == NULL)
        {
            // Call CTOR
            _Instance = new Connections();
        }
    
        return (_Instance);
    }
    ////////////////////////////////////////////////////
    // ClientNode.h
    #ifndef CLIENTNODE_H
    #define CLIENTNODE_H
    #include "Client.h"
    
    class ClientNode
    {
        // Data members
        Client   clntClient;
        ClientNode*  pclntNextClient;
    
    public:
    
        // Access methods       
        void setNext(ClientNode* pclndNextNode);
        void setClient(Client clntNewClient);
        Client getClient();
        ClientNode* getNext();
    
    };
    #endif
    ////////////////////////////////////////////////////
    // ClientNode.cpp
    #include "ClientNode.h"
    
    /*
    * [Description]: This method sets the next node our node would be pointing add
    * [Paramaters]: 
    * pclndNextNode - The address of the next node we want this node to point at
    * [Return Value]: none
    */
    void ClientNode::setNext(ClientNode* pclndNextNode)
    {
        this->pclntNextClient = pclndNextNode;
    }
    
    /*
    * [Description]: This method sets the client struct we want our current node to contain
    * [Paramaters]: 
    * clntNewClient - New client
    * [Return Value]: none
    */
    void ClientNode::setClient(Client clntNewClient)
    {
        this->clntClient = clntNewClient;
    }
    
    /*
    * [Description]: This method returns the client instance our node contains
    * [Paramaters]: none
    * [Return Value]: Our client
    */
    Client ClientNode::getClient()
    {
        return (this->clntClient);
    }
    
    /*
    * [Description]: This method returns the next node our node points at
    * [Paramaters]: none
    * [Return Value]: The address of the next node this node is pointing at
    */
    ClientNode* ClientNode::getNext()
    {
        return (this->pclntNextClient);
    }
    ////////////////////////////////////////////////////
    // Client.h
    #ifndef CLIENT_H
    #define CLIENT_H
    #include <WinSock2.h>
    
    #define MAX_CLIENT_NAME_LEN = 40
    
    class Client
    {
        // Data members
        SOCKET scktClientSock;
        char*  szClientName;
        bool   bIsAdmin;
        int    nNameLength;
    
        public:
    
            // Access methods       
            void   setClientSocket(SOCKET scktClientSock);
            SOCKET getClientSocket();
            void   setClientName(char* szClientName);
            char*  getClientName();
            void   setIsAdmin(bool bIsAdmin);
            bool   getIsAdmin();
            int    getNameLength();
    
            // Other methods
    
    };
    #endif
    ////////////////////////////////////////////////////
    // Client.h
    #include "Client.h"
    
    /*
    * [Description]: This method changes the SOCKET data member of the Client class
    * [Paramaters]:
    * _scktClientSock - the new socket client that is being set
    * [Return Value]: none
    */
    void   Client::setClientSocket(SOCKET _scktClientSock)
    {
        this->scktClientSock = _scktClientSock;
    }
    
    /*
    * [Description]: This method retrieves the client's socket
    * [Paramaters]: none
    * [Return Value]: The socket client
    */
    SOCKET Client::getClientSocket()
    {
        return (this->scktClientSock);
    }
    
    /*
    * [Description]: This method changes the client's name
    * [Paramaters]:
    * _szClientName - a zero terminated string that describes the new client's name
    * [Return Value]: none
    */
    void   Client::setClientName(char* _szClientName)
    {
        // Variable definition
        int nIndex = -1;
    
        // Code section
        this->szClientName = new char[41];
    
        // Copy string char per char
        do
        {
            ++nIndex;
            this->szClientName[nIndex] = _szClientName[nIndex];
        } while (_szClientName[nIndex] != '\0');    
    
        // Name length is equal to index
        this->nNameLength = nIndex;
    }
    
    /*
    * [Description]: This method returns a pointer to the first char of the zero terminated client string
    * [Paramaters]: none
    * [Return Value]: a pointer to the string
    */
    char*  Client::getClientName()
    {
        return (this->szClientName);
    }
    
    /*
    * [Description]: This method is used to set whether the client is an admin or not 
    * [Paramaters]:
    * _bIsAdmin - a boolean indication of whether the user is an admin or not
    * [Return Value]: none
    */
    void   Client::setIsAdmin(bool _bIsAdmin)
    {
        this->bIsAdmin = _bIsAdmin;
    }
    
    /*
    * [Description]: This method determines whether the user is an admin or not
    * [Paramaters]: none
    * [Return Value]: A boolean indication of whether the user is an admin or not
    */
    bool  Client::getIsAdmin()
    {   
        return (this->bIsAdmin);
    }
    
    /*
    * [Description]: This method retrieves the client's name length
    * [Paramaters]: none
    * [Return Value]: the name length
    */
    int  Client::getNameLength()
    {   
        return (this->nNameLength);
    }
    

    同样,这是我写的一些非常旧的代码,如果它不是那么好,我深表歉意,但它确实有效......还请注意,我在服务器代码中包含许多不同的模型,每个模型都由以下内容分隔
    ////////////////////////////////////////////////////
    

    关于我可以通过两者之间的服务器实现客户端 - 客户端通信吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38930003/

    相关文章:

    Linux 用户空间 L2 控制协议(protocol)

    javascript - 我应该重新编写游戏服务器、编写转发器还是使用新插件?

    c# - 如何在 C# 中获取服务器和客户端之间的延迟?

    c - `scanf("%*[^\n]%*c")` 是什么意思?

    c - posix_memalign 与 mmap 有何不同

    c - 解释一下这个小 C 程序中的比较

    java - 聊天应用程序如何与其服务器通信?

    c - 在 RTOS 中安排帧的传输

    java - Socket通信,Java客户端C服务器

    android - 用于蓝牙通信的 OutputStream 到 DataOutputStream