自由之思想，独立之精神

WebSocket

WebSocket is a computer communications protocol, providing full-duplex communication channels over a single TCP connection. It is designed to be implemented in web browsers and web servers, but it can be used by any client or server application.

Protocol

To establish a WebSocket connection, the client sends a WebSocket handshake request, for which the server returns a WebSocket handshake response, as shown in the example below.

The ”upgrade header(the “Upgrade”/”Connection” items) is used in the request/response to set up a connection;

The HTTP status code “101 Switching Protocols” is used in the response;

The client sends a “Sec-WebSocket-Key” header containing base64-encoded random bytes, and the server replies with a hash of the key in the “Sec-WebSocket-Accept” header. The hashing function appends the fixed string ”258EAFA5-E914-47DA-95CA-C5AB0DC85B11” (a GUID) to the value from “Sec-WebSocket-Key” header (which is not decoded from base64), applies the SHA-1 hashing function, and encodes the result using base64(from here). This logic can be verified by the following commands with the example values in the above request/response:

This is used to import the annotations below to create the WebSocket server side code. Since “JSR 356, Java API for WebSocket” is part of Java EE 7, any application server supports Java EE 7 should already have this dependency. For web containers, Tomcat supports the Java WebSocket 1.1 API defined by JSR-356 from version 7.0. So I speculate in a war deployed in such environment, marking the dependency as “<scope>provided</scope>” is enough.

Then you need to create a Java class with the annotations which aims to handle the WebSocket request as the WebSocket server side(from here):

importjava.io.IOException;importjavax.websocket.OnClose;importjavax.websocket.OnMessage;importjavax.websocket.OnOpen;importjavax.websocket.Session;importjavax.websocket.server.ServerEndpoint;/** * @ServerEndpoint gives the relative name for the end point * This will be accessed via ws://localhost:8080/EchoChamber/echo * Where "localhost" is the address of the host, * "EchoChamber" is the name of the package * and "echo" is the address to access this class from the server */@ServerEndpoint("/echo")publicclassEchoServer{/** * @OnOpen allows us to intercept the creation of a new session. * The session class allows us to send data to the user. * In the method onOpen, we'll let the user know that the handshake was * successful. */@OnOpenpublicvoidonOpen(Sessionsession){System.out.println(session.getId()+" has opened a connection");try{session.getBasicRemote().sendText("Connection Established");}catch(IOExceptionex){ex.printStackTrace();}}/** * When a user sends a message to the server, this method will intercept the message * and allow us to react to it. For now the message is read as a String. */@OnMessagepublicvoidonMessage(Stringmessage,Sessionsession){System.out.println("Message from "+session.getId()+": "+message);try{session.getBasicRemote().sendText(message);}catch(IOExceptionex){ex.printStackTrace();}}/** * The user closes the connection. * * Note: you can't send messages to the client from this method */@OnClosepublicvoidonClose(Sessionsession){System.out.println("Session "+session.getId()+" has ended");}}

Note the “javax.websocket.*” annotations which are used to mark the Java class as WebSocket server side implementation.

Besides the annotation implementation, another “MessageHandlers” programmatic endpoint implementation can by used:

From here: Deploying WebSocket endpoints can be done in two ways. Either deploying via putting the endpoint in the WAR file, or using the ServerContainer methods to deploy the programmatic endpoint in the deployment phase.

For deploying Endpoints as a WAR file:

The classes that are scanned for in WAR are the following ones:

Classes that implement the javax.websocket.ServerApplicationConfig.

Classes annotated with javax.websocket.server.ServerEndpoint.

Classes that extend javax.websocket.Endpoint.

More details can be found from the official document. This way is easy and I think it’s the way that most developers do.

Deploying endpoints via “javax.websocket.server.ServerContainer”

Endpoints may be deployed using javax.websocket.server.ServerContainer during the application initialization phase. For websocket enabled web containers, developers may obtain a reference to the ServerContainer instance by retrieving it as an attribute named javax.websocket.server.ServerContainer on the ServletContext, see the following example for annotated endpoint:

Client Side

It seems there are many ways to implement the client side such as using ”Angular Websocket”:

1234567891011121314151617181920212223242526272829303132333435

<scriptsrc="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script><scriptsrc="bower_components/angular-websocket/angular-websocket.js"></script><sectionng-controller="SomeController"><ulng-repeat="data in MyData.collection track by $index"><li></li></ul></section><script>angular.module('YOUR_APP',['ngWebSocket'// you may also use 'angular-websocket' if you prefer])// WebSocket works as well.factory('MyData',function($websocket){// Open a WebSocket connectionvardataStream=$websocket('ws://website.com/data');varcollection=[];dataStream.onMessage(function(message){collection.push(JSON.parse(message.data));});varmethods={collection:collection,get:function(){dataStream.send(JSON.stringify({action:'get'}));}};returnmethods;}).controller('SomeController',function($scope,MyData){$scope.MyData=MyData;});</script>