SpringBoot整合WebSocket实现聊天室流程全解

作者:欲无缘 时间:2021-07-03 23:35:21 

什么是WebSocket 

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

WebSocket通信模型

SpringBoot整合WebSocket实现聊天室流程全解

为什么需要WebSocket

http 通信是单向的,发送请求获取响应,没有请求也就没有响应。

简单理解:

  • HTTP 打电话:客户端问一句服务端答一句

  • WebSocket 打电话:双向对话

SpringBoot整合WebSocket实现聊天室流程全解

Websocket与http的关系

SpringBoot整合WebSocket实现聊天室流程全解

相同点:

  • 都是基于tcp的,都是可靠性传输协议

  • 都是应用层协议

不同点:

  • WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息

  • HTTP是单向的

  • WebSocket是需要浏览器和服务器握手进行建立连接的而http是浏览器发起向服务器的连接,服务器预先并不知道这个连接。

SpringBoot集成WebSocket

maven依赖

<dependency>  
          <groupId>org.springframework.boot</groupId>  
          <artifactId>spring-boot-starter-websocket</artifactId>  
      </dependency>

WebSocketConfig

@Configuration  
public class WebSocketConfig {  
   @Bean  
   public ServerEndpointExporter serverEndpointExporter() {  
       return new ServerEndpointExporter();  
   }  
}

WebSocketService

@Slf4j
@Service
@ServerEndpoint(value = "/myService/{userId}")
public class WebSocketService {
   /**
    * 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    */
   private static AtomicInteger onlineCount = new AtomicInteger(0);;
   /**
    * concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    */
   private static ConcurrentHashMap<String,WebSocketService> webSocketMap = new ConcurrentHashMap<>();
   /**
    * concurrent包的线程安全Set,用来存放每个客户端对应的session对象。
    */
   private static ConcurrentHashMap<String,Session> sessionMap = new ConcurrentHashMap<>();
   /**
    * 与某个客户端的连接会话,需要通过它来给客户端发送数据
    */
   private Session session;
   private String userId = "";
   /**
    * 连接建立成功调用的方法
    */
   @OnOpen
   public void onOpen(Session session,@PathParam("userId") String userId) {
       this.session = session;
       this.userId = userId;
       if(webSocketMap.containsKey(userId) && sessionMap.containsKey(userId)){
           webSocketMap.remove(userId);
           sessionMap.remove(userId);
           sessionMap.put(userId,session);
           webSocketMap.put(userId,this);
       }else{
           webSocketMap.put(userId,this);
           sessionMap.put(userId,session);
           addOnlineCount();
       }
       log.info("用户连接:"+userId+",当前在线人数为:" + getOnlineCount());
   }
   /**
    * 连接关闭调用的方法
    */
   @OnClose
   public void onClose() {
       if(webSocketMap.containsKey(userId)){
           webSocketMap.remove(userId);
           subOnlineCount();
       }
       log.info("用户退出:"+userId+",当前在线人数为:" + getOnlineCount());
   }
   /**
    * 收到客户端消息后调用的方法
    */
   @OnMessage
   public void onMessage(String message, Session session) {
       this.session = session;
       log.info("收到客户端消息 -> {}",message);
       //服务端收到客户端的消息并推送给客户端
       sendMessage(message);
   }
   /**
    * 发生错误时调用
    */
   @OnError
   public void onError(Session session, Throwable error) {
      log.error(error.getMessage());
   }
   /**
    * 实现服务器主动推送   可以通过controller调用此方法实现主动推送
    */
   public void sendMessage(String message){
       try {
           Set<Map.Entry<String, Session>> entries = sessionMap.entrySet();
           for (Map.Entry<String, Session> next : entries) {
               Session session = next.getValue();
               session.getBasicRemote().sendText(this.userId + "说" + message);
           }
       } catch (IOException e) {
           log.error(e.getMessage());
       }
   }
   public static synchronized int getOnlineCount() {
       return onlineCount.get();
   }
   public static synchronized void addOnlineCount() {
       WebSocketService.onlineCount.getAndIncrement();
   }
   public static synchronized void subOnlineCount() {
       WebSocketService.onlineCount.getAndDecrement();
   }
}

WebSocket.html

<!DOCTYPE html>
<html>
<head>
   <meta charset="utf-8">
   <title>websocket通讯</title>
</head>
<body>
<p>userId:<input id="userId" name="userId" type="text" value="10"></p>
<p>msg:<input id="contentText" name="contentText" type="text" value="hello websocket"></p>
<p>操作:<button onclick="openSocket()">开启socket</button></p>
<p>操作:<button onclick="sendMessage()">发送消息</button></p>
</body>
<script type="application/javascript">
   let socket;
   function openSocket() {
       if(socket != null){
           socket.close();
           socket = null;
       }
       let userId = document.getElementById('userId').value
       socket = new WebSocket("ws://localhost:9000/myService/"+userId);
       //打开事件
       socket.onopen = function() {
           console.log("websocket已打开");
       };
       //获得消息事件
       socket.onmessage = function(msg) {
           console.log(msg.data);
       };
       //关闭事件
       socket.onclose = function() {
           console.log("websocket已关闭");
       };
       //发生了错误事件
       socket.onerror = function() {
           console.log("websocket发生了错误");
       }
   }
   function sendMessage() {
       let contentText = document.getElementById('contentText').value
       socket.send(contentText);
   }
</script>
</html>

测试

SpringBoot整合WebSocket实现聊天室流程全解

来源:https://blog.csdn.net/qq_29917503/article/details/128343049

标签:SpringBoot,WebSocket,聊天室
0
投稿

猜你喜欢

  • C#基础之Lambda表达式用法实例教程

    2021-12-06 14:07:56
  • Netty实战入门教程之 什么是Netty

    2023-10-14 01:08:09
  • Android实现图像切换器

    2023-11-27 00:45:41
  • java编写贪吃蛇小游戏

    2023-06-19 01:49:54
  • 很简单的Java断点续传实现原理

    2023-05-20 06:38:09
  • Spring Security基于json登录实现过程详解

    2023-12-07 07:15:18
  • 详解SpringBoot如何实现统一后端返回格式

    2022-11-27 05:26:24
  • Android实现MVVM架构数据刷新详解流程

    2023-07-05 13:33:41
  • Android开发之判断有无虚拟按键(导航栏)的实例

    2023-01-29 07:48:45
  • Java Swing JFrame窗口的实现

    2021-11-30 12:03:37
  • springboot mybatis里localdatetime序列化问题的解决

    2023-06-25 06:58:18
  • Java实现复制文件并命名的超简洁写法

    2022-02-12 12:54:06
  • java实战小技巧之字符串与容器互转详解

    2023-09-04 10:56:01
  • java多线程实现交通灯管理系统

    2022-01-11 07:36:54
  • Springmvc ResponseBody响应json数据实现过程

    2022-06-12 15:22:30
  • Java中关于泛型、包装类及ArrayList的详细教程

    2021-12-25 00:46:34
  • Trie树(字典树)的介绍及Java实现

    2022-06-14 15:38:24
  • Java 按照字节来截取字符串的代码(不会出现半个汉字)

    2023-04-30 01:40:22
  • Unity解析gif动态图操作

    2022-05-27 08:16:43
  • C#使用foreach循环遍历数组完整实例

    2021-12-27 15:10:33
  • asp之家 软件编程 m.aspxhome.com