飞道的博客

SpringBoot 整合WebSocket 简单实战案例

511人阅读  评论(0)

前言


这个简单实战案例主要目的是让大家了解websocket的一些简单使用,如果你已经知道怎么玩websocket了,像稍微再深入一下,可以看这篇,

《Springboot 整合Websocket,Stomp协议,使用rabbitmq做消息代理,消息缓存》:
https://mp.csdn.net/console/editor/html/108276136

但是如果你是第一次尝试整合websocket,我还是建议你把当前这篇看一看,跟着做下实战案例。

 

正文

 

先看看这次实践的目录结构:


两个页面分别模拟不同用户接入websocket。

 

 ------接下来,我们开始整合WebSocket------

先是pom.xml添加依赖:


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

PS:application.properties不需要添加任何配置 ,我只设置了一下服务server.port=8083

接着,创建节点配置类WebSocketStompConfig.java:


  
  1. import org.springframework.context.annotation.Bean;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.socket.server.standard.ServerEndpointExporter;
  4. @Configuration
  5. public class WebSocketStompConfig {
  6. //这个bean的注册,用于扫描带有@ServerEndpoint的注解成为websocket ,如果你使用外置的tomcat就不需要该配置文件
  7. @Bean
  8. public ServerEndpointExporter serverEndpointExporter()
  9. {
  10. return new ServerEndpointExporter();
  11. }
  12. }

然后是WebSocket配置类,WebSocket.java:

(这里面包含这单独发送消息,群发,监听上下线等等方法)


  
  1. import java.io.IOException;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.Map;
  5. import java.util.Set;
  6. import java.util.concurrent.ConcurrentHashMap;
  7. import java.util.concurrent.CopyOnWriteArraySet;
  8. import java.util.concurrent.atomic.AtomicInteger;
  9. import javax.websocket.OnClose;
  10. import javax.websocket.OnError;
  11. import javax.websocket.OnMessage;
  12. import javax.websocket.OnOpen;
  13. import javax.websocket.Session;
  14. import javax.websocket.server.PathParam;
  15. import javax.websocket.server.ServerEndpoint;
  16. import com.alibaba.fastjson.JSON;
  17. import com.alibaba.fastjson.JSONObject;
  18. import com.google.common.collect.Maps;
  19. import org.slf4j.Logger;
  20. import org.slf4j.LoggerFactory;
  21. import org.springframework.stereotype.Component;
  22. /**
  23. * @Author:JCccc
  24. * @Description
  25. * @Date: created in 15:56 2019/5/13
  26. */
  27. @Component
  28. @ServerEndpoint(value = "/connectWebSocket/{userId}")
  29. public class WebSocket {
  30. private Logger logger = LoggerFactory.getLogger( this.getClass());
  31. /**
  32. * 在线人数
  33. */
  34. public static int onlineNumber = 0;
  35. /**
  36. * 以用户的姓名为key,WebSocket为对象保存起来
  37. */
  38. private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();
  39. /**
  40. * 会话
  41. */
  42. private Session session;
  43. /**
  44. * 用户名称
  45. */
  46. private String userId;
  47. /**
  48. * 建立连接
  49. *
  50. * @param session
  51. */
  52. @OnOpen
  53. public void onOpen(@PathParam("userId") String userId, Session session)
  54. {
  55. onlineNumber++;
  56. logger.info( "现在来连接的客户id:"+session.getId()+ "用户名:"+userId);
  57. this.userId = userId;
  58. this.session = session;
  59. // logger.info("有新连接加入! 当前在线人数" + onlineNumber);
  60. try {
  61. //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
  62. //先给所有人发送通知,说我上线了
  63. Map<String,Object> map1 = Maps.newHashMap();
  64. map1.put( "messageType", 1);
  65. map1.put( "userId",userId);
  66. sendMessageAll(JSON.toJSONString(map1),userId);
  67. //把自己的信息加入到map当中去
  68. clients.put(userId, this);
  69. logger.info( "有连接关闭! 当前在线人数" + clients.size());
  70. //给自己发一条消息:告诉自己现在都有谁在线
  71. Map<String,Object> map2 = Maps.newHashMap();
  72. map2.put( "messageType", 3);
  73. //移除掉自己
  74. Set<String> set = clients.keySet();
  75. map2.put( "onlineUsers",set);
  76. sendMessageTo(JSON.toJSONString(map2),userId);
  77. }
  78. catch (IOException e){
  79. logger.info(userId+ "上线的时候通知所有人发生了错误");
  80. }
  81. }
  82. @OnError
  83. public void onError(Session session, Throwable error) {
  84. logger.info( "服务端发生了错误"+error.getMessage());
  85. //error.printStackTrace();
  86. }
  87. /**
  88. * 连接关闭
  89. */
  90. @OnClose
  91. public void onClose()
  92. {
  93. onlineNumber--;
  94. //webSockets.remove(this);
  95. clients.remove(userId);
  96. try {
  97. //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
  98. Map<String,Object> map1 = Maps.newHashMap();
  99. map1.put( "messageType", 2);
  100. map1.put( "onlineUsers",clients.keySet());
  101. map1.put( "userId",userId);
  102. sendMessageAll(JSON.toJSONString(map1),userId);
  103. }
  104. catch (IOException e){
  105. logger.info(userId+ "下线的时候通知所有人发生了错误");
  106. }
  107. //logger.info("有连接关闭! 当前在线人数" + onlineNumber);
  108. logger.info( "有连接关闭! 当前在线人数" + clients.size());
  109. }
  110. /**
  111. * 收到客户端的消息
  112. *
  113. * @param message 消息
  114. * @param session 会话
  115. */
  116. @OnMessage
  117. public void onMessage(String message, Session session)
  118. {
  119. try {
  120. logger.info( "来自客户端消息:" + message+ "客户端的id是:"+session.getId());
  121. System.out.println( "------------ :"+message);
  122. JSONObject jsonObject = JSON.parseObject(message);
  123. String textMessage = jsonObject.getString( "message");
  124. String fromuserId = jsonObject.getString( "userId");
  125. String touserId = jsonObject.getString( "to");
  126. //如果不是发给所有,那么就发给某一个人
  127. //messageType 1代表上线 2代表下线 3代表在线名单 4代表普通消息
  128. Map<String,Object> map1 = Maps.newHashMap();
  129. map1.put( "messageType", 4);
  130. map1.put( "textMessage",textMessage);
  131. map1.put( "fromuserId",fromuserId);
  132. if(touserId.equals( "All")){
  133. map1.put( "touserId", "所有人");
  134. sendMessageAll(JSON.toJSONString(map1),fromuserId);
  135. }
  136. else{
  137. map1.put( "touserId",touserId);
  138. System.out.println( "开始推送消息给"+touserId);
  139. sendMessageTo(JSON.toJSONString(map1),touserId);
  140. }
  141. }
  142. catch (Exception e){
  143. e.printStackTrace();
  144. logger.info( "发生了错误了");
  145. }
  146. }
  147. public void sendMessageTo(String message, String TouserId) throws IOException {
  148. for (WebSocket item : clients.values()) {
  149. // System.out.println("在线人员名单 :"+item.userId.toString());
  150. if (item.userId.equals(TouserId) ) {
  151. item.session.getAsyncRemote().sendText(message);
  152. break;
  153. }
  154. }
  155. }
  156. public void sendMessageAll(String message,String FromuserId) throws IOException {
  157. for (WebSocket item : clients.values()) {
  158. item.session.getAsyncRemote().sendText(message);
  159. }
  160. }
  161. public static synchronized int getOnlineCount() {
  162. return onlineNumber;
  163. }
  164. }

 

接下来用一个HTML5 页面 index.html,连接当前的WebSocket节点,接/发消息, index.html:


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

为了演示多人接入,我们再弄一个index2.html:

 

 好了,一切准备就绪,那么 把项目跑起来:

 

访问index.html模拟用户001连接websocket服务:

可以看到一上线,我们默认就推送了一条上线消息

接下来继续访问index2.html,模拟用户002也接入websocket:

此刻,我们模拟咱们服务器给客户推送消息,有群发和单独发送,我们一一实践:

单独发送,只需要调用websocket.java里面的 sendMessageTo方法:

 

那么我们来写个简单的推送信息接口,

 


  
  1. @Autowired
  2. WebSocket webSocket;
  3. @ResponseBody
  4. @GetMapping("/sendTo")
  5. public String sendTo(@RequestParam("userId") String userId,@RequestParam("msg") String msg) throws IOException {
  6. webSocket.sendMessageTo(msg,userId);
  7. return "推送成功";
  8. }

我们试着给001这位用户推送个消息:

 

可以看到001的页面收到了消息,002没有收到(肯定的):

群发(所有接入到websocket的用户都能收到):

 


  
  1. @ResponseBody
  2. @GetMapping("/sendAll")
  3. public String sendAll(@@RequestParam("msg") String msg) throws IOException {
  4. String fromUserId= "system"; //其实没用上
  5. webSocket.sendMessageAll(msg,fromUserId);
  6. return "推送成功";
  7. }

我们试着给所有用户推送个消息: 

 

可以看到大家都收到了这个群发消息:

 

 

然后是客户给服务端推送消息,直接操作起来:
其实就是websocket.java里面的onMessage 方法:

细看,其实里面写了点消息逻辑。 这是为了区分这个是一条上线消息还是下线消息等等。

那么发送简单直接给服务器推送消息的话,可以把后边的逻辑先注释掉。 也就是:
 

然后简单的客户端推送消息给服务器如:

可以看到控制台的打印:

 

正常收到消息,那么接下来我们把注释的代码打开,

这样只要我们符合逻辑,就能实现001给002 发送消息,或者001给所有人发送消息等等。这些其实都是通过将消息推送到服务器,服务器再判断进行转发而已。

测试一下,001给002发消息:
 

我们把消息弄成json格式:
 


  
  1. {
  2.      "message" : " hello,我是001,我想和你做朋友",
  3.      "userId": "001",
  4.      "to": "002"
  5. }

然后发送:

 

可以看到控制台有打印:

 

然后在去看看002用户的页面,成功收到了001的私发消息:
 

还有其他业务类型 001给所有用户群发等等这些看代码就知道,其实也是根据某个key判断,然后进行发送,就不测试了。

 

 

该篇websocket的实践介绍,就到此吧。

 

 

 

 

 

该篇文章只是简单地介绍一下大家去使用下websocket,场景是不同客户端能收到服务端推送的消息,服务端也能手动客户端发过来的消息,然后也能互相推送消息。

但是大家热情很高,那么使用websocket实现指定用户推送消息,实现多人聊天 ,其实我很早也是做了demo的,只是没时间去写相关的教学文章

demo的代码下载地址:https://download.csdn.net/download/qq_35387940/11851913


大致的效果,大家都可以通过接口以自己的身份登录,然后可以选择给所有人发送消息,也可以指定给某个人发送消息:

(这两种方式的实现,其实已经可以自己单独拆分出来一些按钮调用方法,去模拟私聊,群聊,讨论组等等这种场景)

 

 

 

那么除了这种方式的实现,我还有去进行整合rabbitmq作为消息代理,实现点对点以及一对多的消息推送,也是有对应的demo:

https://download.csdn.net/download/qq_35387940/12754478

 

 

 

 

 

这些demo例子都只做参考,如果合适你的项目场景,你可以使用;如果不合适,你可以自己进行调整魔改。

主要是了解的基本的功能使用方式,很多东西属于扩展的,都是动态的,根据业务需求而变的。

 

 

 

 


转载:https://blog.csdn.net/qq_35387940/article/details/93483678
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场