Creating a WebSocket in Java…..

Malindu Ransara Nawarathne
6 min readDec 26, 2021
Internet illustrations by Storyset

First, lets see what WebSocket is and why it’s special….

A WebSocket is an API which provides the service to open a two-way interactive communication session between client and a server over TCP connection. It simply means full duplex communication between client and a server.

Now let’s see why web socket is special,

· Bidirectional: As I previously mentioned WebSockets allow full duplex communication between client and a server.

· Persistent: WebSocket allows persistent client-server connection rather than establishing and terminating the connection for each client request and server response.

· Low latency: Because of persistent connection between the server and the client which greatly reduce the data size of each message by only sending all subsequent messages including only the relevant information.

· Secure: WSS (WebSockets over SSL/TLS) is encrypted, thus, it protects user against man-in-the-middle attacks.

Let’s see the differences between HTTP and WebSocket

Difference between WebSocket and HTTP — WebSockets — A Conceptual Deep Dive

Furthermore, I would like to demonstrate how to develop a simple application using WebSocket which will echo the string values that are sent by client endpoint. In this example I am using GlassFish as the embedded server which comes with Eclipse Tyrus.

Let’s start to develop the WebSocket…..

In the beginning, we need to add dependencies to the project which will assist in the process to develop the project. In this example I am using Eclipse Tyrus which is an open-source API. Therefore, we need to add the following dependencies to our project.

1. tyrus.client

2. tyrus.server

3. tyrus.container.grizzly

You can get the dependency configuration (Maven, Gradle, etc.) by clicking on the above links.

Step 1: Set up your project by adding relevant dependencies before you are moving into server or client end point developments. If you are developing a Maven project you can refer to the below dependencies.

<dependencies>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-client</artifactId>
<version>1.1</version>
</dependency>
<!-- Thanks for using https://jar-download.com -->

<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-server</artifactId>
<version>1.1</version>
</dependency>
<!-- Thanks for using https://jar-download.com -->

<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-container-grizzly</artifactId>
<version>1.1</version>
</dependency>
<!-- Thanks for using https://jar-download.com -->
</dependencies>

Step 2: Implementing WebSocket Server Class.

1. In this class we need to import the package — org.glassfish.tyrus.server.Server

2. Create a Server object, Arguments for the Server object constructor are:

a. Host name: A domain name given to the host computer.

b. Port number: The port number which the server will run on.

c. Root path: The root path for the server.

d. Server Endpoint Class: Which will handle the server works.

import org.glassfish.tyrus.server.Server;

import javax.websocket.DeploymentException;
import java.util.Scanner;

public class WebSocketServer {
public static void main(String[] args) {
Server server = new Server("localhost", 8080, "/java", WebSocketServerEndpoint.class);

try {
server.start();
System.out.println("[SERVER]: Server is up and running.....");
System.out.println("[SERVER]: Press 't' to terminate server.....");
Scanner scanner = new Scanner(System.in);
String inp = scanner.nextLine();
scanner.close();
if (inp.equalsIgnoreCase("t")) {
System.out.println("[SERVER]: Server successfully terminated.....");
server.stop();
} else {
System.out.println("[SERVER]: Invalid input!!!!!");
}
} catch (DeploymentException e) {
e.printStackTrace();
}
}
}

Step 3: Implementing WebSocket Server Endpoint.

The API allows developers to code by using Java Annotations or traditional mapping API interfaces. The example below follows the annotations approach. In this WebSocket Server Endpoint class I used class level annotation to declare the class as a WebSocket Endpoint which will be deployed available in a WebSocket server as well as method level annotation which will need to handle requests of the server.

1. The class level annotation (@ServerEndpoint(value = “/demoApp”) at Server EndPoint specifies the URL in which the endpoint will be available. According to the example the URL will be, “ws://localhost:8080/java/demoApp”.

2. The method level annotation at Server Endpoint specifies the functionality of the server:

a. onOpen: This method will establish connection after Handshake process.

b. onMessage: This method will define how to cater client request. In this example we are just returning the value received by the client.

c. onClose: This method will handle the termination of the WebSocket connection.

d. onError: This method will define how to handle errors, and in this example, I haven’t implemented the method.

Note: These methods will be called automatically when the connection opens. Also, in this example I am working with strings, but binary is also supported.

import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
@ServerEndpoint(value = "/demoApp")
public class WebSocketServerEndpoint {
@OnOpen
public void onOpen (Session session) {
System.out.println("[SERVER]: Handshake successful!!!!! - Connected!!!!! - Session ID: " + session.getId());
}
@OnMessage
public String onMessage (String message, Session session) {
if (message.equalsIgnoreCase("terminate")) {
try {
session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "Successfully session closed....."));
} catch (IOException e) {
e.printStackTrace();
}
}
return message;
}
@OnClose
public void onClose (Session session, CloseReason closeReason) {
System.out.println("[SERVER]: Session " + session.getId() + " closed, because " + closeReason);
}
}

Step 4: Implementing WebSocket Client Endpoint

Finally, let’s look at the client endpoint, in this WebSocket Client class, I again used class level annotation to declare class as a WebSocket client endpoint as well as I used method level annotation to create connection between WebSocket server endpoint and the WebSocket client endpoint and to handle the messages/data that are sent and received by the sever.

1. “@ClientEndpoint” class level annotation is used to declare the class as a WebSocket Client Endpoint.

2. The method level annotation at Client Endpoint specifies the functionality of the client:

a. onOpen: This method will establish connection after Handshake process.

b. onMessage: This method will maintain the communication between the server and the client.

c. onClose: This method will handle the termination of the WebSocket connection.

d. onError: This method will define how to handle errors.

Note: When the connection is established, the client sends a message to the server using “session.getBasicRemote().sendText(“Server is ready…..”)”. Next, the server receives the message and responds with the same string. After that, the method “onMessage()” will run in the client. The “onMessage()” method will read the user’s input and send to the server.

3. In the main method I first created an instance of a ClientManager and then I created an instance of URI for the specified server, finally I called the function connectToServer from the ClientManager instance by passing client endpoint class and the URI instance as arguments. This method blocks until a connection is established or throws an error if either the connection could not be made or if there had been a problem with the supplied endpoint class.

Note: I used CountDownLatch to make sure that a task waits for other thread or threads before it starts.

import org.glassfish.tyrus.client.ClientManager;import javax.websocket.*;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;
@ClientEndpoint
public class WebSocketClient {
private static CountDownLatch latch;
@OnOpen
public void onOpen (Session session) {
System.out.println("[CLIENT]: Connection established..... \n[CLIENT]: Session ID: " + session.getId() );
try {
session.getBasicRemote().sendText("Server is ready.....");
} catch (IOException e) {
e.printStackTrace();
}
}
@OnMessage
public String onMessage (String message, Session session) {
Scanner scanner = new Scanner(System.in);
System.out.println("[SERVER RESPONSE]: " + message);
String clientInput = scanner.nextLine();
return clientInput;
}
@OnClose
public void onClose (Session session, CloseReason closeReason) {
System.out.println("[CLIENT]: Session " + session.getId() + " close, because " + closeReason);
latch.countDown();
}
@OnError
public void onError (Session session, Throwable err) {
System.out.println("[CLIENT]: Error!!!!!, Session ID: " + session.getId() + ", " + err.getMessage());
}
public static void main(String[] args) {
latch = new CountDownLatch(1);
ClientManager clientManager = ClientManager.createClient();
URI uri = null;
try {
uri = new URI("ws://localhost:8080/java/demoApp");
clientManager.connectToServer(WebSocketClient.class, uri);
latch.await();
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (DeploymentException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Disadvantages of the WebSocket:

· Intermediary caching is not possible with WebSocket.

· Relatively, complexity is higher than the HTTP, if application does not require a lot of dynamic interaction, HTTP is much simpler to implement.

· If only a small number of messages will be sent or connection between server and client is static, cost wise WebSocket is much costly than the HTTP because, WebSocket maintains the connection rather than establishing and terminating the connection for each client request and server response like in HTTP.

I hope you are now able to understand what WebSocket is, and how to implement a WebSocket using Java and is aware of the advantages and disadvantages of using WebSockets.

--

--