Chapter 4. Understanding WebSocket

The idea of web applications was built upon a simple paradigm. In a unidirectional interaction, a web client sent a request to a server, the server replied to the request, and the client rendered the server's response. The communication started with a client-side request and ended with the server's response.

We built our web applications based on this paradigm; however, some drawbacks existed in the technology: the client had to wait for the server's response and refresh the browser to render it. This unidirectional nature of the communication required the client to initiate a request. Later technologies such as AJAX and long polling brought major advantages to our web applications. In AJAX, the client initiated a request but did not wait for the server's response. In an asynchronous manner, the AJAX client-side callback method got the data from the server and the browsers' new DHTML features rendered the data without refreshing the browser.

Apart from unidirectional behavior, the HTTP dependencies of these technologies required the exchange of extra data in the form of HTTPS headers and cookies. This extra data caused latency and became a bottleneck for highly responsive web applications.

WebSocket reduced kilobytes of transmitted data to a few bytes and reduced latency from 150 milliseconds to 50 milliseconds (for a message packet plus the TCP round trip to establish the connection), and these two factors attracted the Google's attention (Ian Hickson).

WebSocket (RFC 6455) is a full duplex and bidirectional protocol that transmits data in the form of frames between client and server. A WebSocket communication, as shown in the following figure, starts with an HTTP connection for a handshake process between a client and a server. Since firewalls let certain ports be open to communicate with the outside, we cannot start with the WebSocket protocol:

Understanding WebSocket

WebSocket communication

During the handshake process, the parties (client and server) decide which socket-based protocol to choose for transmitting data. At this stage, the server can validate the user using HTTP cookies and reject the connection if authentication or authorization fails.

Then, both parties upgrade from HTTP to a socket-based protocol. From this point onward, the server and client communicate on a full duplex and bidirectional channel on a TCP connection.

Either the client or server can send messages by streaming them into frame format. WebSocket uses the heartbeat mechanism using ping/pong message frames to keep the connection alive. This looks like sending a ping message from one party and expecting a pong from the other side. Either party can also close the channel and terminate the communication, as shown in the preceding diagram.

Like a web URI relies on HTTP or HTTPS, WebSocket URI uses ws or wss schemes (for example, ws://www.sample.org/ or wss://www.sample.org/) to communicate. WebSocket's ws works in a similar way to HTTP by transmitting non-encrypted data over TCP/IP. By contrast, wss relies on Transport Layer Security (TLS) over TCP, and this combination brings data security and integrity.

A good question is where to use WebSocket. The best answer is to use it where low latency and high frequency of communication are critical—for example, if your endpoint data changes within 100 milliseconds and you expect to take very quick measures over the data changes.

Spring Framework 4 includes a new Spring WebSocket module with Java WebSocket API standard (JSR-356) compatibility as well as some additional value-adding features.

While using WebSocket brings advantages to a web application, a lack of compatibility in a version of some browser blocks WebSocket communication. To address this issue, Spring 4 includes a fallback option that simulates the WebSocket API in case of browser incompatibility.

WebSocket transmits data in the frame format, and apart from a single bit to distinguish between text and binary data, it is neutral to the message's content. In order to handle the message's format, the message needs some extra metadata, and the client and server should agree on an application-layer protocol, known as a subprotocol. The parties choose the subprotocol during the initial handshake.

WebSocket does not mandate the usage of subprotocols, but in the case of their absence, both the client and server need to transmit data in a predefined style standard, framework-specific, or customized format.

Spring supports Simple Text Orientated Messaging Protocol (STOMP) as a subprotocol—known as STOMP over WebSocket—in a WebSocket communication. Spring's Messaging is built upon integration concepts such as messaging and channel and handler, along with annotation of message mapping. Using STOMP over WebSocket gives message-based features to a Spring WebSocket application.

Using all of these new Spring 4 features, you can create a WebSocket application and broadcast a message to all subscribed clients as well as send a message to a specific user. In this chapter, we start by creating a simple Spring web application, which will show how to set up a WebSocket application and how a client can send and receive messages to or from an endpoint. In the second application, we will see how Spring WebSocket's fallback option can tackle browser incompatibly, how a broker based messaging system works with STOMP over WebSocket, and how subscribed clients can send and receive messages. In the last web application, however, we will show how we can send broker-based messages to a specific user.

Creating a simple WebSocket application

In this section, while developing a simple WebSocket application, we will learn about WebSocket's client and server components. As mentioned earlier, using a subprotocol is optional in a WebSocket communication. In this application, we have not used a subprotocol.

First of all, you need to set up a Spring web application. In order to dispatch a request to your service (called a handler in Spring WebSocket), you need to set up a framework Servlet (dispatcher Servlet). This means that you should register DispatcherServlet in web.xml and define your beans and service in the application context.

Setting up a Spring application requires you to configure it in XML format. Spring introduced the Spring Boot module to get rid of XML configuration files in Spring applications. Spring Boot aims at configuring a Spring application by adding a few lines of annotation to the classes and tagging them as Spring artifacts (bean, services, configurations, and so on). By default, it also adds dependencies based on what it finds in the classpath. For example, if you have a web dependency, then Spring Boot can configure Spring MVC by default. It also lets you override this default behavior. Covering Spring Boot in complete detail would require a full book; we will just use it here to ease the configuration of a Spring application.

These are the Maven dependencies of this project:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.5.RELEASE</version>
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-messaging</artifactId>
    </dependency>
            <dependency>
        <groupId>org.json</groupId>
        <artifactId>json</artifactId>
        <version>20140107</version>
    </dependency>
</dependencies>
<properties>
    <java.version>1.8</java.version>
</properties>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

As mentioned in the beginning of this section, there is no subprotocol (and, subsequently, no application-layer framework) to interpret WebSocket messages. This means that the client and server need to handle the job and be aware of the message's format.

On the server's side, the handler (endpoint) receives and extracts the message and replies back (based on the business logic) to the client. In Spring, you can create a customized handler by extending either TextWebSocketHandler or BinaryWebSocketHandler. TextWebSocketHandler handles string or text messages (such as JSON data) and BinaryWebSocketHandler handles binary messages (such as image or media data). Here is a code listing that uses TextWebSocketHandler:

public class SampleTextWebSocketHandler extends TextWebSocketHandler {
   @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String payload = message.getPayload();
        JSONObject jsonObject = new JSONObject(payload);
        StringBuilder builder=new StringBuilder();
        builder.append("From Myserver-").append("Your Message:").append(jsonObject.get("clientMessage"));
        session.sendMessage(new TextMessage(builder.toString()));
   }
}

Since we process only JSON data here, the class SampleTextWebSocketHandler extends TextWebSocketHandler. The method handleTextMessage obtains the client's message by receiving its payload and converting it into JSON data, and then it sends a message back to the client.

In order to tell Spring to forward client requests to the endpoint (or handler here), we need to register the handler:

@Configuration
@EnableWebSocket
public class SampleEhoWebSocketConfigurer {
    @Bean
    WebSocketConfigurer webSocketConfigurer(final WebSocketHandler webSocketHandler) {
        return new WebSocketConfigurer() {
            @Override
            public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
                registry.addHandler(new SampleTextWebSocketHandler(), "/path/wsAddress");
            }
        };
    }
    @Bean
    WebSocketHandler myWebsocketHandler() {
        return new SampleTextWebSocketHandler();
    }

@Configuration and @EnableWebsocket tell Spring this is the WebSocket configurator of the project. It registers our handler (SampleTextWebSocketHandler) and sets the request path (in a WebSocket URL, such as ws://server-ip:port/path/wsAddress) that will be forwarded to this handler.

And now the question is how to set up a Spring application and glue all of this stuff together. Spring Boot provides an easy way to set up a Spring-based application with a configurable embedded web server that you can "just run":

package com.springessentialsbook.chapter4;
...
@SpringBootApplication
public class EchoWebSocketBootApplication {
    public static void main(String[] args) {
        SpringApplication.run(EchoWebSocketBootApplication
        .class, args);
    }
}

@SpringBootApplication tags the EchoWebSocketBootApplication class as a special configuration class of your application and @SpringBootApplication behaves like the following annotations:

  • @Configuration, which declares the class as a bean definition of an application context
  • @EnableAutoConfiguration, which lets Spring Boot add a dependent bean definition based on the classpath (for example, spring-webmvc in the project classpath tells Spring Boot to set up a web application with its DispatcherServlet registration in web.xml)
  • @ComponentScan, which is used to scan all annotations (services, controllers, configurations, and so on) within the same package (com.springessentialsbook.chapter4) and configure them accordingly

Finally, the main method calls SpringApplication.run to set up a Spring application within a web application without writing a single line of XML configuration (applicationContext.xml or web.xml).

When a client wants to send a WebSocket request, it should create a JavaScript client object (ws = new WebSocket('ws://localhost:8090/path/wsAddress')) and pass the WebSocket service address. In order to receive the data, we need to attach a callback listener (ws.onmessage) and an error handler (ws.onerror), like so:

    function openWebSocket(){
        ws = new WebSocket( 'ws://localhost:8090/path/wsAddress');
        ws.onmessage = function(event){
            renderServerReturnedData(event.data);
        };

        ws.onerror = function(event){
            $('#errDiv').html(event);
        };
    }

    function sendMyClientMessage() {
        var myText = document.getElementById('myText').value;
        var message=JSON.stringify({ 'clientName': 'Client-'+randomnumber, 'clientMessage':myText});
        ws.send(message);
        document.getElementById('myText').value='';
    }

You can run the application by running this command:

mvn spring-boot:run -Dserver.port=8090

This runs and deploys the web application on an embedded server on port 8090 (8080 is not used here as it may conflict with your running Apache service). So, the index page of the application will be accessible at http://localhost:8090/ (follow the instructions in read-me.txt to run the application). It should look like this:

Creating a simple WebSocket application

The opening page of the application in a Chrome browser

When a user sends a text in Chrome, it will be handled by SampleTextWebSocketHandler, the handler will reply, and the response will be rendered in the browser.

If you try to test this application in a version of Internet Explorer lower than 10, you will get a JavaScript error.

As we discussed earlier, certain versions of browsers do not support WebSocket. Spring 4 provides a fallback option to manage these types of browsers. In the next section, this feature of Spring will be explained.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset