Building a chat client/server system with Netty in under 15 minutes.
Articles,  Blog

Building a chat client/server system with Netty in under 15 minutes.


Hi, in this screencast I’ll be showing you
how to create a simple chat application using Netty in under 15 minutes. First we’ll need to create a new Java project,
and I’m going to use Maven for this. For this screencast I’ll be using the netty-all
library. And to avoid some runtime warnings I’m also
adding the javassist library. Also since we’re not living in the dark ages
anymore, I want Maven to use the latest Java 1.7 for this project. Alright, now we’re ready to get started. Let’s create a new class called the ChatClient,
and we’ll need to assign it a new package. Let’s create a constructor using two parameters:
the host to connect to, and the port to connect to. And let’s store these into two local final
variables. We’ll also write a public run method for the
actual client behavior. First we’ll need an instance of Netty’s new
IO event loop group. Next we’re going to use the Bootstrap class
to setup a Channel using this EventLoopGroup. And we’re going to tell it to use the new
IO Sockets. Finally we’ll tell it that we want the Channel
to be handled by the ChatClientIntializer class which we’ll implement in a minute. Now we can ask the Bootstrap object to create
a connection to the server using the specified host and port. Once connected, we can ask for the Channel
object which we can use for interaction with the server. We’ll also need a BufferedReader to capture
the user’s input from the console. We’ll add a while loop which takes the user’s
input from the console and writes it to the server. And finally when we exit the loop for whatever
reason we’ll need to shutdown the EventLoopGroup. Also let’s add a main method which we can
run to start a ChatClient on port 8000 on localhost. Now it’s time to implement the ChatClientInitializer. In stead of implementing the general ChannelHandler
interface we want it to extend the ChannelInitializer class. Keep in mind that we want this class to initialize
SocketChannels, so we’ll need to make some changes. Here’s we’ll define what Netty calls a pipeline.
It basically describes how we want to organize our communication. First we’ll tell Netty we’re expecting frames
of at most 8192 in size, each delimited with line endings. Since we’re just exchanging Strings between
the server and clients, we can use the StringDecoder to decode received bytes into Strings. And we can use the StringEncoder to encode
Strings into bytes, which we can then send over to the server. And finally we’ll need to define a class which
will handle all the decoded incoming Strings from the server. This class will need to implement the ChannelInboundMessageHandlerAdapter
class. Note that we want this class to handle incoming
String objects, so we’ll have to modify it a bit. All we want to do is to print any String message
we receive from the server to the console. All that remains for the ChatClient are a
few calls which could potentially throw Exceptions. For simplicity I’m just going to ignore these. The ChatClient is now finished. Now let’s
start on the ChatServer by creating the ChatServer class. We’re going to create a similar setup as to
the ChatClient class. Let’s start by creating a constructor with a single parameter: the
port to listen on for incoming connections. And let’s store this in a local final variable. Next we’ll create a run method, which will
listen for incoming connections and hand them off for processing. We’ll also need two new IO EventLoopGroups.
The boss group will accept incoming connections as they arrive and pass them on processing
to the worker group. Similarly to how the ChatClient class uses
the Bootstrap class to set up a Channel, we can use the ServerBootstrap class here to
define how the server will process incoming connections. We first specify that it should use the boss
group and the worker group. And like the ChatClient, we want to use the
new IO ServerSockets for communication. Next we need to specify a class which will
handle any incoming messages. We’ll implement this in a minute. Finally we can tell the ServerBootstrap object
to bind to the specified port, and start listening for incoming connections. And before we exit the run method it’s a good
practice to clean up the used EventLoopGroups. Let’s also add a main method which will start
a ChatServer using port 8000 to listen for incoming connections. Now it’s time to implement the ChatServerInitializer
class. Again we don’t want to implement the general
ChannelHandler interface, but rather extend the ChannelInitializer class. This initializer should initialize SocketChannels,
so we’ll need to make some minor changes. Like the ChatClientInitializer class, we’ll
need to specify the ChannelPipeline. However the framer, decoder and encoder are
the exact same as in the ChatClientInitializer class. So we can just copy these over. Finally, we’ll need to specify a handler class
which will handle any incoming messages from the
clients. This class should extend the ChannelInboundMessageHandlerAdapter
class in favor of implementing the general ChannelHandler interface. We’ll need to slightly modify it because we
are going to process incoming String messages here. In order to identify who has sent us a message,
we can ask for the Channel object. Next, we will need to send a message to everybody
else. We can do this by iterating over all known Channels and write a message to each
Channel except for the one on which we received the message. We can keep track of these Channels in the
ChannelGroup object. Remember that we should only send the message
to all Channels except for the one which has sent the message to the server. We’re almost done. We still need to keep track
of our active Channels. We can do this by overriding
the handlerAdded method. This method will be called when a new
ChatClient connects to the ChatServer. Here we can notify the other ChatClients
that a new client has joined. And finally, also add it to the ChannelGroup
object. We’ll also have to keep track of when ChatClients
disconnect from the ChatServer. We can do this by overriding the handlerRemoved method. Here we can also notify all ChatClients that
one client has disconnected and left the conversation And finally, remove it from the ChannelGroup
object. Finally, one method call could potentially
throw an exception, but I’m going to ignore this in this screencast. Alright! We’re done writing code. It time
to test our new chat system! Let’s start by running a ChatServer instance. Next, I’ll start a ChatClient. Also, let’s add and position a second console
for you so that we can display two ChatClients side by side. As you can see, there is only one ChatClient
running so let’s start a second one. On the left you will see the first client
and on the right the second. Now let’s play around a bit. Alright! Everything seems to work properly. Thank you for watching this screencast I hope
you’ve enjoyed it.

36 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *