WebSocket Programming with Java
By: Javier Gonzalez
Tags: Websocket, Java, Client Server Model, Java Apis, Eclipse Tyrus

A Websocket allows to create a communication channel between a client and a server. In particular, a communication channel that uses the websocket protocol as a communication protocol. The websocket protocol is compatible with the HTTP protocol (yes, the one used to connect web browsers and web servers). However, it has very special improvements: (1) lower overhead than HTTP and (2) bi-directional web communication.

Figure 1. Traditional HTTP communication vs WebSocket

Traditional Web communication using the HTTP protocol works as follows: (1) first, a client (usually a web browser) have to connect to the server; (2) then, the client send a request for a resource (such as a request for a webpage); (3) after that, the server responds, and (4) closes the communication channel. On the other hand, websocket works as follows: (1) keep the connection open; moreover (2) both client and server can make requests and send responses. There are diverse Java libraries that allows developers to create Websocket based applications in Java. Either for Java application that will have the role of client and connect to existing Web servers. Or, Java applications that will connect with a server also wrote in Java. We will explore the former — a Java client to connect to an existing server.

Getting Started

First, we need a library that help us to deal with the details. I am showing you examples using Eclipse Tyrus – an open source API for easy development of WebSocket applications. Therefore, we need to add to our project the following dependencies: (1) tyrus.client, (2) tyrus.server, and since we are building a standalone application, we need also help with a container, (3) tyrus.container.grizzly. Click the links and get there the dependecies configuration (Maven, Gradle, etc.) or get the bundles with all the JAR files you need. Up to you. Set up your project and then move forward with the coding part.

Server

The API allow us to work using Java Annotations or “traditional” API interfaces. The example below follows the annotations approach. Arguably, using annotations provides better information about classes and method’s responsibilities. Let us start with the server. It is shown in Figure 1. Important elements:

Import the package — tyrus.server. We need the class Server. Create a Server object. Arguments for the constructor: hostName (localhost in this example), port in which the server will run, the root path for the server app, and the important one, the class to handle the server work. It can be a class annotated with ServerEndpoint, (as our example will) or a class implementing ServerApplicationConfig or a class extending ServerEndpointConfig. Run the start() method in the server object, and that is all.

Figure 1. ChatServer.java

ServerEndpoint

ServerEndPoint is a class level annotation that declares that the class it decorates is a web socket endpoint that will be deployed and made available in a WebSocket server. Our ServerEndPoint is as shown in Figure 2. Important elements:

The class level anotation ServerEndpoint specifies the URL in which the endpoint will be published (in conjunction with the hostName, port, and path defined before). In this case, our example will be running as localhost:8025/project/app. The rest of the class is pretty strait forward to read: methods that will be called automatically when a connection open, onOpen(), a message is received, onMessage(), or the connection closes, onClose().

In the example, we are working with text messages. But binary is also supported. This server prints and returns (to the client) the same message that it receives. Input as parameter and output as a return value. Simple, right?

Figure 2. ChatServerEndpoint.java

Client

Now, it is time to create a client to connect with our server. Figure 3 shows the important elements:

As can be imagine, our class need to be annotated, now as a ClientEndpoint Line 36 and 39 do the magic. We create a ClientManager and then ask it to make a connection between the supplied annotated endpoint (our client) and a server specified by an URI. Once the connection is established, the logic is similar to the one in the server: methods that will be called automatically when a connection open, onOpen(), a message is received, onMessage(), or the connection closes, onClose().

Figure 3. ChatClientEndpoint.java

What is the client doing? First, when the connection opens, the client sends a message to the server using session.getBasicRemote().sendText() –Line 11. The server receives the message and answers with exactly the same string. Then, the method onMessage() will run in the client. Just for fun, our client in the method onMessage() is reading a string from the user’s keyboard (using System.in) and sending that string to our server. At this point, the lifecycle of our application consist of the methods onMessage() being executed on the client and on the server sides.

Source Code

The complete source code for the described example is available on my GitHub. Explore the details there and happy coding.

Challenge

Now that we have a vanilla implementation of WebSockets, How hard it could be to add some decoration here and create a ChatApp with a nice GUI for the client? 🤔