SpringBoot整合WebSocket(看完即入门)

WebSocket

    • 1、什么是webSocket?
    • 2、webSocket可以用来做什么?
    • 3、webSocket协议
    • 4、服务端
        • WebSocket配置类
        • WebSocket操作类
    • 5、webSocket网页客户端工具
      • 通讯测试

1、什么是webSocket?

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

2、webSocket可以用来做什么?

利用双向数据传输的特点可以用来完成很多功能,不需要前端轮询,浪费资源。例如:

1、通告功能
2、聊天功能 (如下是逻辑图)
SpringBoot整合WebSocket(看完即入门)_第2张图片
3、实时更新数据功能
4、弹幕

3、webSocket协议

本协议有两部分:握手和数据传输。
握手是基于http协议的。

GET ws://localhost/chat HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key:dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Protocol: chat,superchat
Sec-WebSocket-Version: 13

来自服务器的握手看起来像如下形式:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept:s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

SpringBoot整合WebSocket(看完即入门)_第3张图片

4、服务端

maven依赖

   <dependency>
      <groupId>org.springframework.bootgroupId>
      <artifactId>spring-boot-starter-websocketartifactId>
  dependency>
   <dependency>
            <groupId>cn.hutoolgroupId>
            <artifactId>hutool-coreartifactId>
            <version>5.7.22version>
        dependency>

WebSocket配置类

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
 * @author Hao
 * @date 2023-03-01 16:18
 * @description:
 * @version:
 */
@Configuration
public class WebSocketConfig {
    /**
     * 	注入ServerEndpointExporter,
     * 	这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
     */
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
    
}

WebSocket操作类

通过该类WebSocket可以进行群推送以及单点推送


import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Component;
import cn.hutool.core.util.StrUtil;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Hao
 * @date 2023-03-01 16:18
 * @description:
 * @version:
 */
@ServerEndpoint(value = "/websocket/{userId}")
@Component
public class WebSocket {
    private final static Logger logger = LogManager.getLogger(WebSocket.class);

    /**
     * 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的
     */

    private static int onlineCount = 0;

    /**
     * concurrent包的线程安全Map,用来存放每个客户端对应的MyWebSocket对象
     */
    private static ConcurrentHashMap<String, WebSocket> webSocketMap = new ConcurrentHashMap<>();

    /**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */

    private Session session;
    private String userId;


    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;
        //加入map
        webSocketMap.put(userId, this);
        addOnlineCount();           //在线数加1
        logger.info("用户{}连接成功,当前在线人数为{}", userId, getOnlineCount());
        try {
            sendMessage(String.valueOf(this.session.getQueryString()));
        } catch (IOException e) {
            logger.error("IO异常");
        }
    }


    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        //从map中删除
        webSocketMap.remove(userId);
        subOnlineCount();           //在线数减1
        logger.info("用户{}关闭连接!当前在线人数为{}", userId, getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        logger.info("来自客户端用户:{} 消息:{}",userId, message);

        //群发消息
        /*for (String item : webSocketMap.keySet()) {
            try {
                webSocketMap.get(item).sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }*/
    }

    /**
     * 发生错误时调用
     *
     * @OnError
     */
    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("用户错误:" + this.userId + ",原因:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 向客户端发送消息
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }

    /**
     * 通过userId向客户端发送消息
     */
    public void sendMessageByUserId(String userId, String message) throws IOException {
        logger.info("服务端发送消息到{},消息:{}",userId,message);
     if(StrUtil.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
         webSocketMap.get(userId).sendMessage(message);
     }else{
         logger.error("用户{}不在线",userId);
     }

    }

    /**
     * 群发自定义消息
     */
    public static void sendInfo(String message) throws IOException {
        for (String item : webSocketMap.keySet()) {
            try {
                webSocketMap.get(item).sendMessage(message);
            } catch (IOException e) {
                continue;
            }
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        WebSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        WebSocket.onlineCount--;
    }

}


测试controller

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

/**
 * @author Hao
 * @date 2023-03-01 16:18
 * @description:
 * @version:
 */
 
@RestController
@RequestMapping("/webSocket")
public class WebSocketController {
    @Autowired
    private WebSocket webSocket;
    @PostMapping("/sentMessage")
    public void sentMessage(String userId,String message){
        try {
            webSocket.sendMessageByUserId(userId,message);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}


5、webSocket网页客户端工具

http://websocket.jsonin.com/

SpringBoot整合WebSocket(看完即入门)_第4张图片
复制三个tab

WebSocket地址分别输入后连接

ws://127.0.0.1:8051/websocket/1
ws://127.0.0.1:8051/websocket/2
ws://127.0.0.1:8051/websocket/3

在这里插入图片描述

通讯测试

1.往客户端发消息

127.0.0.1:8092/webSocket/sentMessage?userId=1&message=请进入视频会议
127.0.0.1:8092/webSocket/sentMessage?userId=2&message=请进入视频会议
127.0.0.1:8092/webSocket/sentMessage?userId=3&message=请进入视频会议

SpringBoot整合WebSocket(看完即入门)_第5张图片

SpringBoot整合WebSocket(看完即入门)_第6张图片

2.客户端收到消息回复“好的”
在这里插入图片描述

最后送所有正在努力的大家一句话:

你不一定逆风翻盘,但一定要向阳而生。

期待下次发布好的文章:

山水相逢,我们江湖见。

你可能感兴趣的:(Java,spring,boot,websocket,java)