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

Can I implement client - client communication through server in between?

我想通过此服务器从客户端 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,如果你也想要,我会 post 它在这里,但基本上使用消息队列或多进程编程对我来说似乎有点没用,如果我是你我会用下面的方式编程

客户端代码 -> 两个线程 服务器代码 -> 两个线程

Client 有两个线程,三个函数,Connect / Send / Receive

  • Connect - 此函数处理与服务器的连接,无论您使用的是 TCP,它都会处理 listen-accept,或者如果您使用一些基于 UDP 的组合协议,它只会处理它 - 确保您有一些连接
  • Send - 此函数向服务器发送一些数据
  • Receive 这个函数从服务器接收数据

Client 的流程如下:

  1. Client 连接到 Main thread
  2. 上的服务器
  3. 连接到服务器后 Client 创建 Second thread
  4. Main thread - Client 进入某个循环,从用户那里读取数据作为输入,然后调用 Send 函数并将其发送到服务器
  5. Second thread - Client 进入某个调用 Receive 的循环 从服务器接收数据并在接收到数据时打印它

处理 Client,现在大约 Server

Server- 具有三个函数和一个全局数据结构,称为 Linked - List(显然是一个 Linked list),它将被所有线程共享,WaitForConnection Receive SendToAll

  • WaitForConnection- 只需调用套接字 API 的 "Accept" 函数(如果您使用的是 TCP)或者如果您使用的是其他一些组合协议,则此函数只是在尝试等待传入连接时阻塞它的线程,当某个连接到达时,此函数将连接注册到名为 Connection-List 的所有连接的全局链表中,并使用适当的套接字和客户端数据
  • SendToAll - 简单地迭代所有 Connection-List 并且对于该列表中的每个连接,它发送一些传递给
  • 的数据
  • Receive只是接收了一些数据,但是先设置了一些超时,为了Receive不会阻塞太久,这很重要!

Server 的流程如下:

  1. Main thread 创建 Second thread
  2. Main thread 进入某个循环并在其中调用 WaitForConnection 以不断获取连接并将它们添加到 Connection-List
  3. Second thread 进入循环遍历 Connection-List,对于 Connection-List 中的每个连接,在适当的套接字上调用接收,如果接收到一些数据,SendToAll用接收到的数据调用,如果超时,则什么也不会发生,在继续循环并执行下一个循环迭代之后
    1. SendToAll 将数据发送到 Connection-List
    2. 内的所有客户端

这是一个非常简单的Multi-Client Broadcast with Server架构,应该很容易实现吧!希望对您有所帮助

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

------------------------ 客户端代码------------ ---

// 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] = '[=10=]';
            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] != '[=10=]')
                {
                    ++nNameLength; 
                }

                // If invalid name
                if (nNameLength == 0)
                {
                    pcNameBuffer[0] = 'G';
                    pcNameBuffer[1] = 'U';
                    pcNameBuffer[2] = 'E';
                    pcNameBuffer[3] = 'S';
                    pcNameBuffer[4] = 'T';
                    pcNameBuffer[5] = '[=10=]';

                    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] = '[=10=]';            
            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] = '[=10=]';

    // 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] != '[=10=]')
        {
            // Increase the number of bytes we want to send
            ++nSentBytes;
        }       

        pcBuffer[nSentBytes] = '[=10=]';

        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] != '[=11=]');    

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

同样,这是我写的一些非常旧的代码,如果它不是很好,我深表歉意,但它确实有效...还要注意,我在服务器代码中包含许多不同的模型,每个模型都由正在关注

////////////////////////////////////////////////////