原文地址:https://www.cnblogs.com/betterboyz/p/8669879.html

WebSocket协议

WebSocket是一种在单个TCP连接上进行全双工通讯的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。

WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输

STOMP协议

STOMP是面向文本的消息传送协议。STOMP客户端与支持STOMP协议的消息代理进行通信。STOMP使用不同的命令,如连接,发送,订阅,断开等进行通信。

SockJS

SockJS是一个JavaScript库,提供跨浏览器JavaScript的API,创建了一个低延迟、全双工的浏览器和web服务器之间通信通道


以上内容出自维基百科和百度百科

使用websocket有两种方式:1是使用sockjs,2是使用h5的标准。使用Html5标准自然更方便简单,所以记录的是配合h5的使用方法。

1、pom引入

 核心是@ServerEndpoint这个注解。这个注解是Javaee标准里的注解,tomcat7以上已经对其进行了实现,如果是用传统方法使用tomcat发布项目,只要在pom文件中引入javaee标准即可使用。

1     <dependency>
2 <groupId>javax</groupId>
3 <artifactId>javaee-api</artifactId>
4 <version>7.0</version>
5 <scope>provided</scope>
6 </dependency>

但使用springboot的内置tomcat时,就不需要引入javaee-api了,spring-boot已经包含了。使用springboot的websocket功能首先引入springboot组件。

1         <dependency>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-websocket</artifactId>
4 </dependency>

2、使用@ServerEndpoint创立websocket endpoint

  首先要注入ServerEndpointExporter,这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint。要注意,如果使用独立的servlet容器,而不是直接使用springboot的内置容器,就不要注入ServerEndpointExporter,因为它将由容器自己提供和管理。

1     @Configuration
2 public class WebSocketConfig {
3 @Bean
4 public ServerEndpointExporter serverEndpointExporter() {
5 return new ServerEndpointExporter();
6 }
7
8 }

下面事websocket的具体实现方法,代码如下:

  1 import org.springframework.stereotype.Component;
2
3 import javax.websocket.*;
4 import javax.websocket.server.ServerEndpoint;
5 import java.io.IOException;
6 import java.util.concurrent.CopyOnWriteArraySet;
7
8 @ServerEndpoint(value = "/websocket")
9 @Component //此注解千万千万不要忘记,它的主要作用就是将这个监听器纳入到Spring容器中进行管理
10 public class WebSocket {
11 //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
12 private static int onlineCount = 0;
13
14 //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
15 private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<WebSocket>();
16
17 //与某个客户端的连接会话,需要通过它来给客户端发送数据
18 private Session session;
19
20 /**
21 * 连接建立成功调用的方法
22 */
23 @OnOpen
24 public void onOpen(Session session) {
25 this.session = session;
26 webSocketSet.add(this); //加入set中
27 addOnlineCount(); //在线数加1
28 System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
29 try {
30 sendMessage("Hello world");
31 } catch (IOException e) {
32 System.out.println("IO异常");
33 }
34 }
35
36 /**
37 * 连接关闭调用的方法
38 */
39 @OnClose
40 public void onClose() {
41 webSocketSet.remove(this); //从set中删除
42 subOnlineCount(); //在线数减1
43 System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
44 }
45
46 /**
47 * 收到客户端消息后调用的方法
48 *
49 * @param message 客户端发送过来的消息
50 */
51 @OnMessage
52 public void onMessage(String message, Session session) {
53 System.out.println("来自客户端的消息:" + message);
54
55 //群发消息
56 for (WebSocket item : webSocketSet) {
57 try {
58 item.sendMessage(message);
59 } catch (IOException e) {
60 e.printStackTrace();
61 }
62 }
63 }
64
65 /**
66 * 发生错误时调用
67 */
68 @OnError
69 public void onError(Session session, Throwable error) {
70 System.out.println("发生错误");
71 error.printStackTrace();
72 }
73
74
75 public void sendMessage(String message) throws IOException {
76 this.session.getBasicRemote().sendText(message);
77 //this.session.getAsyncRemote().sendText(message);
78 }
79
80
81 /**
82 * 群发自定义消息
83 */
84 public static void sendInfo(String message) throws IOException {
85 for (WebSocket item : webSocketSet) {
86 try {
87 item.sendMessage(message);
88 } catch (IOException e) {
89 continue;
90 }
91 }
92 }
93
94 public static synchronized int getOnlineCount() {
95 return onlineCount;
96 }
97
98 public static synchronized void addOnlineCount() {
99 WebSocket.onlineCount++;
100 }
101
102 public static synchronized void subOnlineCount() {
103 WebSocket.onlineCount--;
104 }
105 }

使用springboot的唯一区别是要@Component声明下,而使用独立容器是由容器自己管理websocket的,但在springboot中连容器都是spring管理的。

虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,所以可以用一个静态set保存起来。

3、前端代码

 1 <!DOCTYPE HTML>
2 <html>
3 <head>
4 <title>My WebSocket</title>
5 </head>
6
7 <body>
8 Welcome<br/>
9 <input id="text" type="text" /><button onclick="send()">Send</button> <button onclick="closeWebSocket()">Close</button>
10 <div id="message">
11 </div>
12 </body>
13
14 <script type="text/javascript">
15 var websocket = null;
16
17 //判断当前浏览器是否支持WebSocket
18 if('WebSocket' in window){
19 websocket = new WebSocket("ws://localhost:8084/websocket");
20 }
21 else{
22 alert('Not support websocket')
23 }
24
25 //连接发生错误的回调方法
26 websocket.onerror = function(){
27 setMessageInnerHTML("error");
28 };
29
30 //连接成功建立的回调方法
31 websocket.onopen = function(event){
32 setMessageInnerHTML("open");
33 }
34
35 //接收到消息的回调方法
36 websocket.onmessage = function(event){
37 setMessageInnerHTML(event.data);
38 }
39
40 //连接关闭的回调方法
41 websocket.onclose = function(){
42 setMessageInnerHTML("close");
43 }
44
45 //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
46 window.onbeforeunload = function(){
47 websocket.close();
48 }
49
50 //将消息显示在网页上
51 function setMessageInnerHTML(innerHTML){
52 document.getElementById('message').innerHTML += innerHTML + '<br/>';
53 }
54
55 //关闭连接
56 function closeWebSocket(){
57 websocket.close();
58 }
59
60 //发送消息
61 function send(){
62 var message = document.getElementById('text').value;
63 websocket.send(message);
64 }
65 </script>
66 </html>

以上代码,实现了websocket简单消息推送,可以实现两个页面间的消息显示,但是Java后台主动推送消息时,无法获取消息推送的websocket下的session,即无法实现websocket下session的共享。

为解决主动推送的难题,需要在建立连接时,将websocket下的session与servlet下的HttpSession(或者其他session,我们这用到了shiro下的session)建立关联关系。

webSocket配置Java类:

 1 import com.bootdo.common.utils.ShiroUtils;
2 import org.apache.catalina.session.StandardSessionFacade;
3 import org.apache.shiro.session.Session;
4 import org.springframework.context.annotation.Bean;
5 import org.springframework.context.annotation.Configuration;
6 import org.springframework.web.socket.server.standard.ServerEndpointExporter;
7
8 import javax.servlet.http.HttpSession;
9 import javax.websocket.HandshakeResponse;
10 import javax.websocket.server.HandshakeRequest;
11 import javax.websocket.server.ServerEndpointConfig;
12 import javax.websocket.server.ServerEndpointConfig.Configurator;
13
14 @Configuration
15 public class WebSocketConfig extends Configurator {
16
17 @Override
18 public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
19 /*如果没有监听器,那么这里获取到的HttpSession是null*/
20 StandardSessionFacade ssf = (StandardSessionFacade) request.getHttpSession();
21 if (ssf != null) {
22 HttpSession httpSession = (HttpSession) request.getHttpSession();
23 //关键操作
24 sec.getUserProperties().put("sessionId", httpSession.getId());
25 System.out.println("获取到的SessionID:" + httpSession.getId());
26 }
27 }
28
29 /**
30 * 引入shiro框架下的session,获取session信息
31 */
32 /*
33 @Override
34 public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
35 Session shiroSession = ShiroUtils.getSubjct().getSession();
36 sec.getUserProperties().put("sessionId", shiroSession.getId());
37 }
38 */
39
40 @Bean
41 public ServerEndpointExporter serverEndpointExporter() {
42 //这个对象说一下,貌似只有服务器是tomcat的时候才需要配置,具体我没有研究
43 return new ServerEndpointExporter();
44 }
45 }

webSocket消息实现类方法:

  1 import org.springframework.stereotype.Component;
2
3 import javax.websocket.*;
4 import javax.websocket.server.ServerEndpoint;
5 import java.io.IOException;
6 import java.util.concurrent.CopyOnWriteArraySet;
7
8 //configurator = WebsocketConfig.class 该属性就是我上面配置的信息
9 @ServerEndpoint(value = "/websocket", configurator = WebSocketConfig.class)
10 @Component //此注解千万千万不要忘记,它的主要作用就是将这个监听器纳入到Spring容器中进行管理
11 public class WebSocket {
12 //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
13 private static int onlineCount = 0;
14
15 //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
16 private static CopyOnWriteArraySet<WebSocket> webSocketSet = new CopyOnWriteArraySet<WebSocket>();
17
18 //与某个客户端的连接会话,需要通过它来给客户端发送数据
19 private Session session;
20
21 /**
22 * 连接建立成功调用的方法
23 * <p>
24 * config用来获取WebsocketConfig中的配置信息
25 */
26 @OnOpen
27 public void onOpen(Session session, EndpointConfig config) {
28
29 //获取WebsocketConfig.java中配置的“sessionId”信息值
30 String httpSessionId = (String) config.getUserProperties().get("sessionId");
31
32 this.session = session;
33 webSocketSet.add(this); //加入set中
34 addOnlineCount(); //在线数加1
35 System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
36 try {
37 sendMessage("Hello world");
38 } catch (IOException e) {
39 System.out.println("IO异常");
40 }
41 }
42
43 /**
44 * 连接关闭调用的方法
45 */
46 @OnClose
47 public void onClose() {
48 webSocketSet.remove(this); //从set中删除
49 subOnlineCount(); //在线数减1
50 System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
51 }
52
53 /**
54 * 收到客户端消息后调用的方法
55 *
56 * @param message 客户端发送过来的消息
57 */
58 @OnMessage
59 public void onMessage(String message, Session session) {
60 System.out.println("来自客户端的消息:" + message);
61
62 //群发消息
63 for (WebSocket item : webSocketSet) {
64 try {
65 item.sendMessage(message);
66 } catch (IOException e) {
67 e.printStackTrace();
68 }
69 }
70 }
71
72 /**
73 * 发生错误时调用
74 */
75 @OnError
76 public void onError(Session session, Throwable error) {
77 System.out.println("发生错误");
78 error.printStackTrace();
79 }
80
81
82 public void sendMessage(String message) throws IOException {
83 this.session.getBasicRemote().sendText(message);
84 //this.session.getAsyncRemote().sendText(message);
85 }
86
87
88 /**
89 * 群发自定义消息
90 */
91 public static void sendInfo(String message) throws IOException {
92 for (WebSocket item : webSocketSet) {
93 try {
94 item.sendMessage(message);
95 } catch (IOException e) {
96 continue;
97 }
98 }
99 }
100
101 public static synchronized int getOnlineCount() {
102 return onlineCount;
103 }
104
105 public static synchronized void addOnlineCount() {
106 WebSocket.onlineCount++;
107 }
108
109 public static synchronized void subOnlineCount() {
110 WebSocket.onlineCount--;
111 }
112 }

注意,有上面配置后,如果配置获取的信息为null,需加入监听实现类:

 1 import org.springframework.stereotype.Component;
2
3 import javax.servlet.ServletRequestEvent;
4 import javax.servlet.ServletRequestListener;
5 import javax.servlet.http.HttpServletRequest;
6 import javax.servlet.http.HttpSession;
7
8 /**
9 * 监听器类:主要任务是用ServletRequest将我们的HttpSession携带过去
10 */
11 @Component //此注解千万千万不要忘记,它的主要作用就是将这个监听器纳入到Spring容器中进行管理,相当于注册监听吧
12 public class RequestListener implements ServletRequestListener {
13 @Override
14 public void requestInitialized(ServletRequestEvent sre) {
15 //将所有request请求都携带上httpSession
16 HttpSession httpSession= ((HttpServletRequest) sre.getServletRequest()).getSession();
17 System.out.println("将所有request请求都携带上httpSession " + httpSession.getId());
18 }
19
20 public RequestListener() {
21 }
22
23 @Override
24 public void requestDestroyed(ServletRequestEvent arg0) {
25 }
26 }

对应的前端页面无需改变。

spring boot下WebSocket消息推送(转)的更多相关文章

  1. spring boot下WebSocket消息推送

    WebSocket协议 WebSocket是一种在单个TCP连接上进行全双工通讯的协议.WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范.WebSo ...

  2. 【WebSocket】WebSocket消息推送

    准备使用WebSocket实现Java与Vue或者安卓间的实时通信,实现私密聊天.群聊.查询下资料备用. WebSocket客户端 websocket允许通过JavaScript建立与远程服务器的连接 ...

  3. node.js Websocket消息推送---GoEasy

    Goeasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送!个人感觉goeasy推送更稳定,推送 速度快,代码简单易懂上手快 浏览器兼容性:GoEasy推送 支持websocket ...

  4. C(++) Websocket消息推送---GoEasy

    Goeasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送!个人感觉goeasy推送更稳定,推送 速度快,代码简单易懂上手快 浏览器兼容性:GoEasy推送 支持websocket ...

  5. spring+rabbitmq+stomp搭建websocket消息推送(非spring boot方式)

    前言: 两年前做过spring+activemq+stomp的ws推送,那个做起来很简单,但现在公司用的mq中间件是rabbitmq,因此需要通过rabbitmq去做ws通信.仔细搜了搜百度/谷歌,网 ...

  6. websocket消息推送实现

    一.服务层 package com.demo.websocket; import java.io.IOException; import java.util.Iterator; import java ...

  7. WebSocket消息推送

    WebSocket协议是基于TCP的一种新的网络协议,应用层,是TCP/IP协议的子集. 它实现了浏览器与服务器全双工(full-duplex)通信,客户端和服务器都可以向对方主动发送和接收数据.在J ...

  8. Spring mvc服务端消息推送(SSE技术)

    SSE技术是基于单工通信模式,只是单纯的客户端向服务端发送请求,服务端不会主动发送给客户端.服务端采取的策略是抓住这个请求不放,等数据更新的时候才返回给客户端,当客户端接收到消息后,再向服务端发送请求 ...

  9. [置顶] spring集成mina 实现消息推送以及转发

    spring集成mina: 在学习mina这块时,在网上找了很多资料,只有一些demo,只能实现客户端向服务端发送消息.建立长连接之类.但是实际上在项目中,并不简单实现这些,还有业务逻辑之类的处理以及 ...

随机推荐

  1. HTTP 错误 404.0 - Not Found 您要找的资源已被删除、已更名或暂时不可用。

    现象:打开一个页面,一直报404异常,但是文件是存在的,打开同一目录下的其它文件都没问题,改文件名也不行,始终找不到原因 解决方案:404异常是一个幌子,实际异常是页面读取了null值,应该报空引用, ...

  2. 项目中集成Easyui-Tree,可拖拽更新节点

    由于公司架构较旧,使用的jdk版本为1.4,页面上也没有el表达式. Html <ul class="easyui-tree" id="data"> ...

  3. 关于V1.6.0版本的项目总结

    10月28号,我们开了1.6项目的总结大会,包括Ting总在内,前端.后台.运维都来参加总结大会了.虽然目标基本达成,但同时也暴露出很多问题,我们希望各方能够坐下来总结一下自己或者各个相关团队的功过是 ...

  4. linux gcc升级备忘录

    1. 拉包 sudo wget http://mirrors.opencas.org/gnu/gcc/gcc-6.3.0/gcc-6.3.0.tar.bz2 #如果以上给出的镜像不可用,也可以是使用h ...

  5. 重写alert方法完成类似gmail的友好提示

    当在网页中调用aelrt()方法的时候,系统会自动显示友好的提示方式 . 下面是css样式控制代码: /*----------------------------------------------- ...

  6. sqlserver中事务总结:begin tran,rollback tran,commit tran

     第1个相关用法:摘自:https://shiyousan.com/post/f13d29b7-0d87-4168-bd8b-8b28b0991b5a 以下是出现错误的SQL部分语句: 此错误的原因是 ...

  7. TensorFlow环境搭建及安装教程

    1.安装虚拟环境virtualenv相关配置(创建了python3.5的环境) 2.http://docs.nvidia.com/cuda/cuda-installation-guide-linux/ ...

  8. NDK下IPC问题

    由于AllJoyn的join session timeout问题一直无法解决,我们怀疑AllJoyn有些内部变量没有清理干净,因此考虑将AllJoyn相关功能放到一个单独的进程中,一旦join ses ...

  9. Mac OS X 10.10 执行 Eclipse 提示须要安装 Java

  10. Mysql 如何查询两个时间段之间的数据?

    Mysql 如何查询两个时间段之间的数据?