d2jsp
Log InRegister
d2jsp Forums > Off-Topic > Computers & IT > Programming & Development > My New Project For Practice > Chatroom Program
12Next
Add Reply New Topic New Poll
Member
Posts: 36,123
Joined: Jul 18 2008
Gold: 2,407.00
Feb 10 2017 06:41am
Hello. Since I recently graduated and started working full time, I've started to feel like my first job isn't giving me the kind of experience that I really need. So I've decided to start a project of writing my own chatroom program. The reason I've decided on this program is because I think it will give me good experience with a wide array of programming concepts that I am not using currently at work. I think this project will touch on the following:

- OOP - I am most likely going to use Java just because it's something I think I need more experience with.
- Socket Programming - obviously, I am going to need sockets to communicate between clients and the server.
- threading - I figure each chatroom will have its own thread.
- SSL - I would like messages to be encrypted
- unit tests - I am going to try to be not lazy about unit testing everything
- client-server architecture - there will be the server-side chatroom program and the client programs
- GUIs - each client program will be a simple GUI
- Documentation - I am going to use JavaDoc for API documentation, and see if there are any good tools for automatically creating other documentation, like install instructions, etc.

Anyway, I am going to use this thread as a blog so I can document this little venture for anyone who is interested. If things go well (and I don't give up), I might try porting the server program to C++ for better performance, or expanding it from being an in-memory program to a program with a persistent database. But I have plenty of DB experience so I'm not in a rush to practice that.
Member
Posts: 36,123
Joined: Jul 18 2008
Gold: 2,407.00
Feb 12 2017 08:19am
I've been going through some of Oracle's Socket documentation as well as looking at some SSL/TLS tutorials for Java. I've been fiddling with some UML diagrams and sequence charts for how the program will hopefully work that I will post once I get everything fleshed out more. In the mean time, this is the road map I think I am going to use. I am going to follow an agile methodology of continuously integrating new features onto the same program.

- Step 1: Create a TLS encrypted echo server.
- Step 2: Create the Chatroom object through which the client sends messages / receives echos
- Step 3: Implement multiple-client functionality
- Step 4: Allow creation of different chat rooms
- Step 5: Add support for chatroom passwords
- Step 6: Create a client GUI

A couple of other things I meant to mention.

- The server program will initially be designed to run on a Linux platform. I know it's Java so it's pretty platform independent, but things like log file locations, key file locations, etc, are going to be for typical linux environments.
- I am going to use ANT to help compile the code. It's an Apache project that is basically a Java Makefile, but it's something I want more practice with.

This post was edited by Mastersam93 on Feb 12 2017 08:20am
Member
Posts: 36,123
Joined: Jul 18 2008
Gold: 2,407.00
Feb 12 2017 02:55pm
I was able to get an encrypted echo server working. The only problem is it needs to use Java's keystore information to avoid SSL handshake errors. I need to get the server and then client to choose/accept certificates during run time, not as command line arguments to java.

Here is the code for the server class. It is still very basic.


Code
/*
* ChatServer.java
*
* Main class for the Chat server program.
*
*/

//import java.util.Hashtable;
import javax.net.ServerSocketFactory;
import javax.net.ssl.SSLServerSocketFactory;
import java.net.*;
import java.io.*;


public class ChatServer {


private final int PORT = 29001;
private ServerSocket ssocket;


public ChatServer() throws IOException {

// Create the server socket.
ServerSocketFactory ssocketFactory = SSLServerSocketFactory.getDefault();
this.ssocket = ssocketFactory.createServerSocket( PORT );

}


public void waitForConnections() throws IOException {

//These do not need to be here.
String msg = "";
Socket s;
PrintWriter out;
BufferedReader in;

while (! msg.equals("quit")){

// Accept connections
s = this.ssocket.accept();

// I/O handlers
out = new PrintWriter( s.getOutputStream(), true );
in = new BufferedReader( new InputStreamReader( s.getInputStream() ) );

// Read a message
msg = in.readLine();

// Repeat it as long as it is non-empty.
if( msg != null ){
out.println(msg);
}
}
}
}
Member
Posts: 13,425
Joined: Sep 29 2007
Gold: 0.00
Warn: 20%
Feb 13 2017 12:48pm
I personally thing you should design and post your protocol before attempting to build the server and client. After all the protocol is basically your entire messaging and communication backbone.

There are a few different kinds out there depending on how complex you want to make it. For example you can use a column based protocol. Example:

Code
USER Protocol
Function | Message | Channel | To-Username
TALK | "hi" | #channel | N/A
WHISPER| "hi" | N/A | "abduct"


More or less you have columns and rows depicting the arguments and function of the packet being sent, deliminated by some separator. New lines are acceptable here. For example a TALK packet might look like "TALK\nHi\n#channel" Then to parse you just break the packet on newlines and access the array of args.

Code
switch args[0]
when "TALK"
talk(args[1], args[2])
when "WHISPER"
etc


Other options are binary packets which tend to be better since you can customize the protocol more. For example you can specifcy the first byte to be the packet ID, next 2 bytes being packet length (to check for truncated/malformed packets), and then a way to extract strings, ints, etc from the packet. For example let 0x50 be "TALK", and the message and channel be strings with the implentation that the first byte is the string length. A packet my look like this:

Code
\x50\x000C\x02Hi\x08#channel


From there you will always get the first byte to determine what packet is going to be parsed, the next two bytes for packet length (sans the length identifier) so that you can discard or handle broken packets, and then parse the message for strings of X bytes in length. Since you know 0x50 is a TALK packet, and it has 2 arguments of type string, you will proceed to read the next byte (0x02) and know that the string is 2 bytes long for the message. After you read the next byte after the message (0x08) to determine you have to read 8 bytes for the channel name. What this sets up is a way for the server to handle arbitrary string lengths without having to look for identifiers such as a nullbyte or a newline as in the previous example.

Just some things to think about before going too balls deep into designing everything.

Edit:: If you need more ideas the bit torrent bencode https://en.wikipedia.org/wiki/Bencode is another simple protocol to implement which handles strings, lists, and dictionaries.

Or I guess you can just transmit JSON strings back and form and map them to JSON mapped classes. That works too and is also advised for OOP languages.

For example in crystal you can do:

Code
class Talk
JSON.mapping({message : String, channel : String})
end

Talk.from_json("{"message" : "hi", "channel" : "#channel"}")

Talk.message
=>"hi"
Talk.channel
=>"#channel"


Where you create a class mapping, then when you parse a json string to it, it will automatically create getters so you can get the contents in an OOP way.

This post was edited by AbDuCt on Feb 13 2017 12:58pm
Member
Posts: 36,123
Joined: Jul 18 2008
Gold: 2,407.00
Feb 13 2017 03:26pm
Quote (AbDuCt @ Feb 13 2017 01:48pm)
I personally thing you should design and post your protocol before attempting to build the server and client. After all the protocol is basically your entire messaging and communication backbone.

There are a few different kinds out there depending on how complex you want to make it. For example you can use a column based protocol. Example:

Code
USER Protocol
Function | Message | Channel | To-Username
TALK | "hi" | #channel | N/A
WHISPER| "hi" | N/A | "abduct"


More or less you have columns and rows depicting the arguments and function of the packet being sent, deliminated by some separator. New lines are acceptable here. For example a TALK packet might look like "TALK\nHi\n#channel" Then to parse you just break the packet on newlines and access the array of args.

Code
switch args[0]
when "TALK"
talk(args[1], args[2])
when "WHISPER"
etc


Other options are binary packets which tend to be better since you can customize the protocol more. For example you can specifcy the first byte to be the packet ID, next 2 bytes being packet length (to check for truncated/malformed packets), and then a way to extract strings, ints, etc from the packet. For example let 0x50 be "TALK", and the message and channel be strings with the implentation that the first byte is the string length. A packet my look like this:

Code
\x50\x000C\x02Hi\x08#channel


From there you will always get the first byte to determine what packet is going to be parsed, the next two bytes for packet length (sans the length identifier) so that you can discard or handle broken packets, and then parse the message for strings of X bytes in length. Since you know 0x50 is a TALK packet, and it has 2 arguments of type string, you will proceed to read the next byte (0x02) and know that the string is 2 bytes long for the message. After you read the next byte after the message (0x08) to determine you have to read 8 bytes for the channel name. What this sets up is a way for the server to handle arbitrary string lengths without having to look for identifiers such as a nullbyte or a newline as in the previous example.

Just some things to think about before going too balls deep into designing everything.

Edit:: If you need more ideas the bit torrent bencode https://en.wikipedia.org/wiki/Bencode is another simple protocol to implement which handles strings, lists, and dictionaries.

Or I guess you can just transmit JSON strings back and form and map them to JSON mapped classes. That works too and is also advised for OOP languages.

For example in crystal you can do:

Code
class Talk
JSON.mapping({message : String, channel : String})
end

Talk.from_json("{"message" : "hi", "channel" : "#channel"}")

Talk.message
=>"hi"
Talk.channel
=>"#channel"


Where you create a class mapping, then when you parse a json string to it, it will automatically create getters so you can get the contents in an OOP way.



There really isn't any protocol for my design. Client sockets are wrapped up into User objects which are going to be contained within a Chatroom object, so all that gets communicated is a single string message to insert into the chatroom. Which channel it goes to is implicated by which Chatroom object the User is a part of.

Functionality like private messages, switching channels with text commands, etc, are not going to be implemented. This is just to practice, I'm not looking to make a production quality product.

/e. But initially I considered a REST style protocol with JSON formatted packets like

Code
{
User: Sam,
Channel: general,
Message: hi
}


This post was edited by Mastersam93 on Feb 13 2017 03:29pm
Member
Posts: 36,123
Joined: Jul 18 2008
Gold: 2,407.00
Feb 18 2017 07:26pm
Been going over TLS in Java to get my desired functionality, and working with Java keystores is just becoming a pain. I am considering two options now:

Option 1: Use the "apache" style of SSL sockets in Java as seen in this tutorial: http://juliusdavies.ca/commons-ssl/ssl.html . The reason why I like this option is because this is more similar to how ever SSL enabled program I've installed and configured works, where you specify certificate files to use as opposed to letting Java handle everything behind the scenes. It also seems like it can't be too hard to modify their client example to allow the client user to verify the server cert over having an accepted cert hard-coded in.

Option 2: Just use a different language, probably python. This would make everything so much simpler, because python, but the whole point of this program is to get practice, so pushing through the harder stuff is valuable. However, we do use python at work, so that is an added benefit.

I suppose I'll try option 1 and move to option 2 if I have to.
Member
Posts: 36,123
Joined: Jul 18 2008
Gold: 2,407.00
Feb 18 2017 09:09pm
So after playing around with it a bit, I think I am going to traditional Java keystore methods and if a certification verification fails then prompt the user if they want to accept the certificate anyway using an all-accepting TrustManager instance. It honestly seems easier to do this than install external implementations of TLS to get the apache TLS libraries working. I found out that with
Code
System.setProperty(property, value)
you can specify Java parameters from within the code so I don't need to invoke Java with special command line parameters.

Java ships with a handful of trusted CA's, so if a certificate from a trusted CA is used the user won't need (I don't believe at least) to import a certificate to a java truststore. So I suppose it makes sense from a security standpoint to enforce the policy that users should only accept certificates from trusted CA's, it just makes testing a pain in the ass since I don't want to pay for a trusted CA signed certificate.

The goal was to have functionality like SSH or WinSCP, where the user is prompted to accept the certificate, but that's a bigger pain in the ass in Java than you might expect.
/e Their SSL libraries are so abstracted it's hard to even know what each class does.

This post was edited by Mastersam93 on Feb 18 2017 09:10pm
Member
Posts: 36,123
Joined: Jul 18 2008
Gold: 2,407.00
Feb 20 2017 05:18pm
I've been working on handling exceptions so the program that I have currently handles disconnects, etc, more gracefully. I think I am going to need to refactor the server program to make it make sense more. Right now I have a driver which instantiates the server socket class that handles connections. I think I should chance to to have a driver that sets system information, then instantiates a server class which handles socket errors, etc, which instantiates a server socket class that does the actual socket handling.
Member
Posts: 36,123
Joined: Jul 18 2008
Gold: 2,407.00
Feb 20 2017 07:29pm
Quote (Mastersam93 @ Feb 20 2017 06:18pm)
I've been working on handling exceptions so the program that I have currently handles disconnects, etc, more gracefully. I think I am going to need to refactor the server program to make it make sense more. Right now I have a driver which instantiates the server socket class that handles connections. I think I should chance to to have a driver that sets system information, then instantiates a server class which handles socket errors, etc, which instantiates a server socket class that does the actual socket handling.


Changed my mind on all of that. Just decided to handle exceptions locally in ChatServer class functions.
Member
Posts: 36,123
Joined: Jul 18 2008
Gold: 2,407.00
Feb 21 2017 06:26pm
So steps 2 and 3 are going to need to be reversed I think. Because of the way I'm going to have a chatroom object handling multiple client sockets, it just makes more sense to implement multi-client functionality first, then wrap it in a chatroom object.
Go Back To Programming & Development Topic List
12Next
Add Reply New Topic New Poll