This week's book giveaway is in the OO, Patterns, UML and Refactoring forum. We're giving away four copies of Refactoring for Software Design Smells: Managing Technical Debt and have Girish Suryanarayana, Ganesh Samarthyam & Tushar Sharma on-line! See this thread for details.
I made a very simple, easy to use, chat application for LAN that can communicate without a server and at the same time, provide the users with a user list so that they can see who's connected and who is not. The reason why I do not want to have a central server is because my home network does not have a computer that can act as a server all the time. So, the only way I can think of to implement this program is to make the application both a server and a client. The program runs a thread for a ServerSocket to wait for a connection, and uses a MulticastSocket and joins a multicast group to broadcast its presence on the network. When the same program on other machines receive the message, they will make a Socket connection to the one that broadcasted its presence. Here's a simple illustration:
1. LAN Chat "A" is running on a machine while LAN Chat "B" is on a different machine. Both of them are listening on the same multicast address and port and runs a server socket.
2. "B" sends a broadcast packet containing the port number of its ServerSocket.
3. "A" receives the message and connects to "B"s ServerSocket on the port number specified in the packet.
4. "A" and "B" are now connected. Now another LAN Chat, "C", starts on another machine, and broadcasts it's ServerSocket port to the multicast group.
5. "A" and "B" receive the packet and connect to "C"s server socket on the port specified in the packet.
6. "A", "B", and "C" are now connected.
The problem here is that datagram packets sent over a MulticastSocket does not have a guaranteed delivery. Let's say another LAN Chat, "D", starts on another machine and broadcasts its ServerSocket port to the multicast group. "A" and "B" received the packet but "C" didn't. So "A" and "B" connects to "D". "D" will never know that "C" exists because "C" did not receive "D"s packet.
To solve the problem, I made the application tell everyone in its list that a new user exists everytime it makes a successful connection to the new user. So here's what happens...
1. LAN Chats "A", "B", and "C" are all connected to each other when "D" starts on another machine.
2. "D" broadcasts its ServerSocket port number to the multicast group.
3. "A" receives the broadcast message and connects to "D" on the port number specified in the message. "A" then tells "B" and "C" that LAN Chat "D" exists. "A" gives "B" and "C" the IP address and port number of "D", so that they can connect to "D".
Now I solved the problem of the case of an undelivered datagram packet. "D" will now be able to know the presence of "C". The only problem now is that there will be unimportant messages that will be sent over the network, because every time a successful connection is made, the LAN Chat tells the other LAN Chats that a new LAN Chat exists.
However, the program works fine. But I'd like to know if there's a more proper way of implementing a serverless LAN Chat with user list. I want to get rid of the unecessary messages sent over the LAN.
Depends on whether you are designing for a large number of clients or a small number of clients. If you have a large number of clients, you could have every client broadcast itself every few minutes. With your current solutions, if you have a N clients, you will have N*(N-1) messages going back and forth when the N+1 client joins in. It might be cheaper for all N clients to do a broadcast every 5 minutes or so
Alternatively, you can have one of the nodes be the "master". The nodes can spin a random number to decide whether they should be the master. If they decide to be master, they broadcast a message. From this point on, the master is responsible for broadcasting a master list of clients every few minutes. Once all the clients see someone has decided to be the master, they stop spinning the random number. If they don't hear from the master every few minutes, they will go back to a mode where they spin the random number again. This implementation is probably the most lightweight, but most difficult to implement. You will have to decide what will you do if 2 nodes decide to be the master?how do you handle the "split brain" problem? for example: 4 nodes, A, B, C, D. A and B are in europe, C and D are in US. A is the master. Something happens and there is a disconnection between europe and america. So, C and D don't hear from A. So, they decide C is the master. Now, you have 2 masters, A and C, both of them don't know that there is another master. In few minutes, through the magic of internet, the connection is established again. Now what do you do? Should A and C try to find each other again? or do you live with the split brain.
Besides this, you probably want to think about how you want to handle very large number of clients, if you are going that route. Keeping disconnections aside. Everyone talking to everyone is a O(N^2) solution, which won't scale well. The beautiful thing about a client-server architecture is that it's an O(N) solution. Obviously the problem with client server architecture is that the server is your failure point, which I guess you really want to avoid.
I believe there are protocols like ToR that have solved this problem. I think ToR arranges the nodes in a tree like fashion.. although I might be wrong.