Java EE HTML5 WebSocket example
Introduction
The HTML5 specification brought the capability for web browsers to establish full-duplex TCP connections with websocket compliant servers.
In other words, browsers are now able to establish a connection with a server and keep sending or receiving data through that same established communication channel without the need of the overhead introduced by the HTTP protocol itself.
In this tutorial we will implement a simple websocket server endpoint in a Java EE environment and also the respective client-side infrastructure for sending and receiving data.
This tutorial considers the following environment:
- Ubuntu 12.04
- JDK 1.7.0.21
- Glassfish 4.0
WebSocket server endpoint
Let's define a Java EE websocket server endpoint:
package com.byteslounge.websockets; import java.io.IOException; import javax.websocket.OnClose; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; @ServerEndpoint("/websocket") public class WebSocketTest { @OnMessage public void onMessage(String message, Session session) throws IOException, InterruptedException { // Print the client message for testing purposes System.out.println("Received: " + message); // Send the first message to the client session.getBasicRemote().sendText("This is the first server message"); // Send 3 messages to the client every 5 seconds int sentMessages = 0; while(sentMessages < 3){ Thread.sleep(5000); session.getBasicRemote(). sendText("This is an intermediate server message. Count: " + sentMessages); sentMessages++; } // Send a final message to the client session.getBasicRemote().sendText("This is the last server message"); } @OnOpen public void onOpen() { System.out.println("Client connected"); } @OnClose public void onClose() { System.out.println("Connection closed"); } }
As you may have already noticed we are importing several classes from javax.websocket package.
@ServerEndpoint annotation is used at type level and defines the current class as a websocket server endpoint. The value used in this annotation represents the URL where the endpoint will be listening for client connections.
onOpen and onClose methods are annotated with @OnOpen and @OnClose respectively. These annotations are almost self-explanatory: They define which methods will be called upon a new client connection and disconnection.
Method onMessage is annotated with @OnMessage. This annotation defines which method will be called when a new message is received from the client. Note that this method may be defined with an optional parameter of type javax.websocket.Session (in our case the session parameter). If this parameter is defined the container will inject the session that is associated with the current client that sent the message being handled.
In our case we are just writing the client message content to the standard output. Then we proceed to send a message to the client followed by 3 test messages with a 5 second interval. Finally we send a final message to the client.
Client side
Now we need to write the client-side of our websocket test application:
<!DOCTYPE html> <html> <head> <title>Testing websockets</title> </head> <body> <div> <input type="submit" value="Start" onclick="start()" /> </div> <div id="messages"></div> <script type="text/javascript"> var webSocket = new WebSocket('ws://localhost:8080/byteslounge/websocket'); webSocket.onerror = function(event) { onError(event) }; webSocket.onopen = function(event) { onOpen(event) }; webSocket.onmessage = function(event) { onMessage(event) }; function onMessage(event) { document.getElementById('messages').innerHTML += '<br />' + event.data; } function onOpen(event) { document.getElementById('messages').innerHTML = 'Connection established'; } function onError(event) { alert(event.data); } function start() { webSocket.send('hello'); return false; } </script> </body> </html>
This is a simple test page that contains the JavaScript that will create a websocket connection to out websocket server endpoint.
onOpen method will be called when we establish a connection with the server endpoint.
onError method is called when an error occurs during client-server communication.
onMessage method will be called when a message is received from the server. In our case we are just appending the messages received from the server to the DOM.
We connect to the websocket server endpoint by using the construct new WebSocket() and passing the endpoint URL:
ws://localhost:8080/byteslounge/websocket
Testing
We may now test our application by accessing the testing page:
http://localhost:8080/byteslounge/page.html
We will see the Connection established message as expected:
Now as soon as we press the button we will send the initial message to the server through the websocket and receive the subsequent test messages sent by the server:
WebSockets Handshake
The TCP connection between the client and the server is established after the occurrence of a handshake over the HTTP protocol. It's easy to observe the handshake by using some HTTP traffic debugger. As soon as we create the WebSocket instance in the client the following request and respective server response will occur:
Note: we will only include HTTP headers that are relevant to the websockets handshake
Request:
Connection: Upgrade
Upgrade: websocket
Sec-WebSocket-Key: wVlUJ/tu9g6EBZEh51iDvQ==
Response:
Upgrade: websocket
Sec-WebSocket-Accept: 2TNh+0h5gTX019lci6mnvS66PSY=
Note that the client is requesting the protocol to be upgraded to the WebSocket protocol by using Connection: Upgrade and Upgrade: websocket HTTP headers. The server response states that the client request was accepted and it will change protocol to WebSocket (using HTTP status code 101):
HTTP/1.1 101 Web Socket Protocol Handshake
Downloadable sample
The example source code is available for download at the end of this page. The test was executed in Glassfish 4 (you will need a Java EE 7 compliant application server).