Project scenario:
WebSocket is a network protocol for full-duplex communication between client and server. It allows real-time, bidirectional data transfer over a single TCP connection.
In actual projects, specific requirements were encountered and the WebSocket network protocol was used. My requirement is that I need to continuously call the third-party interface, and the frequency and speed of the calls are controlled by the front end. For example, if called once every 50ms, the 50 speed parameter is controlled by the front end. It can be understood as the effect of acceleration and deceleration.
Problem description:
If the front-end uses timers and other things to frequently send requests to the back-end, this is obviously unreasonable. In all this scenario, WebSocket can be used for full-duplex communication between the client and the server. To put it bluntly, the front-end and back-end establish a connection, and requests are no longer frequently initiated by the front-end. Instead, the backend controls the request. A simple understanding is that the backend defines a url path of WebSocket’s ws, and the frontend initiates a connection to the backend based on this exclusive url path. After the connection is successful, the front end sends a message to the back end, and the back end responds to the front end after receiving it.
Front-end code:
Tip: The following is a simple dome case with detailed comments. Through these buttons, you can open the console to see the specific effect, which can be used as a reference.
<template> <div class="diagram"> <button @click="webSocket()">Establish socket connection</button> <button @click="websocketclose()">Connection closed</button><br> <button @click="websocketsend(num2)">Data sending change 150</button> <button @click="websocketsend(num1)">Data sending change 100</button> <button @click="websocketsend(num)">Data sent 50</button><br> </div> </template> <script> export default { name: "test", data() { return { num: 50, num1 : "updateVelocityValue:100", num2 : "updateVelocityValue:150", }; }, created() { //this.webSocket(); // Connect to WebSocket }, destroyed: function () { this. websocket close(); }, methods: { say() { if (this.websock == null) { alert("Connection exception: null"); return 'null' } if(this.userInput===""){ this.tips('Please enter the chat content'); }else{ this.tips(''); } this.websocketsend(this.userInput); this. userInput = ''; }, //WebSocket connection webSocket: function () { // establish socket connection if ('WebSocket' in window) {//Determine whether the current browser supports webSocket this.websock = new WebSocket("ws://localhost:8010/websocket/343369");//Establish a connection with the back-end service } else { alert('Your browser does not support websocket yet:('); } console.log(this.websock); this.websock.onopen = this.websocketonopen; this.websock.onerror = this.websocketonerror; this.websock.onmessage = this.websocketonmessage; this.websock.onclose = this.websocketclose; }, // send data websocketsend :function (data) { this. websock. send(data) }, // data reception websocketonmessage :function(e) { console.log(e.data); }, //WebSocket connection closed websocketclose :function(e) { this. websock. close(); console.log("Connection closed"); }, //WebSocket connection error occurred websocketonerror :function(e) { console.log("WebSocket connection error"); }, } }; </script> <style lang="less" scoped> </style>
Backend code (three steps):
Tip: SpringBoot used in the backend is divided into 3 steps. As follows:
First step: Maven dependencies
<!--websocket--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> <version>1.3.5.RELEASE</version> </dependency>
Step 2: Configuration Class
package com.it.config; 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(); } }
The third step: specific implementation (you can read the comments, it is very clear and brainless)
Code description: When the front end clicks the socket connection button, it will establish a connection with the back end and enter the back end onOpen() method. The ws path needs to be noted and the comments are described. The front end clicks the data again to send 50, and sends a message to the back end. The back end enters the onMessage() method, receives the message and processes the business logic. I will judge whether it is the default 50 parameter or a modified parameter. If the parameter is 50, open a thread. to execute business logic. After processing things, the front end clicks on the connection to close. This channel will be closed.
package com.it.websocket; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.CopyOnWriteArraySet; import com.alibaba.fastjson.JSON; import com.it.domain.User; import com.it.dto.MyResponseDto; import com.it.service.impl.EmulationOutcomeServiceImpl; import com.it.service.impl.UserServiceImpl; import com.it.util.DateUtil; import com.it.util.SpringContextUtils; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Component; import lombok.extern.slf4j.Slf4j; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; @Slf4j @ServerEndpoint("/websocket/{userName}") //It is the url when you connect. If the backend is 127.0.0.1:8080/websocket/Zhang San, then write the front-end websocket connection url @Component // Don’t forget this annotation. Its main function is to incorporate this listener into the Spring container for management. public class WebSocketServer { //Used to record the current number of online connections. It should be designed to be thread-safe private static int onlineCount = 0; //The thread-safe Set of the concurrent package is used to store the MyWebSocket object corresponding to each client. private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>(); //The connection session with a client needs to be used to send data to the client private Session session; //parameters for continuous simulation private Integer velocityValue = 50; //Thread used to perform continuous simulation private Thread simulationThread; //user name private String userName; /** * Method called when the connection is successful (can receive one parameter) */ @OnOpen public void onOpen(@PathParam("userName") String userName,Session session) { this.userName =userName; this.session = session; webSocketSet.add(this); addOnlineCount(); log.info("The connection was established successfully: the current number of people online is (" + getOnlineCount() + "people)"); } /** * Processing logic after receiving client message * @param message message sent by the client */ @OnMessage public void onMessage(String message, Session session) { log.info("Received a message from the client:" + message); if (message. startsWith("updateVelocityValue:")) { int newVelocityValue = Integer.parseInt(message.substring("updateVelocityValue:".length())); this.velocityValue = newVelocityValue; } else { startSimulation(); } } /** * Thread that executes continuous simulation */ private void startSimulation() { stopSimulation(); // first stop the existing simulation thread simulationThread = new Thread(() -> { while (simulationThread != null) { try { ApplicationContext context = SpringContextUtils. getApplicationContext(); MyResponseDto myResponseDto =null; if (context == null) { System.out.println("ApplicationContext is null"); } else { //Get execution status information of process simulation EmulationOutcomeServiceImpl emulationOutcomeService = context.getBean(EmulationOutcomeServiceImpl.class); if (emulationOutcomeService == null) { System.out.println("emulationOutcomeService is null"); } else { myResponseDto = emulationOutcomeService.sustainEmulation(velocityValue); } //Get user information according to user name (set current simulation time and percentage progress) UserServiceImpl userService = context. getBean(UserServiceImpl. class); if (userService == null) { System.out.println("userService is null"); } else { User user = userService. getUserByUserName(userName); if (user != null){ //The time the engine returns long time = Long. parseLong(myResponseDto. getTime()); //Get the start time entered by the user SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date startDate = null; try { startDate = sdf.parse(DateUtil.forString(user.getStartEmulationTime(), "yyyy-MM-dd HH:mm:ss")); } catch (ParseException e) { throw new RuntimeException(e); } long startTime = startDate.getTime() / 1000; // Convert milliseconds to seconds //Convert the merged current time into a date string, assign it to the myResponseDto object, and return to the front end Date date = new Date(time + startTime); String formattedDate = sdf. format(date); myResponseDto.setTime(formattedDate); //Set the percentage progress algorithm Date startDateTime = user. getStartEmulationTime(); Date endDateTime = user.getEndEmulationTime(); Date currentDateTime = sdf.parse(formattedDate); long totalTime = endDateTime.getTime() - startDateTime.getTime(); long elapsedTime = currentDateTime.getTime() - startDateTime.getTime(); double progress = (double) elapsedTime / totalTime; myResponseDto.setProgress((int) (progress * 100) + "%"); } } } String jsonStr = JSON.toJSONString(myResponseDto); sendInfo(jsonStr); Thread.sleep(velocityValue); } catch (Exception e) { log.info("Connection disconnected"); throw new RuntimeException(e); } } }); //Start the thread simulationThread.start(); } //Close the thread of continuous simulation (Scheme 2: mark the thread as interrupted) private void stopSimulation() { System.out.println("---"); if (simulationThread != null & amp; & amp; simulationThread.isAlive()) { simulationThread.interrupt(); try { simulationThread.join(); } catch (InterruptedException e) { Thread. currentThread(). interrupt(); } simulationThread = null; } } /** * Send a message to the front end. */ public static void sendInfo(String message) throws IOException { log.info(message); for (WebSocketServer item : webSocketSet) { try { item.sendMessage(message); } catch (IOException e) { continue; } } } //Send a message public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } /** * Called when closing the connection */ @OnClose public void onClose() { webSocketSet.remove(this); subOnlineCount(); this.stopSimulation(); log.info("A connection is closed! The current online number is" + getOnlineCount()); } /** * Called when an error occurs */ @OnError public void onError(Session session, Throwable error) { log.error("An error occurred"); error.printStackTrace(); } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketServer.onlineCount + + ; } public static synchronized void subOnlineCount() { WebSocketServer.onlineCount--; } }
The above is only a very shallow use, if it is helpful to you, Sanlian supports it, it really can’t be more detailed! ! !