I'm trying to create a Chat program for people to talk to each other through. So far I have it so multiple clients can connect to the server and send/receive messages to/from the server. The problem I'm having is because of the way Clients that are connected to the server are stored I can't figure out how to send text to more then just one client at a time....

This is the part of my server that finds clients connecting and stores them.

// |-----------------------| //
// | Check for Connections | //
// |-----------------------| //
    while ( true ){
        SOCKET hClientSocket ;
        struct sockaddr_in clientAddr ;
        int nSize = sizeof( clientAddr ) ;

        hClientSocket = accept( hServerSocket, ( struct sockaddr *) &clientAddr, &nSize ) ;
        if ( hClientSocket == INVALID_SOCKET ){
            cout << "accept( ) failed" << endl ;
        } else {
            HANDLE hClientThread ;
            struct CLIENT_INFO clientInfo ;
            DWORD dwThreadId ;

            clientInfo . clientAddr = clientAddr ;
            clientInfo . hClientSocket = hClientSocket ;
    const char* ConnectionIp;
    ConnectionIp = inet_ntoa( clientAddr . sin_addr );
    
    char dateStr [9];
     char timeStr [9];
     _strdate( dateStr);
     _strtime( timeStr );

            cout << ( "%s", dateStr) << " [" << ("%s", timeStr) << "] - (" << inet_ntoa( clientAddr . sin_addr ) << ") has logged in! " << endl ;
             SaveLog(ConnectionIp, 1);

            // Start the client thread
            hClientThread = CreateThread( NULL, 0,
                ( LPTHREAD_START_ROUTINE ) ClientThread,
                ( LPVOID ) &clientInfo, 0, &dwThreadId ) ;
            if ( hClientThread == NULL ){
                cout << "Unable to create client thread" << endl ;
            } else {
                CloseHandle( hClientThread ) ;
            }
        }

This checks for clients connecting and if the client can connect it sets the client info and creates hClientSocket for that client and creates the thread. It also then displays a message on the server console indicating that the client has connected.

This next section creates the Client's thread and checks for incoming messages from the client. If there is one it displays the message to the sever and the message is stored as szClientMsg. It also checks if the Client entered "quit" and if so it disconnects the client and lets the server know. Near the very end of this is the line that sends the message received in szClientMsg back to the client. I need this message to be sent to everyone and not just the one who originally sent the message.

BOOL WINAPI ClientThread( LPVOID lpData ){
    CLIENT_INFO *pClientInfo = ( CLIENT_INFO * ) lpData ;
    char szClientMsg[ 9999 ] ;
    int nLength ;
    const char* PlayerIp;
    PlayerIp = inet_ntoa( pClientInfo -> clientAddr . sin_addr );
    
    while ( 1 ){
        nLength = recv( pClientInfo -> hClientSocket, szClientMsg, sizeof( szClientMsg ), 0 ) ;
        if ( nLength > 0 ){
            szClientMsg[ nLength ] = '\0' ;
            cout << "(" << inet_ntoa( pClientInfo -> clientAddr . sin_addr ) << ") - " << szClientMsg << endl ;
            SaveMsg(PlayerIp, szClientMsg);

            // Send string back, if its not QUIT
            if ( strcmp( szClientMsg, "QUIT" ) == 0 || strcmp( szClientMsg, "quit" ) == 0 ){                 
                char dateStr [9];
                 char timeStr [9];
                 _strdate( dateStr);
                 _strtime( timeStr );                 
cout << ( "%s", dateStr) << " [" << ("%s", timeStr) << "] - (" << inet_ntoa( pClientInfo -> clientAddr . sin_addr ) << ") has logged out! " << endl; // Player logged out
               SaveLog(PlayerIp, 2);
                closesocket( pClientInfo -> hClientSocket ) ;
                return TRUE ;
            }
            // try sending the data in multiple requests
            int nCntSend = 0 ;
            char *pClientMsg = szClientMsg ;

            while ( ( nCntSend = send( pClientInfo -> hClientSocket, pClientMsg, nLength, 0 ) != nLength ) ){
                if ( nCntSend == -1 ){
                    cout << "Error sending the data to " << inet_ntoa( pClientInfo -> clientAddr . sin_addr ) << endl ;
                    break ;
                }
                
                if ( nCntSend == nLength )
                    break ;

                pClientMsg += nCntSend ;
                nLength -= nCntSend ;
            }
// Send data to client
send(pClientInfo -> hClientSocket, szClientMsg, 999, 0);

        } else {
            cout << "Error reading the data from " << inet_ntoa( pClientInfo -> clientAddr . sin_addr ) << endl ;
        }
    }
    return TRUE ;
}

The problem I'm having is since this creates a thread/socket for each Client as they connect I can't figure out how to send a message to everyone connected at once. I know I have to change the part of the send() code that is

pClientInfo -> hClientSocket

since that is setting it to just one Client, I just don't know what I need to change it to so it works properly.

Thx in advance for any help and if you need more information just tell me.

A short answer ... if you want to have a separate thread for each client connection, then when you receive an incoming message from one client, you need to communicate to each of the other threads so they can send it out.

Unless your server process needs to do something besides service client connections, it may be simpler to skip the threading and use the select() function to wait for input on all of the client connections at once, loop over the connections on which there is input, and for each of those, get the input and then loop over all of the connections internally to send the message out to all the clients.

I know I have to communicate with all of the other client threads connected. What I don't know and need help with is HOW to communicate with all of the client threads to send the message out..... Also I would like to keep the server the way it is now because I need it like this for some other things the Server will be doing.

This is an observer pattern problem. http://sourcemaking.com/design_patterns/observer Therefore you need an observer, which keeps a list of all dependents. When an update is received observer loops through and updates all dependents.

Using one thread per client is not going to scale very well if you intent to have many chatters. You may want to consider using IO completion ports in the windows platform (not trivial to implement) http://msdn.microsoft.com/en-us/magazine/cc302334.aspx. You also will simplify your problem by designing in terms of objects. (Chat, Listener, Observer, Connection etc)

For now lets assume one client one thread. A possible solution is as follows.

1. When client is created, register socket with observer
2. When client is terminated, unregister with observer
3. Each client thread reads message and places message in queue
4. The Observer waits for event in queue and when it receives message updates all dependents

Have fun, you will learn a lot if you can take this little project to completion!!!

Edited 5 Years Ago by template<>: n/a

Would you be able to provide me with a code example for how to do this? I understand what is needed to be done, I just don't understand how exactly to code it.

If I can see a code example of what is needed to be done I will be able to understand it a lot more and have a better chance of getting it to work then just reading pages of text about the function itself.

Are you familar with C++ and OO programming such as classes, objects etc?

Edited 5 Years Ago by template<>: n/a

Oh sorry, I meant I'm not that familiar with OO programming, not C++...

Also here is the ClientInfo part of the code.

struct CLIENT_INFO
{
    SOCKET hClientSocket ;
    struct sockaddr_in clientAddr ;
} ;

Edited 5 Years Ago by Ertzel: n/a

Ok let design first; below is a high level description of the design, Depending on your needs you may want it differently, so feel free to take these thoughts and convert to your own. Try to picture each of the classes and the interactions in your mind until you’re one with the design. Adjust and tweak as needed.

Listener: This class listens to incoming client connections and creates an instance of Chatter for each connection. You already have implementation for Listener, try to encapsulate it in a class.

Chatter: This class sends and receives chat messages from each client. Once again you have the implementation, convert it into a class. Messages it receives are puts in a common Queue for distribution to other Chatters.

ChatApp: This is the main class that contains all the other classes and coordinates the interactions. ChatApp creates the Listener and has a list of IChatters. It also reads the Queue and distributes messages to all other Chatters.

IChatter: Interface to Chatter. The interface has a send method that ChatApp uses to distribute messages to other Chatters. Chatter class is derived from IChatter. (class Chatter: public IChatter)

Class IChatter
{
public:
   ~IChatter(){;}
   virtual void sendMessage(char *message_) = 0;
};

IChatApp: Interface to ChatApp used by Chatters to register and unregister them selves as they are created and destroyed. (class ChatApp:public IChatApp)

class IChatApp
{
public:
    ~IChatApp(){;}
        virtual void registerChatter(IChatter *chatter) = 0;  // add to list
        virtual void unregisterChatter( IChatter *chatter) = 0; // remove from list
};

So from main() one or more ChatApps are created. Each ChatApp represents a group of Chatters.


Queue: Each ChatApp contains a single Queue. The Queue is passed to the Listener and when a Chatter is created, the Queue is passed to each Chatter. When Chatter receives an incoming message it is placed in Queue. The Queue is read by ChatApp and message distributed to all other Chatters using the IChatter interface. Since multiple threads are reading and writing from this Queue, it requires locks.

ChatterList: A list of IChatters maintained by ChatApp. When IChatApp register and unregister is called, items are added and removed from this list. Since multiple threads can register and unregister, locks are required.

Edited 5 Years Ago by template<>: n/a

This article has been dead for over six months. Start a new discussion instead.