Lesson 8 - Java Server - Communication Protocol
Today we're going to design a protocol that the client and server will use to
communicate. So far we have been sending text messages, but unknowingly we used
to send a complete object, not just text. We were able to send text because the
String
class can be serialized and sent over the network.
Sending Objects
Java has the ability to send entire objects over the network. We'll use this
feature to implement our communication protocol. We use
ObjectInputStream
and ObjectOutputStream
classes to
send and receive objects. All objects to be sent must contain the
serialVersionUID
constant. This is a unique number according to
which objects will be rebuilt on the other side of the line. Usually the IDE has
the option to generate this number. In addition, all of these objects must
implement the Serializable
interface. This is only a markup
interface, so it doesn't require any methods to be implemented. It's information
to JVM that such objects can be serialized and sent over the network.
Communication Protocol
All the classes we're going to create will be in the share
module. This is to make them accessible for both client
and
server
modules. In the share
module, in the
src/main/java/
folder, create a package that represents the whole
project, in my case it's: cz.stechy.chat
. In this package, create a
net.message
package to hold all classes for the client-server
communication. In this package, we'll create an IMessage
interface
to represent the message itself. We'll let the interface be inherited from
Serializable
so that we don't have to implement it in concrete
classes and be sure that the class can be sent through
ObjectOutputStream
.
public interface IMessage extends Serializable { String getType(); Object getData(); default boolean isSuccess() { return true; } }
The interface contains three methods:
getType()
- returns the message type; each type will by represented by one classgetData()
- returns the data that the message carriesisSuccess()
- Whether the requested action performed correctly or failed
We'll create a simple implementation of the IMessage
interface
to be able to send text messages again:
public class TextMessage implements IMessage { public static final String MESSAGE_TYPE = "text"; private final String data; public TextMessage(String data) { this.data = data; } @Override public String getType() { return "text"; } @Override public Object getData() { return data; } }
Protocol Implementation
Since we've defined the base class that we'll send over the network, we need
to replace the code where we send or receive Object
data to the
IMessage
data type.
The interfaces where we have to change the data type are:
IClient
void sendMessageAsync(IMessage message);
void sendMessage(IMessage message) throws IOException;
IWriterThread
void sendMessage(ObjectOutputStream writer, IMessage message);
Also modify the implementation of these methods in the corresponding classes according to the interfaces.
In the Client
class it's necessary to update the message
receiving:
IMessage received;
while ((received = (IMessage) reader.readObject()) != null) {}
Now we no longer accept the Object
class, but the
IMessage
interface. The type casting is appropriate here, because
our protocol is based on the idea that all the objects we send will have a
common interface - IMessage
.
In the ClientDispatcher
class we have to modify the message
sending using the TextMessage
class:
client.sendMessage(new TextMessage("count: " + count));
Functionality Test
Finally, let's see if our work has been successful so far and test whether the server is able to communicate with the client.
In the client
module we'll create the corresponding package
again and a SimpleClient
class in it. The class will have only a
main()
method to establish a connection to the server, send data,
wait for a response, and end the connection:
public class SimpleClient { public static void main(String[] args) throws Exception { Socket socket = new Socket("localhost", 15378); Thread.sleep(1000); ObjectOutputStream writer = new ObjectOutputStream(socket.getOutputStream()); writer.writeObject(new TextMessage("Hello from client.")); writer.flush(); ObjectInputStream reader = new ObjectInputStream(socket.getInputStream()); System.out.println(((IMessage) reader.readObject()).getData().toString()); socket.close(); } }
Start the server first and then the client. The result should be a "Hello from client" message.
That would be all for today's shorter lesson.
Next time, in Java Server - Event bus, we'll implement a simple event bus.