What is the WebSocket
WebSocket is a protocol for full duplex communication over a single TCP connection……………..
Why implement handshake monitoring management
“Broken pipe” will cause an error if the connection is created randomly and left unconnected
On the surface, an error is reported and there is no functional defect. However, the number of requests increases and the system crashes easily. Let me focus on this side.
There are many reasons for the occurrence. At present, the reason for my occurrence is that the client has closed the connection and the server continues to push.
How to use
The webSocket integrated with SpringBoot will be used next
Import the Maven
First, the SpringBoot version
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8. RELEASE</version>
</parent>
Copy the code
Integrated websocket
// Add a web integration<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
Copy the code
Java code
The Config configuration
First, we need to rewrite WebSocketHandlerDecoratorFactory
It is mainly used to monitor the client to come in and close the connection with a handshake
code
Need a class to manage sockets
package com.li.manager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.WebSocketSession;
import java.util.concurrent.ConcurrentHashMap;
/** * Socket manager */
@Slf4j
public class SocketManager {
private static ConcurrentHashMap<String, WebSocketSession> manager = new ConcurrentHashMap<String, WebSocketSession>();
public static void add(String key, WebSocketSession webSocketSession) {
log.info("New webSocket connection {}", key);
manager.put(key, webSocketSession);
}
public static void remove(String key) {
log.info("Remove webSocket connection {}", key);
manager.remove(key);
}
public static WebSocketSession get(String key) {
log.info("Get webSocket connection {}", key);
returnmanager.get(key); }}Copy the code
package com.li.factory;
import com.li.manager.SocketManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.WebSocketHandlerDecorator;
import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory;
import java.security.Principal;
/** * The server and the client are executed when they are shaking hands
@Component
@Slf4j
public class WebSocketDecoratorFactory implements WebSocketHandlerDecoratorFactory {
@Override
public WebSocketHandler decorate(WebSocketHandler handler) {
return new WebSocketHandlerDecorator(handler) {
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
log.info(SessionId = {}, session.getId());
Principal principal = session.getPrincipal();
if(principal ! =null) {
log.info("Key = {} save", principal.getName());
// The socket connection is cached
SocketManager.add(principal.getName(), session);
}
super.afterConnectionEstablished(session);
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
log.info(SessionId = {}, session.getId());
Principal principal = session.getPrincipal();
if(principal ! =null) {
// Identity verification succeeded. Socket connection removed
SocketManager.remove(principal.getName());
}
super.afterConnectionClosed(session, closeStatus); }}; }}Copy the code
Session variables getId() and getPrincipal().getName()
GetId () : returns a unique session identifier.
GetPrincipal () : Returns Principal instance if authenticated, null if not authenticated
Principal: The abstract concept of the Principal, which can be the company ID, name, user’s unique identification token, etc
When you use the code above, you will find that getPrincipal() returns NULL. Why? I also need to rewrite a DefaultHandshakeHandler here
code
package com.li.handler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
import java.util.Map;
/** * We can request information, such as token, or session, to determine whether the user can connect, so as to prevent illegal users */
@Slf4j
@Component
public class PrincipalHandshakeHandler extends DefaultHandshakeHandler {
@Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
/ * * * side can according to your requirements, how to obtain the only value, get the value of the unicode * already, can deal with the connection attributes, in listening WebSocketSession. Both getPrincipal (). The getName () * can realize the Principal () * /
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) request;
HttpServletRequest httpRequest = servletServerHttpRequest.getServletRequest();
/** * get your most familiar stranger, take parameters, you can cookie, request header, or URL carry, here I use url carry */
final String token = httpRequest.getParameter("token");
if (StringUtils.isEmpty(token)) {
return null;
}
return new Principal() {
@Override
public String getName(a) {
returntoken; }}; }return null; }}Copy the code
We have everything we need. Let’s get ready to load
code
package com.li.config;
import com.li.factory.WebSocketDecoratorFactory;
import com.li.handler.PrincipalHandshakeHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration;
/** * WebSocketConfig config */
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Autowired
private WebSocketDecoratorFactory webSocketDecoratorFactory;
@Autowired
private PrincipalHandshakeHandler principalHandshakeHandler;
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
/** * myUrl means that your front end should map */ to the URL
registry.addEndpoint("/myUrl")
.setAllowedOrigins("*")
.setHandshakeHandler(principalHandshakeHandler)
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
/** * queue point-to-point topic broadcast user point-to-point prefix */
registry.enableSimpleBroker("/queue"."/topic");
registry.setUserDestinationPrefix("/user");
}
@Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
registration.addDecoratorFactory(webSocketDecoratorFactory);
super.configureWebSocketTransport(registration); }}Copy the code
It’s finally done
Finally, a request is made via HTTP and sent to the client
code
package com.li.controller;
import com.li.manager.SocketManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.socket.WebSocketSession;
import java.util.Map;
@RestController
@Slf4j
public class TestController {
@Autowired
private SimpMessagingTemplate template;
Var socket = new SockJS(host+'/myUrl' +'? token=1234'); * /
@RequestMapping("/sendUser")
public void sendUser(String token) {
log.info("Token = {}, send hello to it", token);
WebSocketSession webSocketSession = SocketManager.get(token);
if(webSocketSession ! =null) {
/** * prevents broken pipe */
template.convertAndSendToUser(token, "/queue/sendUser"."Hello"); }}/** * broadcasts, the server actively pushes to the connected client */
@RequestMapping("/sendTopic")
public void sendTopic(a) {
template.convertAndSend("/topic/sendTopic"."Good evening, everyone.");
}
/** * The client sends the message, and the server receives the message@param message
*/
// RequestMapping
@MessageMapping("/sendServer")
public void sendServer(String message) {
log.info("message:{}", message);
}
/** * The client sends a message, and everyone receives it, which is equivalent to live speaking **@param message
* @return* /
@MessageMapping("/sendAllUser")
@SendTo("/topic/sendTopic")
public String sendAllUser(String message) {
// The template mode can also be used
return message;
}
/** * point-to-point user chat (); /** * point-to-point user chat ()@RequestBodyVar socket = new SockJS(host+'/myUrl' +'? token=4567'); Token Indicates the specified name *@param map
*/
@MessageMapping("/sendMyUser")
public void sendMyUser(@RequestBody Map<String, String> map) {
log.info("map = {}", map);
WebSocketSession webSocketSession = SocketManager.get(map.get("name"));
if(webSocketSession ! =null) {
log.info("sessionId = {}", webSocketSession.getId());
template.convertAndSendToUser(map.get("name"), "/queue/sendUser", map.get("message")); }}}Copy the code
The front-end code
You can start it directly
<html>
<head>
<meta charset="UTF-8" />
<title>Spring Boot WebSocket+ Broadcast</title>
</head>
<body>
<noscript>
<h2 style="color:#ff0000">It appears that your browser does not support Websocket</h2>
</noscript>
<div>
<div>
<button id="connect" onclick="connect()">The connection</button>
<button id="disconnect" onclick="disconnect();">disconnect</button>
</div>
<div id="conversationDiv">
<label>Enter your name</label> <input type="text" id="name" />
<br>
<label>The input message</label> <input type="text" id="messgae" />
<button id="send" onclick="send();">send</button>
<p id="response"></p>
</div>
</div>
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
var stompClient = null;
//gateway Indicates the gateway address
var host="http://127.0.0.1:8888";
function setConnected(connected) {
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = ! connected;document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
$('#response').html();
}
// SendUser ***********************************************
function connect() {
// address + endpoint path, build webSocket link address, note that the corresponding config addEndpoint
var socket = new SockJS(host+'/myUrl' + '? token=4567');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected:' + frame);
// Listen to the path and callback
stompClient.subscribe('/user/queue/sendUser'.function(response) {
showResponse(response.body);
});
});
}
Var socket = new SockJS(host+'/myUrl'); /* function connect() {addsocket = new SockJS(host+'/myUrl'); stompClient = Stomp.over(socket); stompClient.connect({}, function(frame) { setConnected(true); console.log('Connected:' + frame); Stompclient. subscribe('/topic/sendTopic', function(response) {showResponse(response.body); }); }); } * /
function disconnect() {
if(stompClient ! =null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function send() {
var name = $('#name').val();
var message = $('#messgae').val();
/*// The path where the client sends the message to the server stompClient.send("/sendServer", {}, message); * /
// sendTopic stompClient.send("/sendAllUser", {}, message); // sendTopic stompClient.send("/sendAllUser", {}, message); * /
Token =1234, token=4567 * then you can send */ to the specified user by writing name as token
stompClient.send("/sendMyUser", {}, JSON.stringify({name:name,message:message}));
}
function showResponse(message) {
var response = $('#response');
response.html(message);
}
</script>
</body>
</html>
Copy the code