WEBSOCKET VERSUS COMET – REAL-TIME WEB APPLICATIONS

Introduction

Many web application developers have tried to develop a high performance web application with real-time behaviors. A common example of such an application could be a communication web platform like a chat application. A message sent by a user should be transmitted in real-time to other users.

Years ago I tried to develop a chat web application but until now it has not been so easy to achieve. I would like to show you how easily and clearly it can be implemented today.
There will be a comparison of the different approaches to develop a real-time web application which will show that it has never been easier to achieve it before.

The approaches I want to compare are the “new” WebSocket technology and the “old” Comet approach. First you will see how difficult it was and how easy it is today to write a small communication platform. You will also get the easy and clear source code of the communication platform through the WebSocket technology. This article does not cover a complete WebSocket description but a short insight of the WebSocket technology advantages and a practical usage.

Challenges

The biggest challenge to achieve real-time on web applications is the application layer protocol HTTP. The protocol is a request-response protocol where:

  • only the client is able to send a request and
  • only the server is able to send a response to the previous sent client-request.

Therefore only the client is able to start the communication. Without a previous received client-request the server is not able to communicate with the client.

Usually HTTP uses TCP as transport layer protocol. The TCP connection will be closed after a request-response phase. It has to be reinitiated by the client in case the client wants to communicate with the server again.

There are other solutions like HTTP-polling but with such an approach you can only achieve near-real-time behavior because the client has to poll the server for new updates with a defined frequency. You could set the poll frequency very low but in this case the application would not perform very well.

Usually with HTTP the TCP connection is closed after each request-response phase. Another challenge occurs when the client and server are connected permanently which cause resource problems on the server-side because of keeping open all connections for each client.

We do concentrate on “real” web applications and not on web applications which embed rich client applications like a swing application on an HTML site via applets or by using JNLP. Of course there is no barrier for rich client applications to transmit data in real-time in both directions by using the TCP transport-protocol directly and not the HTTP application-layer-protocol.
After showing all the challenges I promise that there is a good solution with the new WebSocket technology. But let us first see how difficult it had been for developers before this new technology was developed.

Comet

Comet is not a technology but only a model which uses the HTTP protocol and which tries to hide the limitations due to its incapacity starting the communication on server side by sending a request to the client. Comet tries to hide this limitation by simulating a server-push request.

The Comet model consists of two steps. In the first step:

  • the client sends a request to the server and
  • the server holds this request open until there is data available for the client.

In the second step:

  • data is sent from the server to the client and
  • a new request is started by the client.

With this approach the server can send data to the client immediately.

This technique is called long-polling. The client polls data from the server by sending a request remaining open for a long time until data is available for sending to the client at once.

At first sight this technique looks good but it is not so easy to implement due to some problems with it. For example there are timeouts if the client-request is open too long. In the next step the client has to re-initiate the polling. In addition the behavior of time-outs can vary for different environments. Also an open request on server side which is not closed requires much of server resources.

I think now it should be clear for us that this technique could be only a hack to avoid polling with a defined frequency. There is another efficient solution to achieve real-time behaviors in web applications.

WebSockets

As I supposed that you are waiting for the best solution for implementing a real-time communication web application, I will introduce the new WebSocket technology to you.

The new WebSocket technology has nothing to do with HTTP but only the WebSocket handshake is interpreted by HTTP servers as an http upgrade-request. While Http is commonly using TCP, also WebSocket is based on TCP.

For HTTP the TCP connection is usually closed after every request-response phase. In contrast the WebSocket connection is started by the client by sending a connection request to the server and after the connection is established the TCP connection is never closed. While the connection is kept open the client and the server are able to send data mutually. Also WebSockets support full duplex allowing the transmission of data in both directions simultaneously similar to TCP.

In order to save resources on server-side the Java Reference Implementation (RI) of the WebSocket protocol uses asynchronous handling. It is not necessary to keep running a thread per client-connection on the server-side.

As the WebSocket protocol was published as IETF standard there are many technologies and libraries supporting it. Since Java EE 7 implemented the WebSocket protocol in the RI – code name Tyrus – also Glassfish supports it. In addition the WebSocket protocol is supported by HTML5 and by a wide range of browsers. Also in Android applications you can use it by adding different libraries like autobahn-library. I am not surprised that the support of WebSockets is mature and I think it will become better in the future.

Example – communication web application with WebSockets

Now we will implement our own communication web application. The application consists of a simple website showing a textarea field, an input field and a send button. The textarea field shows all messages which are sent by the connected clients. With the input field and the send button a message can be sent by a client.

As mentioned before this application is implemented with WebSockets in order to create a real-time application. All messages will be shown to every connected user immediately after sending.

Server

The server is implemented with the Java 7 JEE RI of the WebSocket technology tyrus. As server we are using the tyrus standalone server which is using the grizzly web-container not to deploy the web application to the Java 7 EE Glassfish server.
All dependencies we get through the following maven dependencies:

		<dependency>
		    <groupId>javax.websocket</groupId>
		    <artifactId>javax.websocket-api</artifactId>
		    <version>1.0</version>
		</dependency>
		<dependency>
		    <groupId>org.glassfish.tyrus</groupId>
		    <artifactId>tyrus-server</artifactId>
		    <version>1.2.1</version>
		</dependency>
		<dependency>
		    <groupId>org.glassfish.tyrus</groupId>
		    <artifactId>tyrus-container-grizzly</artifactId>
		    <version>1.2.1</version>
		</dependency>

Now we have all external libraries we need in order to implement a standalone server supporting the WebSocket technology. Let us start to implement the deployable web application. All we need is the following code snippet.

@ServerEndpoint(value = "/meeting-room")
public class ForwardServer {

    @OnOpen
    public void onOpen(Session session) {
        for( Session open : session.getOpenSessions()){
        	try {
			open.getBasicRemote().sendText( "new user connected.");
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
        }
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        for( Session open : session.getOpenSessions()){
        	try {
			open.getBasicRemote().sendText( "msg: "+message);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
        }
    }

    @OnClose
    public void onClose(Session session) {
    }
}

We call the class ForwardServer because the server is forwarding the incoming messages to all connected clients. The endpoint is reachable through ../meeting-room.

If a client connects with the server all clients receive the message “new user connected”. This is implemented by the onOpen-Method. With the Session-argument of the onOpen-Method you get all Sessions of the connected clients by calling session.getOpenSessions(). And with the corresponding client-session you can send messages to all connected clients.

If a user sends a message all clients connected to the server get this message which is implemented in the onMessage-Method in a similar way by iterating over all connected clients and by sending the received message to all clients.

After having implemented the forward server we have to deploy it on the standalone server. This is implemented by the following code snippet. In addition the standalone-server is started.

public class WebSocketServer {

    public static void main(String[] args) {
        runServer();
    }

    public static void runServer() {
        Server server = new Server("localhost", 80, "/websockets", ForwardServer.class);
        try {
            server.start();           
            synchronized( server){
            	server.wait();
            }        
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            server.stop();
        }
    }
}

Now the server implementation is finished and we can reach our forward server by the following url: ws://localhost/websockets/meeting-room

Client

Let us implement the client side. We are not using a Java client but a simple JavaScript client. Fortunately almost every new browser will support the new WebSocket technology.

As mentioned before the web application is very easy to implement. As you can see it is very easy to instantiate a WebSocket in JavaScript. The connect()-method instantiates a WebSocket and implements the onmessage() -method which only appends the receiving message to an textarea field. The sendmessage()-method uses the WebSocket and sends the message from the input field to the server.

<html>
    <head>
        <title>meeting room</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <script type="text/javascript">
		// Websocket Endpoint url
		var endPointURL = "ws://localhost/websockets/meeting-room";
		var webSocket;
		 
		function connect () {
		    webSocket = new WebSocket(endPointURL);
		    webSocket.onmessage = function (event) {
		        var messagesArea = document.getElementById("messages");
		        var message = event.data + "\r\n";
		        messagesArea.value =  message + messagesArea.value;
		    }
		}
		 
		function disconnect () {
		    webSocket.close();
		}
		 
		function sendmessage() {   
		    webSocket.send( document.getElementById("messageInput").value);
		    document.getElementById("messageInput").value = "";
		}
	</script>
    </head>
    <body onload="connect();" onunload="disconnect();">
        <h1>meeting room</h1>
        <textarea id="messages" style="width:500px;height:200px" disabled="true"></textarea>
        <div>
            <input id="messageInput" style="width:440px;" onkeydown="if (event.keyCode == 13) sendMessage();" />
            <input type="button" value="send" onclick="sendmessage();" />
	</div>
    </body>
</html>

For a Java client supporting the WebSocket technology you can do this by adding the following dependency.

		<dependency>
		    <groupId>org.glassfish.tyrus</groupId>
		    <artifactId>tyrus-client</artifactId>
		    <version>1.2.1</version>
		</dependency>

The implementation of a Java client is as easy as the implementation of the JavaScript client. The client class is annotated with @ClientEndpoint and deployed by the Tyrus ClientManager. After connecting to the server it will be sent a short message to the client. In a next step the client listens for other messages which will be printed to the standard console.

@ClientEndpoint
public class Client {

	private Session sess;

    @OnOpen
    public void onOpen(Session session) {
    	sess = session;
    }

    @OnMessage
    public void onMessage(String message, Session session) {
    	System.out.println( "msg:"+message);
    } 

    public void sendMessage( String message) throws IOException{
    	sess.getBasicRemote().sendText( message);
    }

    public static void main(String[] args) throws Exception {
        ClientManager client = ClientManager.createClient();

        final Client c = new Client();       
        client.connectToServer( c, new URI("ws://localhost/websockets/meeting-room"));

        c.sendMessage( "send data by java-client ...");

        synchronized (client) {
		client.wait();
	}
    }
}

Conclusion

Hopefully I could show how easy it is to implement a real-time web application with the new WebSocket technology. In contrast to the Comet model the WebSocket technology can achieve real-time behavior for web applications without the misuse of the HTTP protocol.

With the WebSocket technology a developer gets a new tool to make a web application more attractive for many web application users. Before the WebSocket technology was born there had been a solution to achieve a high performance real-time web application with the Comet-model but it has never been easier to achieve by WebSockets.

Schreiben Sie einen Kommentar