views:

832

answers:

3
+2  Q: 

Java Chat Server

I am writing a java based chat server and currently my design is based on following :- when a person in a chat room sends a message, the chatroom class at the server side, sends the same message to every participant in the room in a loop. Obviously , this is a poor design because networks calls are being made to individual participants in a loop. Hence, for example, consider there are 10 people in a chat room. When one user sends a message, the chatroom class will send the same message in a loop to all 10 people. if lets say, the 5th person in a loop has a crappy connection, the time when the sixth .. 10th person will see the message will be affected.

if i move from unicast to multicast per room, then how do i get a private multicast group ip per chat room? Also, it seems overkill to have individual groups per chat room. One of the main problem is that when i replied to users in a room via a loop, the method that sent data over socket connection was blocking. Hence, i am thinking if i use non blocking NIO sockets, and then send the message to recipients in a loop, would that solve the problem ? Are there other clever tricks that can be done to optimize sending of data to recipients in the room?

+3  A: 

The simple implementation is to use two threads per client. One thread to read from the socket the other to write to the socket. If you have few clients this will be fine. You will have to get to know NIO to handle many of clients. ('many' meaning when the threaded model does not work well.)

The Client's reading thread reads an entire message from the socket and puts it on a queue in the ChatRoom object. The chat room has a thread that takes messages off the queue and puts them on the Client's queue. The clients writing thread polls its queue and writes the message to the socket.

The ChatRoom has a thread to accept connections and create Client objects and puts them in a Collection. It has another thread to poll its message queue and distribute the messages to the Client queues.

Apache Mina has an example using NIO

Clint
With 2 threads per client and both threads polling and sleeping, the overhead of context switching and thread scheduling by VM will grow linearly with number of clients. I will liket to deploy it on a commodity hardware such as a dual core processor, i think vm scheduler will keep CPU busy with mostly avoidable work
The only number you mention as clients is 10. In my experience you will not have a problem handling 10 chatters with just about any hardware.
Clint
@Maninder Batth have you seen http://mailinator.blogspot.com/2008/02/kill-myth-please-nio-is-not-faster-than.html
grom
Setting a tiny stacksize (e.g. 32k, the minimum on Windows) really helps the threaded model. That's 64k per connection or just 640mb for 10,000 concurrent connections! That's la fraction of 1gb in a world where a Vista laptop requires more than 2gb of ram to run properly.
Seun Osewa
+2  A: 

I agree that serially looping over your recipients would be a bad idea. For this, you could consider using a ThreadPool to help. However, I would think that Multicast would be your best bet. It is well suited to the chatroom model. You would only need to send once and your iterative approach will be solved. You can get a unique group id by specifying a different port in your address.

akf
I think Java's Multicast supports only UDP, which means the server cannot tell if message delivery is successful without some kind of ACK from the recipient.
Zach Scrivena
i am trying to make it a very generalized product and it may be possible that some client networks may not support multicasting. Hence, i am being hesitant to go with multicasting approach
@Zach Scrivena: I agree, the trade-off for the efficiency of Multicast is a loss of data integrity (data ordering and potential loss). However, for something like a chat room, I see that as a viable trade.
akf
+2  A: 

The simple approach is to use two threads per client connection. One thread handles reading messages from the client the other for sending messages, thereby can send/receive messages from the client simultaneously.

To avoid network calls when looping over the client connections to broadcast a message, the server thread should add the messages into a queue to send to the client. LinkedBlockingQueue in java.util.concurrent is perfect for this. Below is an example:

/**
 * Handles outgoing communication with client
 */
public class ClientConnection extends Thread {
    private Queue<String> outgoingMessages = new LinkedBlockingQueue<String>(MAX_OUTGOING);
    // ...
    public void queueOutgoing(String message) {
        if (!outgoingMessages.offer(message)) {
            // Kick slow clients
            kick();
        }
    }

    public void run() {
        // ...
        while (isConnected) {
            List<String> messages = new LinkedList<String>();
            outgoingMessages.drainTo(messages);
            for (String message : messages) {
                send(message);
            }
            // ...
        }
    }
}

public class Server {
    // ...
    public void broadcast(String message) {
        for (ClientConnection client : clients) {
            client.queueOutgoing(message);
        }
    }
}
grom
So the number of threads per server will aprox be :- number of people * 2. Lets say, i want to serve at the least 1000 people per chat server. Also, i would like atleast 5 servers per hardware machine. Also assume the hardware is dual core server.Hence, 2000 threads per server and 10k threads per 5 server on a dual core machine. with atleast 50% threads active at any time, 5k threads per dual core, i see there will be too much time spent in context switching and thread scheduling. Any comments ?
NPTL can handle a lot of connections. Refer to http://mailinator.blogspot.com/2008/02/kill-myth-please-nio-is-not-faster-than.html
grom