What is the WebSocket?

WebSocket protocol is a new network protocol based on TCP. It implements full-duplex communication between the browser and the server — allowing the server to actively send messages to the client.

Why WebSocket?

As we all know, in the past, the client wanted to know the processing progress of the server, so it had to use Ajax to poll constantly and let the browser send requests to the server every few seconds, which put great pressure on the server. Another polling method is to use long poll, which is similar to making a phone call and holding the phone until receiving a message. That is to say, after the client initiates a connection, if there is no message, it does not return a response to the client, and the connection phase is always blocked.

WebSocket solves these problems with HTTP. After the server completes the protocol upgrade (HTTP -> WebSocket), the server can actively push information to the client, which solves the synchronization delay caused by polling. Since WebSocket only needs one HTTP handshake, the server can keep communicating with the client until the connection is closed, thus eliminating the need for the server to repeatedly parse the HTTP protocol and reducing the cost of resources.

The back and forth communication is now implemented through SpringBoot integration with WebSocket.

Integrate WebSocket to realize front-end and back-end communication

Project code structure diagram

Rely on the import

SpringBoot2.0’s support for websockets is fantastic, with packages introduced directly.

<dependency>  
	<groupId>org.springframework.boot</groupId>  
	<artifactId>spring-boot-starter-websocket</artifactId>  
</dependency> 
Copy the code

Configuration WebSocketConfig

Enabling WebSocket support is also simple by injecting the ServerEndpointExporter object into the container.

package com.tuhu.websocketsample.configuration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); }}Copy the code

WebSocketServer WebSocketServer

Since WebSocket is a client-server like form (using THE WS protocol), the WebSocketServer here is actually a WS protocol Controller. Enable @serverendpoint (“/ webSocket “), @Component, and implement @onOpen, @onClose,@onMessage, etc

package com.tuhu.websocketsample.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; @Component @serverendpoint ("/ webSocket /{sid}") @slf4J public class WebSocketServer {/** * Static variable used to record the number of current online connections. It should be designed to be thread-safe. */ private static int onlineCount = 0; /** * A thread-safe Set for the Concurrent package, used to hold each client's corresponding MyWebSocket object. */ private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>(); /** * A Session with a client through which to send data to the client */ private Session Session; /** * private String sid=""; @onopen public void OnOpen (Session Session, @pathParam ("sid") String sid) {this. Session = Session;  // Add webSocketSet.add(this); AddOnlineCount (); Log.info (" new window starts listening :"+sid+", number of people currently online :"+ getOnlineCount()); this.sid=sid; Try {sendMessage(" Connection succeeded "); } catch (IOException e) {log.error("websocket IO exception "); @onClose public void OnClose () {// Remove webSocketSet.remove(this) from set; SubOnlineCount (); Log.info (" There is a connection down! "+ getOnlineCount())"; } /** * @param message @param message public void OnMessage (String message, Session Session) {log.info(" received message from window "+ SID +" :"+message); For (WebSocketServer item: webSocketSet) {try {item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } /** * @param session * @param error */ @OnError public void onError(Session session, Throwable error) {log.error(" error "); error.printStackTrace(); Public void sendMessage(String Message) throws IOException { this.session.getBasicRemote().sendText(message); } /** * Send customized messages ** / public static void sendInfo(String message, @pathParam ("sid") String sid) throws IOException { Log.info (" push message to window "+sid+", push content :"+message); for (WebSocketServer item : If (sid==null) {item.sendMessage(message); if(sid==null) {item. }else if(item.sid.equals(sid)){ item.sendMessage(message); } } catch (IOException e) { continue; } } } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketServer.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketServer.onlineCount--; }}Copy the code

Being pushed

To push new messages, write a method on your Controller that calls webSocketServer.sendInfo ()

package com.tuhu.websocketsample.controller; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.ModelAndView; import java.io.IOException; @restController @requestMapping ("/ checkCenter ") Public class CheckCenterController {/** * page request * @param cid * @return */ @GetMapping("/socket/{cid}") public ModelAndView socket(@PathVariable String cid) { ModelAndView mav=new ModelAndView("/socket"); mav.addObject("cid", cid); return mav; @responseBody @requestMapping ("/socket/push/{cid}") public String pushToWeb(@PathVariable String cid,String message) { try { WebSocketServer.sendInfo(message,cid); } catch (IOException e) { e.printStackTrace(); return "error:"+cid+"#"+e.getMessage(); } return "success:"+cid; }}Copy the code

The page initiates a socket request

Then in the page with JS code to call socket, of course, too old browser is not good, general new browser or Google Browser is no problem. Also, remember that the protocol is WS. Open the connection directly from the browser console.

var socket; If (typeof(WebSocket) == "undefined") {console.log(" your browser does not support WebSocket"); if(typeof(WebSocket) == "undefined") {console.log(" Your browser does not support WebSocket"); }else{console.log(" Your browser supports WebSocket"); / / implementation WebSocket object, specify to connect to the server address and port Connect the socket = new WebSocket (" ws: / / localhost: 8080 / WebSocket / 20 "); Onopen = function() {console.log("Socket open "); //socket.send(" This is the message from the client "+ location.href + new Date()); }; Onmessage = function(MSG) {console.log(msg.data); // Find the message to start processing front-end trigger logic}; Onclose = function() {console.log(" socket closed "); }; Onerror = function() {alert(" socket error "); $(window). Unload (function(){// socket.close(); / /}); }Copy the code

Running effect

You can now open the connection in the browser and send messages to the browser by invoking the interface server from the client.

Now open two pages to open two connections:

  • Socket = new WebSocket (” ws: / / localhost: 8080 / WebSocket / 20 “);
  • socket = new WebSocket(“ws://localhost:8080/websocket/22”) ;

Push data to the front end:

  • http://localhost:8080/checkcenter/socket/push/20?message=Hello
  • http://localhost:8080/checkcenter/socket/push/22?message=HelloWorld

You can see that the server has pushed the message to the client

The client also receives the message

First open the page, specify CID, enable socket reception, and then another page to call the push information method encapsulated by the Controller to this CID socket, you can push messages to the front end.

subsequent

ServerEndpointExporter error

org.springframework.beans.factory.BeanCreationException: Error creating bean with the name ‘serverEndpointExporter defined in the class path resource/com/XXX/WebSocketConfig. Class: Invocation of init method failed; nested exception is java.lang.IllegalStateException: javax.websocket.server.ServerContainer not available

If tomcat deployments consistently report this error, remove the injection of @Bean ServerEndpointExporter in WebSocketConfig.

ServerEndpointExporter is a standard implementation provided by Spring that scans the ServerEndpointConfig configuration class and the @ServerEndpoint annotation instance. The rules are simple:

If you use a default embedded container such as Tomcat, you must manually provide ServerEndpointExporter in the context.

ServerEndpointExporter is not required if an external container is used to deploy the WAR package, because SpringBoot assigns the server scanning behavior to the external container by default. Note out the code for the injection bean in WebSocketConfig when deploying online.