Thomas
Thomas

Reputation: 1706

tcp server with multiple clients sending message back to all connected clients

I have a tcp chat program: server.c and client.c.

The server is in a while(1) loop and uses select to detect clients wanting to connect on it's socket. A new thread is then created for the accepted client and its socket descriptor is given as an argument for thread: pthread_create (&thread,NULL, do_something, (void *) &socket_descriptor);

When receiving a message from a client, the server should send this message to all connected clients. (not implemented this yet).

Now I'm wondering how to do this. I absolutely need each accepted connection to be in a thread.

I was thinking of using a select inside the do_something as well; will select detect if data is incoming on the socket descriptor? Or would you do it another way?

edit: added code my code:

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include "tcp_comm.h"
#include <sys/time.h>
#include <sys/types.h>

#define BUFSIZE 1024
#define PORT 1234

void *do_something(void *a);

int main (void){
    Socket server = tcp_passive_open( PORT );
    MySocket *s = (MySocket *)server;
    printf("Server socked_id (main): %i", s->sd);

    pthread_t thread;

    fd_set active_socketDescriptors,read_socketDescriptors;

    FD_ZERO(&active_socketDescriptors);         
    FD_SET(s->sd,&active_socketDescriptors);    

    while (1){
        read_socketDescriptors = active_socketDescriptors;
        if (select (FD_SETSIZE, &read_socketDescriptors, NULL, NULL, NULL) < 0){
            perror ("select"); 
            exit (EXIT_FAILURE);
        }

        int i;
        for (i = 0; i < FD_SETSIZE; ++i){
            if (FD_ISSET (i, &read_socketDescriptors)){
                if (i == s->sd){
                    Socket client = tcp_wait_for_connection( server ); 
                    pthread_create (&thread,NULL, do_something, (void *)client); 
                    FD_SET (s->sd, &active_socketDescriptors);          
                } else {
                }
            }
        }
    }

    tcp_close( server );
    return 0;

}
void *do_something(void *client){
    unsigned char input[BUFFER_SIZE]; 
    pthread_detach(pthread_self());

    MySocket *s = (MySocket *)client;
    printf("Client socked_id (thread): %i", s->sd);
    int j;
    while (1){
        int nbytes = tcp_receive(client, input, BUFSIZE );
        if (nbytes <= 0){
                if (nbytes ==0){
                    /* connection closed by client*/    
                    printf("Client closed connection");         
                } else {
                    /* other error*/
                    perror("tcp_receive");              
                }
                tcp_close(&client);
                /*remove the socket descriptor from set in the main BRAINSTORM ABOUT THIS */
        } else {
            /*data incoming */
            printf("\nMessage from client: %s",input);
        }
    }
    return 0;
}

edit 2: reformulation of problem I have to use threads (it not because of the system; linux) but because it's mandatory in the assignment to have a thread for each client.

The problem i have specifically is that only the main thread can send the data recieved in each thread from each client to all clients because only the main thread has access to the set which contains the socket descriptors.

edit3: what I need to add in each thread but I can't because of the s.thread and s.main being in different places & the thread not knowing the set of the main.

for (j=0; j<=FD_SETSIZE;j++){
    if(FD_ISSET(j,&active_socketDescriptors)){
        if (j != s.thead && j!=s.main){
            tcp_send(j, (void*)input,nbytes);
        }       
    }   
}

edit 4: I solved it this way: i have a dynamic array list where i put a list of connected clients with there socket descriptor. Inside the thread of the server (do something) I have the recieve blocking until it gets input then this input is send to all connected clients using there socket descriptor from the list which it loops trough. Inside the clients there is a thread listening and a thread sending.

Upvotes: 3

Views: 9120

Answers (3)

Houcheng
Houcheng

Reputation: 2894

This is related to your design.

If you only need to do one or two features for each connected client, then suggest you to use only one thread to implement your server.

If you has to do lots of features for each connected client, then multiple thread design is okay. However, the question you asked should be how did I passing the data from receiving thread to all others. The suggested answer from me is ether:

a) use message queue to passing inter thread data: each thread has one message queue and each thread will listen to its own socket and this message queue. When receiving data from socket, the thread sending the data to all other message queues

b) use an single global buffer: if has any incoming data form socket, put this data into this global buffer and adding a tag to this data indicating where this data comes from.

my 2 cents.

Upvotes: 0

idz
idz

Reputation: 12988

Given your description it might be worth rethinking the architecture of your application. (Unless this has been dictated by limitations on your system). Let me explain this a little more...

By your description, if I understood you correctly, after a client has connected to the server any messages it (the client) sends will be relayed (by the server) to all other clients. So, rather than creating a new thread why not simply add the newly connected socket to the FDSET of the select. Then when a message comes in you can simply relay to the others.

If you expect a large number of clients for a single server you should see if the poll system call is available on your system (it's just like select but supports monitoring more clients). A good poll/select version ought to out-perform your threaded version.

If you really want to continue with your threaded version here's one way to accomplish what you are trying to do. When you create the thread for each accepted client you also create a pipe back to the server thread (and you add this to the server select/poll set.) and pass that to the client thread. So your server thread now not only receives new connections but relays the messages too.

Although you said that you absolutely must deal with each client in a separate thread, unless you are using a real time operating system, you will probably find that the thread context-switch/synchronization you need to do will soon dominate over the multiplexing overhead of the first solution I suggested. (But since you did not mention an OS I am guessing!)

Upvotes: 1

Some programmer dude
Some programmer dude

Reputation: 409176

If the client connection sockets are non-blocking, then using e.g. select to wait for the socket receive data is a possible way. However, since you already have the connected sockets in threads, you can keep them blocking, and just do a read call on them. The call to read will block until you receive data, which can then be spread to the other threads.

Edit

After better understanding your requirements, you should probably have the sockets non-blocking, and use a loop with select with a short timeout. When select timeouts (i.e. returns 0) then you check if there is data to send. If there is, then send the data, and go back to the select call.

Upvotes: 3

Related Questions