preface
We know thatHTTP
The protocol is stateless and connectionless, and adopts the request/response mode. The communication request can only be initiated by the client and responded by the server. This request/response mode is very weak when the client server needs continuous interaction. Before HMTL5 came out, most of the client server continuous interaction is achieved through AJAX polling, but polling efficiency is low, wasting bandwidth and server resources. So WebSocket was invented. WebSocket is a protocol provided by HTML5 for full duplex communication over a single TCP connection. Next I use Spring and WebSocket to achieve a simple chat function, I hope to be helpful to you.
Project directory
Adding a dependency package
pom.xml
<? xml version="1.0" encoding="UTF-8"? > <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> < modelVersion > 4.0.0 < / modelVersion > < groupId > groupId < / groupId > < artifactId > nChat < / artifactId > The < packaging > pom < / packaging > < version > 1.0 - the SNAPSHOT < / version > < dependencies > <! --Sping core dependencies --> <! -- https://mvnrepository.com/artifact/org.springframework/spring-core --> <dependency> < the groupId > org. Springframework < / groupId > < artifactId > spring - the core < / artifactId > < version > 5.1.3. RELEASE < / version > </dependency> <! -- https://mvnrepository.com/artifact/org.springframework/spring-web --> <dependency> < the groupId > org. Springframework < / groupId > < artifactId > spring - web < / artifactId > < version > 5.1.3. RELEASE < / version > </dependency> <! -- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --> <dependency> < the groupId > org. Springframework < / groupId > < artifactId > spring - JDBC < / artifactId > < version > 5.1.3. RELEASE < / version > </dependency> <! -- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> < the groupId > org. Springframework < / groupId > < artifactId > spring - webmvc < / artifactId > < version > 5.1.3. RELEASE < / version > </dependency> <! -- https://mvnrepository.com/artifact/org.springframework/spring-context --> <dependency> < the groupId > org. Springframework < / groupId > < artifactId > spring - the context < / artifactId > < version > 5.1.3. RELEASE < / version > </dependency> <! -- https://mvnrepository.com/artifact/org.springframework/spring-context-support --> <dependency> < the groupId > org. Springframework < / groupId > < artifactId > spring - the context - support < / artifactId > < version > 5.1.3. RELEASE < / version > </dependency> <! -- https://mvnrepository.com/artifact/org.springframework/spring-aop --> <dependency> < the groupId > org. Springframework < / groupId > < artifactId > spring aop - < / artifactId > < version > 5.1.3. RELEASE < / version > </dependency> <! -- https://mvnrepository.com/artifact/org.springframework/spring-test --> <dependency> < the groupId > org. Springframework < / groupId > < artifactId > spring - test < / artifactId > < version > 5.1.3. RELEASE < / version > < scope >test</scope> </dependency> <! --Mybatis dependency --> <! -- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> The < artifactId > mybatis < / artifactId > < version > 3.4.6 < / version > < / dependency > <! -- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring --> <dependency> <groupId>org.mybatis</groupId> < artifactId > mybatis - spring < / artifactId > < version > 1.3.2 < / version > < / dependency > <! -- https://mvnrepository.com/artifact/org.springframework/spring-messaging --> <dependency> < the groupId > org. Springframework < / groupId > < artifactId > spring - messaging < / artifactId > < version > 5.1.3. RELEASE < / version > </dependency> <! -- https://mvnrepository.com/artifact/org.springframework/spring-websocket --> <dependency> < the groupId > org. Springframework < / groupId > < artifactId > spring - websocket < / artifactId > < version > 5.1.2. RELEASE < / version > </dependency> <! --MySQL connection driver --> <! -- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> < artifactId > mysql connector - Java < / artifactId > < version > 8.0.13 < / version > < / dependency > <! -- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <dependency> <groupId>javax.servlet</groupId> < artifactId > javax.mail. The servlet API - < / artifactId > < version > 4.0.1 < / version > < scope > provided < / scope > < / dependency > </dependencies> </project>Copy the code
WebSocket implementation
Java implements WebSocket in many ways, and different vendors implement WebSocket in the same way.
Spring implemented WebSocket
Spring implements WebSocket by adding Spring’s dependency on WebSocket support
<! -- https://mvnrepository.com/artifact/org.springframework/spring-messaging --> <dependency> < the groupId > org. Springframework < / groupId > < artifactId > spring - messaging < / artifactId > < version > 5.1.3. RELEASE < / version > </dependency> <! -- https://mvnrepository.com/artifact/org.springframework/spring-websocket --> <dependency> < the groupId > org. Springframework < / groupId > < artifactId > spring - websocket < / artifactId > < version > 5.1.2. RELEASE < / version > </dependency>Copy the code
In Java import Spring WebSocket package import org. Springframework. Web. Socket. *; Spring implementation of WebSocket needs to write the following items.
- Configuring WebSocket There are two ways to configure WebSocket, one is to write a configuration class, the other is to write a configuration file (XML file), the function of configuring WebSocket is to add WebSocket processor, interceptor to the registry, here I use the configuration class to configure. WebSocketConfig.java
package com.nChat.websocket; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; @Configuration @EnableWebMvc @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { public void RegisterWebSocketHandlers (WebSocketHandlerRegistry WebSocketHandlerRegistry) {/ / this is used for establishing the communication connection webSocketHandlerRegistry .addHandler(new WebSocketHandler(),"/ws/socketServer")
.addInterceptors(new WebSocketInterceptor())
.setAllowedOrigins("*"); }}Copy the code
The purpose of the three annotations is as follows
- @Configuration Annotation: Declare this class to configure the Spring container application context for the Configuration class (equivalent to the web.xml Configuration file) that will be loaded when the project starts.
- @enableWebMVC note: If Spring MVC is enabled, without this, the RequestMapping in Controller is disabled, I don’t know why.
- @enablewebsocket note: EnableWebSocket service.
registerWebSocketHandlers
Method configures the WebSocket entry, domains to allow access, registers WebSocket handlers, interceptors, and so on when requesting access/ws/socketServer
“, the WebSocket connection will be established.
- Write the processor webSockethandler.java
package com.nChat.websocket;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
@Service
public class WebSocketHandler extends TextWebSocketHandler {
public static final Map<Integer,WebSocketSession> USER_SOCKET_SESSION_MAP;
static{
USER_SOCKET_SESSION_MAP = new HashMap<Integer, WebSocketSession>();
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
int uid = Integer.parseInt(session.getAttributes().get("WEBSOCKET_UID").toString()); // Save the session in USER_SOCKET_SESSION_MAP if it is a new user connectionif(USER_SOCKET_SESSION_MAP.get(uid) == null || ! USER_SOCKET_SESSION_MAP.get(uid).isOpen()) { USER_SOCKET_SESSION_MAP.put(uid, session); } super.afterConnectionEstablished(session); } @Override public void handleMessage(WebSocketSession session, WebSocketMessage<? > message) throws Exception { super.handleMessage(session, message); } @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { super.handleTextMessage(session, message); } @Override protected void handlePongMessage(WebSocketSession session, PongMessage message) throws Exception { super.handlePongMessage(session, message); } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { super.handleTransportError(session, exception); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception { super.afterConnectionClosed(session, status); } @Override public booleansupportsPartialMessages() {
returnsuper.supportsPartialMessages(); } /** * @description: sends a message to the specified user * @param: [uid, message] * @return: void
* @author: Xue 8
* @date: 2019/1/19
*/
public void sendMessageToUser(int uid, TextMessage message){
WebSocketSession session = USER_SOCKET_SESSION_MAP.get(uid);
if(session ! = null && session.isOpen()) { try { session.sendMessage(message); } catch (IOException e) { e.printStackTrace(); }}}}Copy the code
The WebSocket processor inherits TextWebSocketHandler (or BinaryWebSocketHandler), where it overwrites the corresponding methods and writes its own business code. When Spring receives a WebSocket event, it calls the corresponding method. Here I have a custom method sendMessageToUser that sends a message to the specified user. A WebSocketSession is an abstraction of a WebSocket. A WebSocketSession is like a dedicated channel between a server and a client. One WebSocketSession corresponds to one user. WebSocket operations are based on this WebSocketSession.
- Write the interceptor WebSocketInterceptor. Java
package com.nChat.websocket;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
import java.util.Collection;
import java.util.Map;
public class WebSocketInterceptor extends HttpSessionHandshakeInterceptor {
public WebSocketInterceptor() {
super();
}
public WebSocketInterceptor(Collection<String> attributeNames) {
super(attributeNames);
}
@Override
public Collection<String> getAttributeNames() {
return super.getAttributeNames();
}
@Override
public void setCopyAllAttributes(boolean copyAllAttributes) {
super.setCopyAllAttributes(copyAllAttributes);
}
@Override
public boolean isCopyAllAttributes() {
return super.isCopyAllAttributes();
}
@Override
public void setCopyHttpSessionId(boolean copyHttpSessionId) {
super.setCopyHttpSessionId(copyHttpSessionId);
}
@Override
public boolean isCopyHttpSessionId() {
return super.isCopyHttpSessionId();
}
@Override
public void setCreateSession(boolean createSession) {
super.setCreateSession(createSession);
}
@Override
public boolean isCreateSession() {
return super.isCreateSession();
}
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
ServletServerHttpRequest servletServerHttpRequest = (ServletServerHttpRequest) request;
int uid = Integer.parseInt(servletServerHttpRequest.getServletRequest().getParameter("uid"));
System.out.println("coming " + uid);
if(uid ! = 0) {// Intercepting the request here saves the UID to the WebSocketSession before holding the hand and lets the handler WebSocketHandler operate on the uid attributes. Put ("WEBSOCKET_UID", uid);
}
return super.beforeHandshake(request, response, wsHandler, attributes);
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) {
System.out.println("out"); super.afterHandshake(request, response, wsHandler, ex); }}Copy the code
WebSocket interceptor inheritance HttpSessionHandshakeInterceptor, before and after the handshake to intercept the request, the request before shaking hands interception, namely when requesting access/ws/socketServer will request to intercept, You can retrieve URL parameters, request headers, protocols, and other information in a request, and then store this information in a WebSocketSession to associate the user with the WebSocketSession.
- Write Spring MVC controller indexController.java
package com.nChat.controller;
import com.nChat.websocket.WebSocketHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.socket.TextMessage;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
@Controller
public class IndexController {
@Autowired
WebSocketHandler webSocketHandler;
@RequestMapping("/send")
public String send(HttpServletRequest request,
HttpServletResponse response){
return "send";
}
@RequestMapping("/doSend")
public String doSend(HttpServletRequest request,
HttpServletResponse response,
@RequestParam(value = "uid") int uid,
@RequestParam(value = "messages") String messages){
HttpSession session = request.getSession(true);
session.setAttribute("SESSION_USERNAME", uid);
webSocketHandler.sendMessageToUser(uid,new TextMessage(messages));
return "send";
}
@RequestMapping("/register")
public String register(HttpServletRequest request,
HttpServletResponse response){
return "register"; }}Copy the code
Note that @requestMapping is different from/WS /socketServer in the WebSocket configuration class, where/WS /socketServer is used to establish WebSocket connections between clients and servers. The Controller @requestMapping is used to handle client requests.
- Write front-end test WebSocket establishment and send information to create a new WebSocket connection, which uses UID to represent the WebSocket connection register.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"% > <! DOCTYPE html PUBLIC"- / / / / W3C DTD HTML 4.01 Transitional / / EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>register</title>
</head>
<body>
<script type="text/javascript" src="http://cdn.bootcss.com/jquery/3.1.0/jquery.min.js"></script>
<script type="text/javascript" src="http://cdn.bootcss.com/sockjs-client/1.1.1/sockjs.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/reconnecting-websocket/1.0.0/reconnecting-websocket.js"></script>
<script type="text/javascript">
var websocket = null;
function createWebSocket() {
if ('WebSocket' in window) {
websocket = new WebSocket("ws://localhost:8080/ws/socketServer? uid=" + $("#uid").val());
console.log($("#uid").val())
}
else if ('MozWebSocket' in window) {
websocket = new MozWebSocket("ws://localhost:8080/ws/socketServer? uid=" + $("#uid").val());
}
else {
websocket = new SockJS("http://localhost:8080/ws/socketServer? uid=" + $("#uid").val());
}
websocket.onopen = onOpen;
websocket.onmessage = onMessage;
websocket.onerror = onError;
websocket.onclose = onClose;
function onOpen(openEvt) {
//alert(openEvt.Data);
}
function onMessage(evt) {
alert(evt.data);
}
function onError() {}function onClose() {
}
window.close=function() { websocket.onclose(); }} </script> Please enter UID: <input rows="5" cols="10" id="uid" name="uid"></input>
<button onclick="createWebSocket();"</button> </body> </ HTML >Copy the code
Send message page, send message according to UID send.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"
pageEncoding="utf-8"% > <! DOCTYPE html PUBLIC"- / / / / W3C DTD HTML 4.01 Transitional / / EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<body>
<h2>send messages</h2>
<body>
<form action="/doSend"> To whom: <inputtype="text" name="uid"/> What message is sent: <inputtype="text" name="messages"/>
<input type="submit" value="Send"/>
</form>
</body>
</body>
</html>
Copy the code
- Run the tests to establish WebSocket connections with UID 1 and UID 2 respectively.
Send a message to the WebSocket with UID 1
Send a message to the WebSocket with UID 2
Tomcat implementation WebSocket
Implementing WebSocket using Tomcat is not as cumbersome as implementing WebSocket in Spring, requiring only one processor to be written. First add dependencies
<! -- https://mvnrepository.com/artifact/javax.websocket/javax.websocket-api --> <dependency> < the groupId > javax.mail. Websocket < / groupId > < artifactId > javax.mail. Websocket API - < / artifactId > < version > 1.1 < / version > <scope>provided</scope> </dependency>Copy the code
Then write the processing class
package com.nChat;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import net.sf.json.JSONObject;
@ServerEndpoint("/websocket/{username}")
public class WebSocket {
private static int onlineCount = 0;
private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();
private Session session;
private String username;
@OnOpen
public void onOpen(@PathParam("username") String username, Session session) throws IOException {
this.username = username;
this.session = session;
addOnlineCount();
clients.put(username, this);
System.out.println("Connected");
}
@OnClose
public void onClose() throws IOException {
clients.remove(username);
subOnlineCount();
}
@OnMessage
public void onMessage(String message) throws IOException {
JSONObject jsonTo = JSONObject.fromObject(message);
if(! jsonTo.get("To").equals("All")){
sendMessageTo("For one person.", jsonTo.get("To").toString());
}else{
sendMessageAll("For all.");
}
}
@OnError
public void onError(Session session, Throwable error) {
error.printStackTrace();
}
public void sendMessageTo(String message, String To) throws IOException {
// session.getBasicRemote().sendText(message);
//session.getAsyncRemote().sendText(message);
for (WebSocket item : clients.values()) {
if (item.username.equals(To) )
item.session.getAsyncRemote().sendText(message);
}
}
public void sendMessageAll(String message) throws IOException {
for (WebSocket item : clients.values()) {
item.session.getAsyncRemote().sendText(message);
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
WebSocket.onlineCount++;
}
public static synchronized void subOnlineCount() {
WebSocket.onlineCount--;
}
public static synchronized Map<String, WebSocket> getClients() {
returnclients; }}Copy the code
Jetty implementation WebSocket
This doesn’t seem common… Here will not demonstrate how to configure, interested in online search related articles.
conclusion
WebSocket is a full-duplex communication protocol provided by HTML5 in a single TCP connection. Manufacturers can implement their own WebSocket framework according to the WebSocket API, such as Spring WebSocket and Tomcat WebSocket. I think the relationship between WebSocket and Spring WebSocket and Tomcat WebSocket is the same as the relationship between JPA and Hibernate and Mybatis. Both WebSocket and JPA have defined standards, and each manufacturer implements its own framework according to this standard.
Complete source code: github.com/xue8/Java-D…
Original address: ddnd.cn/2019/01/19/…