views:

72

answers:

2

For college, I am developing a local relay chat. I have to program a chat server and client that will only work on sending messages on different terminal windows on the same computer with threads and fifos.

The fifos part I am having no trouble, the threads part is the one that is giving me some headaches.

The server has one thread for receiving commands from a fifo (used by all clients) and another thread for each client that is connected.

For each client that is connected I need to know a certain information. Firstly, I was using global variables, which worked as longs as there was only one client connected, which is much of a chat, to chat alone.

So, ideally I would have some data like:

-nickname
-name
-email
-etc...

per client that is connected. However, I don't know how to do that.

I could create a client_data[MAX_NUMBER_OF_THREADS] where client_data was a struct with everything I needed to have access to, but this would require to, in every communication between server and client to ask for the id of the client in the array client_data and that does not seem very pratical

I could also instantiate a client_data immediately after creating the thread but it would only be available in that block, and that is not very pratical either.

As you can see I am in need of a little guidance here. Any comment, piece of code or link to any relevant information is greatly appreciated.

Thanks.

+3  A: 

I don't know what language you're using but here are some basic ideas:

  • Start your server in a thread (possibly your main thread).
  • The server's while loop will block on accepting a socket connection.
  • When the socket connection is accepted it should spawn a new thread to handle the connection.
  • Start communicating with the client in the new thread.

A simple socket server loop looks like this (in Java):

while(true){
    ClientWorker w;
    try{
      //server.accept returns a client connection
      w = new ClientWorker(server.accept(), textArea);
      Thread t = new Thread(w);
      t.start();
    } catch (IOException e) {
      // log the exception or something...  
    }
  }

If you're wondering what it does- the ClientWorker is available here. In C# if you're creating a new Thread don't forget to set its IsBackground property to true so the thread closes when your app shuts down, i.e. no hanging threads.

Remember: accepting a socket connection or receiving data from a socket is usually a blocking call, which means that your thread will block until somebody connects to the socket or data comes through the socket.

In C#:

  1. The Chat Client: http://www.geekpedia.com/tutorial239_Csharp-Chat-Part-1---Building-the-Chat-Client.html
  2. The Chat Server: http://www.geekpedia.com/tutorial240_Csharp-Chat-Part-2---Building-the-Chat-Server.html
  3. A Basic Client/Server: http://www.dreamincode.net/forums/topic/33396-basic-clientserver-chat-application-in-c%23/

In Java:

  1. Chat Client/Server: http://pirate.shu.edu/~wachsmut/Teaching/CSAS2214/Virtual/Lectures/chat-client-server.html
  2. Nakov Chat Client/Server: http://inetjava.sourceforge.net/lectures/part1_sockets/InetJava-1.9-Chat-Client-Server-Example.html

In C++

  1. On Code Project: http://www.codeproject.com/KB/cpp/chat_client_server.aspx
  2. Another Code Project TCP/IP chat client/server: http://www.codeproject.com/KB/IP/ipchat.aspx

Update

Instead of doing global variables, just define a struct for the client account and declare an account variable for each user... here is how you can define the account information:

struct account {
   char nickname[32];
   char first_name[32];
   char last_name[32];
   char e_mail[32];
   char password[32];
};

When the client sends a message it should have a standard format: FROM|TO|CONTENT

struct message{
   char nickname_from[32];
   char nickname_to[32]; // optional
   char msg_content[256];
};

Put each message on the fifo [queue] and you will have all the information you need to identify who sent it.

Lirik
I appreciate your response, but I have to implement this without sockets. Just fifos and threads. It is a LOCAL relay chat.
nunos
@nunos, it's the same concept with blocking i/o: instead of blocking on a socket you'll be blocking on the read from wherever you're reading. You still need to create a new thread for each "connected" client and establish a dedicated i/o channel of communication with the client.
Lirik
Thread-per-client obviously doesn't scale though, so if you wanted a very robust server you would use non-blocking IO with something like select() to monitor multiple sockets from a single thread. But for something simple then thread-per-client is fine.
Mike Weller
+1  A: 

Here is some psuedo code that might actually almost run. Note, I'm not freeing what I allocate, I'm not checking errors, I'm just trying to demonstrate how to pass a structure to a thread and use a simple mutex.

There is one thing to be careful of, The function pointer for threads specifies a void * argument, which could be literally any kind of type. In the thread, we assume that its safe to cast the thread argument to a type that we've defined for use. If you are passing multiple possible types, you have to be careful :)

I'm not quite sure about the structure of your program, or how you are handling threads, but here's a short approach-agnostic example on how to pass data to them:

typedef struct client {
    char *firstname;
    char *lastname;
    char *email;
    char *nickname
} client_t;


pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;

void *client_thread(void *threadarg)
{
    client_t *client = (client_t *) threadarg;

    pthread_mutex_lock(&client_lock);
    /* read and write to client structure */
    pthread_mutex_unlock(&client_lock);

    /* do some other stuff */
    pthread_exit(NULL);
}

int main(void)
{
    client_t *client;
    pthread_t cthread;

    client = (client_t *)malloc(sizeof(struct client));
    if (client == NULL)
      return 1;

    client->firstname = strdup("Joe Public");
    /* set the rest of the data */

    pthread_create(&cthread, NULL, (void *)client_thread, (void *)client);
    /* join / detach / etc */

    /* Free all the structure members and the structure itself */

   return 0;
}

I'm pretty sure this is what you were asking?

Tim Post
Yours and Lirik response has given me some insight on how to approach this. I now have an idea to start implementing the threads. Thanks.
nunos