spring boot 集成 websocket 实现消息主动

来源:https://www.cnblogs.com/leigepython/p/11058902.html

pom.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
 4     <modelVersion>4.0.0</modelVersion>
 5     <parent>
 6         <groupId>org.springframework.boot</groupId>
 7         <artifactId>spring-boot-starter-parent</artifactId>
 8         <version>2.1.1.RELEASE</version>
 9         <relativePath/> <!-- lookup parent from repository -->
10     </parent>
11     <groupId>com.zhengcj</groupId>
12     <artifactId>spring-boot-09-websocket</artifactId>
13     <version>0.0.1-SNAPSHOT</version>
14     <name>spring-boot-09-websocket</name>
15     <description>Demo project for Spring Boot</description>
16 
17     <properties>
18         <java.version>1.8</java.version>
19     </properties>
20 
21     <dependencies>
22         <dependency>
23             <groupId>org.springframework.boot</groupId>
24             <artifactId>spring-boot-starter-thymeleaf</artifactId>
25         </dependency>
26         <dependency>
27             <groupId>org.springframework.boot</groupId>
28             <artifactId>spring-boot-starter-web</artifactId>
29         </dependency>
30         <dependency>
31             <groupId>org.springframework.boot</groupId>
32             <artifactId>spring-boot-starter-websocket</artifactId>
33         </dependency>
34 
35         <dependency>
36             <groupId>org.springframework.boot</groupId>
37             <artifactId>spring-boot-starter-test</artifactId>
38             <scope>test</scope>
39         </dependency>
40     </dependencies>
41 
42     <build>
43         <plugins>
44             <plugin>
45                 <groupId>org.springframework.boot</groupId>
46                 <artifactId>spring-boot-maven-plugin</artifactId>
47             </plugin>
48         </plugins>
49     </build>
50     
51 
52 </project>

SpringBoot09WebsocketApplication.java

 1 package com.zhengcj.websocket;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 
 6 /**
 7  * 地址:https://www.cnblogs.com/leigepython/p/11058902.html
 8  * @author zhengcj
 9  *
10  */
11 @SpringBootApplication
12 public class SpringBoot09WebsocketApplication {
13 
14     public static void main(String[] args) {
15         SpringApplication.run(SpringBoot09WebsocketApplication.class, args);
16     }
17 
18 }

WebSocketConfig.java

 1 package com.zhengcj.websocket.config;
 2 
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.Configuration;
 5 import org.springframework.web.socket.server.standard.ServerEndpointExporter;
 6 
 7 /**
 8  * @author zhengcj
 9  * @Date 2020年4月27日 上午11:37:26
10  * @version
11  * @Description  socket配置类,往 spring 容器中注入ServerEndpointExporter实例
12  *
13  */
14 @Configuration
15 public class WebSocketConfig {
16     @Bean
17     public ServerEndpointExporter serverEndpointExporter() {
18         return new ServerEndpointExporter();
19     }
20 
21 }

WebSocketServer.java

  1 package com.zhengcj.websocket.common;
  2 
  3 import java.io.IOException;
  4 import java.util.HashMap;
  5 import java.util.Map;
  6 import java.util.concurrent.atomic.AtomicInteger;
  7 
  8 import javax.websocket.OnClose;
  9 import javax.websocket.OnError;
 10 import javax.websocket.OnMessage;
 11 import javax.websocket.OnOpen;
 12 import javax.websocket.Session;
 13 import javax.websocket.server.PathParam;
 14 import javax.websocket.server.ServerEndpoint;
 15 
 16 import org.springframework.stereotype.Component;
 17 
 18 /**
 19  * WebSocket服务端代码,包含接收消息,推送消息等接口
 20  * @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
 21  *  注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
 22  * @author zhengcj
 23  * @Date 2020年4月27日 下午1:36:13
 24  * @version
 25  * @Description
 26  */
 27 @Component
 28 @ServerEndpoint(value = "/socket/{name}")
 29 public class WebSocketServer {
 30 
 31     // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
 32     private static AtomicInteger online = new AtomicInteger();
 33     // concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketServer对象。
 34 //    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
 35     private static Map<String, Session> sessionPools = new HashMap<>();
 36 
 37     /**
 38      * 发送消息方法
 39      * @param session 客户端与socket建立的会话
 40      * @param message 消息
 41      * @throws IOException
 42      */
 43     public void sendMessage(Session session, String message) throws IOException {
 44         if (session != null) {
 45             session.getBasicRemote().sendText(message);
 46         }
 47     }
 48 
 49     /**
 50      * 连接建立成功调用
 51      * @param sessionv 客户端与socket建立的会话
 52      * @param userName 客户端的userName
 53      */
 54     @OnOpen
 55     public void onOpen(Session session, @PathParam(value = "name") String userName) {
 56         sessionPools.put(userName, session);
 57         addOnlineCount();
 58         System.out.println(userName + "加入webSocket!当前人数为" + online);
 59         try {
 60             sendMessage(session, "欢迎" + userName + "加入连接!");
 61         } catch (IOException e) {
 62             e.printStackTrace();
 63         }
 64     }
 65 
 66     /**
 67      * 关闭连接时调用
 68      * @param userName 关闭连接的客户端的姓名
 69      */
 70     @OnClose
 71     public void onClose(@PathParam(value = "name") String userName) {
 72         sessionPools.remove(userName);
 73         subOnlineCount();
 74         System.out.println(userName + "断开webSocket连接!当前人数为" + online);
 75     }
 76 
 77     /**
 78      * 收到客户端消息时触发(群发)
 79      * @param message
 80      * @throws IOException
 81      */
 82     @OnMessage
 83     public void onMessage(String message) throws IOException {
 84         System.out.println("群发信息:"+message);
 85         for (Session session : sessionPools.values()) {
 86             try {
 87                 sendMessage(session, message);
 88             } catch (Exception e) {
 89                 e.printStackTrace();
 90                 continue;
 91             }
 92         }
 93     }
 94 
 95     /**
 96      * 发生错误时候
 97      * @param session
 98      * @param throwable
 99      */
100     @OnError
101     public void onError(Session session, Throwable throwable) {
102         System.out.println("发生错误");
103         throwable.printStackTrace();
104     }
105 
106     /**
107      * 给指定用户发送消息
108      * 
109      * @param userName
110      *            用户名
111      * @param message
112      *            消息
113      * @throws IOException
114      */
115     public void sendInfo(String userName, String message) {
116         Session session = sessionPools.get(userName);
117         try {
118             sendMessage(session, message);
119         } catch (Exception e) {
120             e.printStackTrace();
121         }
122     }
123 
124     public static void addOnlineCount() {
125         online.incrementAndGet();
126     }
127 
128     public static void subOnlineCount() {
129         online.decrementAndGet();
130     }
131 }

WebMvcConfig.java

 1 package com.zhengcj.websocket.config;
 2 
 3 import org.springframework.context.annotation.Configuration;
 4 import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
 5 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 6 
 7 /** 
 8  * @author zhengcj
 9  * @Date 2020年4月27日 下午1:38:31
10  * @version
11  * @Description 
12  *
13  * 在SpringBoot2.0及Spring 5.0 WebMvcConfigurerAdapter已被废弃,目前找到解决方案就有
14  * 1 直接实现WebMvcConfigurer (官方推荐)
15  * 2 直接继承WebMvcConfigurationSupport
16  * @ https://blog.csdn.net/lenkvin/article/details/79482205
17  */
18 @Configuration
19 public class WebMvcConfig implements WebMvcConfigurer{
20     /**
21      * 为各个页面提供路径映射
22      * @param registry
23      */
24     @Override
25     public void addViewControllers(ViewControllerRegistry registry) {
26         registry.addViewController("/zhengcj").setViewName("zhengcj");
27         registry.addViewController("/yuzh").setViewName("yuzh");
28     }
29 }

SocketController.java

 1 package com.zhengcj.websocket.controller;
 2 
 3 import java.io.IOException;
 4 
 5 import javax.annotation.Resource;
 6 
 7 import org.springframework.web.bind.annotation.GetMapping;
 8 import org.springframework.web.bind.annotation.RequestParam;
 9 import org.springframework.web.bind.annotation.RestController;
10 
11 import com.zhengcj.websocket.common.WebSocketServer;
12 
13 /** 
14  * @author zhengcj
15  * @Date 2020年4月27日 下午1:44:32
16  * @version
17  * @Description 
18  *
19  */
20 @RestController
21 public class SocketController {
22     @Resource
23     private WebSocketServer webSocketServer;
24 
25     /**
26      * 给指定用户推送消息
27      * @param userName 用户名
28      * @param message 消息
29      * @throws IOException
30      */
31     @GetMapping("/socket")
32     public void pushOneUser(@RequestParam String userName, @RequestParam String message){
33         System.err.println("====socket===="+message);
34         webSocketServer.sendInfo(userName, message);
35     }
36 
37     /**
38      * 给所有用户推送消息
39      * @param message 消息
40      * @throws IOException
41      */
42     @GetMapping("/socket/all")
43     public void pushAllUser(@RequestParam String message){
44         try {
45             System.err.println("====socket/all===="+message);
46             webSocketServer.onMessage(message);
47         } catch (IOException e) {
48             e.printStackTrace();
49         }
50     }
51 }

yuzh.html

<!DOCTYPE HTML>
<html>
<head>
<title>WebSocket</title>
</head>

<body>
	Welcome
	<br />
	<input id="text" type="text" />
	<button onclick="send()">Send</button>
	<button onclick="closeWebSocket()">Close</button>
	<div id="message"></div>
</body>

<script type="text/javascript">
	var websocket = null;

	//判断当前浏览器是否支持WebSocket
	if ('WebSocket' in window) {
		websocket = new WebSocket("ws://localhost:8080/socket/yuzh");
	} else {
		alert('Not support websocket')
	}

	//连接发生错误的回调方法
	websocket.onerror = function() {
		setMessageInnerHTML("error");
	};

	//连接成功建立的回调方法
	websocket.onopen = function(event) {
		setMessageInnerHTML("open");
	}

	//接收到消息的回调方法
	websocket.onmessage = function(event) {
		setMessageInnerHTML(event.data);
	}

	//连接关闭的回调方法
	websocket.onclose = function() {
		setMessageInnerHTML("close");
	}

	//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
	window.onbeforeunload = function() {
		websocket.close();
	}

	//将消息显示在网页上
	function setMessageInnerHTML(innerHTML) {
		document.getElementById('message').innerHTML += innerHTML + '<br/>';
	}

	//关闭连接
	function closeWebSocket() {
		websocket.close();
	}

	//发送消息
	function send() {
		var message = document.getElementById('text').value;
		websocket.send(message);
	}
</script>
</html>

  zhengcj.html

<!DOCTYPE HTML>
<html>
<head>
<title>WebSocket</title>
</head>

<body>
	Welcome
	<br />
	<input id="text" type="text" />
	<button onclick="send()">Send</button>
	<button onclick="closeWebSocket()">Close</button>
	<div id="message"></div>
</body>

<script type="text/javascript">
	var websocket = null;

	//判断当前浏览器是否支持WebSocket
	if ('WebSocket' in window) {
		websocket = new WebSocket("ws://localhost:8080/socket/zhengcj");
	} else {
		alert('Not support websocket')
	}

	//连接发生错误的回调方法
	websocket.onerror = function() {
		setMessageInnerHTML("error");
	};

	//连接成功建立的回调方法
	websocket.onopen = function(event) {
		setMessageInnerHTML("open");
	}

	//接收到消息的回调方法
	websocket.onmessage = function(event) {
		setMessageInnerHTML(event.data);
	}

	//连接关闭的回调方法
	websocket.onclose = function() {
		setMessageInnerHTML("close");
	}

	//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
	window.onbeforeunload = function() {
		websocket.close();
	}

	//将消息显示在网页上
	function setMessageInnerHTML(innerHTML) {
		document.getElementById('message').innerHTML += innerHTML + '<br/>';
	}

	//关闭连接
	function closeWebSocket() {
		websocket.close();
	}

	//发送消息
	function send() {
		var message = document.getElementById('text').value;
		websocket.send(message);
	}
</script>
</html>

  

原文地址:https://www.cnblogs.com/zcjyzh/p/14272537.html