通过Spring 4在Java中使用websocket

jopen 11年前

WebSocket是一种提供全双工通信的协议,通常是浏览器(或其他客户端)与Web服务器之间的通信。这使得它适合于高度交互的Web应用程序,如聊天,游戏,仪表盘等。

A websocket client connects to a websocket server and a handshake is performed. This handshake occurs over HTTP. Once the handshake is complete, the same connection is used for TCP based, bidirectional, socket communication. Since the handshake is done over HTTP, it is good bet that this connection will not be blocked by firewalls making it a good alternative to other RPC mechanisms.

Websocket Supporting Versions

Websocket is relatively new therefore only the latest web browsers, frameworks and application servers support this. You will be needing the following versions to run this example:

  • Spring framework - 4.0.1
  • Apache Tomcat – 7.0.52
  • Web browser – Chrome and Firefox has had support for quite a while. Take a look at http://caniuse.com/websockets and determine if your browser has websocket support.

For you to follow the example in this post, I am assuming you are already familiar with creating a Spring MVC application however, since at the time of writing of this post (March, 2014), Spring 4 is new, I’ll go through the steps you need to take in order to convert a Spring 3 application to a Spring 4.

Steps to convert a Spring 3 project to Spring 4

  1. Update servlet-api jar
    Websockets require servlet api version 3 or above.
            <dependency>            <groupId>javax.servlet</groupId>            <artifactId>javax.servlet-api</artifactId>            <version>3.1.0</version>          </dependency>
  2. Update web.xml namespaces,xsd files and version
    Open web.xml and take a look at the web-app tag. The namespaces, xsd and version should all be 3.0
  3. Update Spring framework jars.
    Update all spring framework jars to version 4.0.1.RELEASE. The artifacts you would require to upgrade are spring-core, spring-context, spring-web and spring-webmvc
  4. Update XML Schema locations
    In your Spring context XML files, update XML schema locations to point to version 4.0. For example,
    in your applicationContext.xml (or differently named content XML), the beans tag will have an attribute named xsi:schemaLocation. The values for this attribute are space separated URLs. Change the version of xsd files to 4. They will look something like http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
  5. Update Jackson libraries
    Jackson is the JSON library used by Spring. Version 1.x of the Jackson library has been deprecated by Spring so we need to upgrade to a later version.
    <dependency>      <groupId>com.fasterxml.jackson.core</groupId>      <artifactId>jackson-core</artifactId>      <version>2.3.0</version>  </dependency>    <dependency>      <groupId>com.fasterxml.jackson.core</groupId>      <artifactId>jackson-databind</artifactId>      <version>2.3.0</version>  </dependency>

 

Once these changes are made, you may still need to update some deprecated dependencies depending on which Spring features you are using after which your migration to Spring 4 would have been complete.

 

Websocket Implementation

Adding dependencies

Once you have successfully setup (or migrated to) a Spring 4 project, we can begin implementing websockets. We need the following dependencies in our classpath.

<dependency>     <groupId>org.springframework</groupId>     <artifactId>spring-websocket</artifactId>     <version>4.0.1.RELEASE</version>  </dependency>  <dependency>     <groupId>org.springframework</groupId>     <artifactId>spring-messaging</artifactId>     <version>4.0.1.RELEASE</version>  </dependency>
Create a websocket handler class

A websocket handler is a class that will receive requests from the websocket client. In this example, we are creating a class called WebsocketEndPoint which extends the framework class TextWebSocketHandler. We are overriding a function (handleTextMessage) that will be called whenever a client sends a message to the server.

This function receives two parameters, the TextMessage object, which contains a String payload and a WebSocketSession object which we are using to send a message back to the client in the last line of the following snippet.

package co.syntx.example.websocket.handler;    import org.springframework.web.socket.TextMessage;  import org.springframework.web.socket.WebSocketSession;  import org.springframework.web.socket.handler.TextWebSocketHandler;    public class WebsocketEndPoint extends TextWebSocketHandler {     @Override   protected void handleTextMessage(WebSocketSession session,     TextMessage message) throws Exception {    super.handleTextMessage(session, message);    TextMessage returnMessage = new TextMessage(message.getPayload()+" received at server");    session.sendMessage(returnMessage);   }  }
Create a handshake interceptor (Optional)

The websocket handshake interceptor is used to define and specify a class that intercepts the initial websocket handshake. Interceptors are purely optional. We are adding these here for debugging so that we know when and if a handshake took place.

package com.gemalto.dirserviceintegration.websocket.interceptor;    import java.util.Map;    import org.springframework.http.server.ServerHttpRequest;  import org.springframework.http.server.ServerHttpResponse;  import org.springframework.web.socket.WebSocketHandler;  import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;    public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor{     @Override   public boolean beforeHandshake(ServerHttpRequest request,     ServerHttpResponse response, WebSocketHandler wsHandler,     Map<String, Object> attributes) throws Exception {    System.out.println("Before Handshake");    return super.beforeHandshake(request, response, wsHandler, attributes);   }     @Override   public void afterHandshake(ServerHttpRequest request,     ServerHttpResponse response, WebSocketHandler wsHandler,     Exception ex) {    System.out.println("After Handshake");    super.afterHandshake(request, response, wsHandler, ex);   }    }
Configure Handler and Interceptor

We have two ways to configure handlers and interceptors
i) Create beans from within a java class that implements WebSocketConfigurer interface.(The new XML-less way)
ii) Define beans in spring application context via the applicationContext.xml (the traditional way).

We’ll use the later in this example.

Create the following beans via XML to configure the websocket handler and interceptor.

<bean id="websocket" class="co.syntx.example.websocket.handler.WebsocketEndPoint"/>    <websocket:handlers>      <websocket:mapping path="/websocket" handler="websocket"/>      <websocket:handshake-interceptors>      <bean class="co.syntx.example.websocket.HandshakeInterceptor"/>      </websocket:handshake-interceptors>  </websocket:handlers>

The first line creates an instance of the bean we created earlier.
The websocket mapping tag maps a URL pattern to the handler.
In the handler-interceptors tag, configure the interceptor bean.

At this point, our server configuration and code is complete so we can now proceed to write our client.

 

Websocket Client in Javascript

<script type="text/javascript">          function setConnected(connected) {              document.getElementById('connect').disabled = connected;              document.getElementById('disconnect').disabled = !connected;              document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';              document.getElementById('response').innerHTML = '';          }            function connect() {           if ('WebSocket' in window){              console.log('Websocket supported');              socket = new WebSocket('ws://localhost:8080//websocket');                console.log('Connection attempted');                socket.onopen = function(){                console.log('Connection open!');                setConnected(true);             }                socket.onclose = function(){               console.log('Disconnecting connection');              }                socket.onmessage = function (evt)                  {                     var received_msg = evt.data;                    console.log(received_msg);                    console.log('message received!');                    showMessage(received_msg);                 }              } else {              console.log('Websocket not supported');            }          }            function disconnect() {              setConnected(false);              console.log("Disconnected");          }            function sendName() {              var message = document.getElementById('message').value;              socket.send(JSON.stringify({ 'message': message }));          }            function showMessage(message) {              var response = document.getElementById('response');              var p = document.createElement('p');              p.style.wordWrap = 'break-word';              p.appendChild(document.createTextNode(message));              response.appendChild(p);          }    </script>

The above javascript snippet gives you an idea of how to connect to and communicate with a websocket server.

  1. A new connection is attempted by new WebSocket(‘ws://localhost:8080//websocket’);
  2. During the attempt, the handshake with the server side is performed. If handshake is successful and connection is established, the socket.onopen event handler is called.
  3. When a message from the server is received, the socket.onmessage event handler is called.
  4. In order to send a message to the server socket.send() function is used.