Mukesh Gupta Mukesh Gupta - 3 months ago 9
C Question

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

I want to send message from client A to client B, through this server. I am not sure how can I get that? One approach I can think of is making a message queue for each client and add message to that queue if someone sends message to that client, and sends from that queue to the respective client ? But I am not sure How can I implement this ? Can anyone help me with this ?

There can be n clients at any moments. In that case broadcast to all clients.

Below is my server code. I have checked username and password of the user.

insidePortal()
function will take care of sending message to other client.

#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]) {

}

Answer

I have this really long code I've wrote in the past that implements something similar to yours, but It's CPP, If you want too I'll post it here, but basically using message queues or multi-process programming seems kinda useless to me, If I were you I'd program it in the following way

Client code -> Two Threads Server code -> Two Threads

Client Has two threads, and Three functions, Connect / Send / Receive

  • Connect - This function handles connection to server, whether you are using TCP it handles the listen-accept or if you use some UDP based made up protocol, it just handles it - makes sure you have some connectin
  • Send - This function sends some data to server
  • Receive This functino receives data from server

The flow of Client would be the following:

  1. Client connects to server on Main thread
  2. After connecting to server Client creates Second thread
  3. On Main thread - Client enters some loop that reads data from user as Input then calls Send function and sends it to server
  4. On Second thread - Client enters some loop that calls Receive to receive data from server and prints it when data is received

that handles Client, now about Server

Server- Has Three functions and Some one Global data structure called Linked - List ( a Linked list obviously ) that would be shared by all it's threads, WaitForConnection Receive SendToAll

  • WaitForConnection- Simply calls the "Accept" function of sockets API ( if you're using TCP ) or if you're using some other made up protocol, this function just blocks it's thread as trying to wait for incoming connection, when some connection arrives, this function registers the connection into the global linked-list of all connections called Connection-List, with the appropriate socket and client data
  • SendToAll - Simply iterates all Connection-List and for every Connection within that list, It sends some data passed
  • Receive just receives some data but sets some timeout first, this is very important in order for Receive not to block for too long!

The flow of Server would be the following:

  1. Main thread creats Second thread
  2. Main thread enters some loop and calls WaitForConnection within it in order to continuously get connections and add them into Connection-List
  3. Second thread enters some loop that iterates over Connection-List, for each connection within Connection-List, receive is called upon the appropriate socket, if some data is receive, SendToAll is called with the data received, if timeout, nothing happens, after that loop is continued and the next loop iteration is executed
    1. SendToAll sends the data to ALL clients within Connection-List

This is some very simple Multi-Client Broadcast with Server architecture It should be really easy to implement! I hope this helps you

I Apologize in advance as this is a code I wrote a while ago so It has a lot of nesting within it, and a lot of comments!

------------------------- CLIENT CODE ----------------

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

`

------------------------- SERVER CODE ----------------

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

Again this is some really old code I've wrote I apologize if it's not so good but It definitely works... also notice that I've contains many different models within the server code, each are seperated by the following

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