【后端】websocket学习笔记

文章目录

  • [1. 消息推送常见方式](#1. 消息推送常见方式)
    • [1.1 轮询 VS 长轮询](#1.1 轮询 VS 长轮询)
    • [1.2 SSE(server-sent event)服务器发送事件](#1.2 SSE(server-sent event)服务器发送事件)
  • [2. websocket介绍](#2. websocket介绍)
    • [2.1 介绍](#2.1 介绍)
    • [2.2 原理](#2.2 原理)
    • [2.3 websoket API](#2.3 websoket API)
      • [2.3.1 客户端【浏览器】API](#2.3.1 客户端【浏览器】API)
      • [2.3.2 服务端API](#2.3.2 服务端API)
  • [3. 代码实现](#3. 代码实现)
    • [3.1 流程分析](#3.1 流程分析)
    • [3.2 pom依赖](#3.2 pom依赖)
    • [3.3 配置类](#3.3 配置类)
    • [3.4 消息格式](#3.4 消息格式)
    • [3.5 消息类](#3.5 消息类)
  • [4. 前端代码(非视频源码)](#4. 前端代码(非视频源码))

参考视频
【后端&网络&大数据&数据库目录贴】

1. 消息推送常见方式

  • 轮询
  • 长轮询
  • websocket
  • SSE

1.1 轮询 VS 长轮询

1.2 SSE(server-sent event)服务器发送事件

  • SSE在服务器和客户端之间打开一个单向通道
  • 服务端响应的不再是一次性的数据包,而是text/event-stream类型的数据流信息
  • 服务器有数据变更时将数据流式传输到客户端

2. websocket介绍

2.1 介绍

websocket是一种基于TCP链接上进行全双工通信的协议

  • 全双工:允许数据在两个方向上同时传输。(TCP是全双工,HTTP是基于TCP的)
  • 半双工:允许数据在两个方向上传输,但是同一个时间段内只允许一个方向上传输。

2.2 原理

  • ws://localhost?chat 依然是http协议
  • Connection:UpgradeUpgrade: websocket表明连接升级
  • 响应码 101说明已经切换成功

2.3 websoket API

2.3.1 客户端【浏览器】API

  • websocket对象创建
js 复制代码
let ws=new WebSocket(URL);
  • websocket对象相关事件
  • websocket对象提供的方法

2.3.2 服务端API

  • 服务端如何接收客户端发送的数据呢?
  • 服务端如何推送数据给客户端呢?

3. 代码实现

3.1 流程分析

3.2 pom依赖

xml 复制代码
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

3.3 配置类



3.4 消息格式

3.5 消息类

java 复制代码
@ServerEndpoint(value = "/chat", configurator = GetHttpSessionConfigurator.class)
@Component
public class ChatEndpoint {
    public static final Map<String, Session> sessionsMap = new ConcurrentHashMap<>();
    private HttpSession httpSession;

    /**
     * 建立websocket连接后,被调用
     *
     * @param session
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config) {
        //1.将session进行保存
        this.httpSession = (HttpSession) config.getUserProperties().get("httpSession");
        String userName = (String) httpSession.getAttribute("userName");

        ChatEndpoint.sessionsMap.put(userName, session);
        //2. 广播消息,需要将登录的所有的用户推送给所有用户
        String sendMessage = MessageUtil.sendMessage(true, null, userName + "上线");
        boardcast(sendMessage);
    }

    /**
     * 广播消息
     */
    private void boardcast(String message) {
        // 遍历 map 集合
        Set<Map.Entry<String, Session>> entries = ChatEndpoint.sessionsMap.entrySet();
        for (Map.Entry<String, Session> entry : entries) {
            Session session = entry.getValue();
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                // 记录日志
            }
        }
    }

    @OnMessage
    public void onMessage(String message) {
        try {
            String fromName = (String) this.httpSession.getAttribute("userName");
            // 将消息推送给指定的用户   message : {"toName":"张三","message":"你好"}
            ClientMessage clientMessage = JSON.parseObject(message, ClientMessage.class);
            Session session = ChatEndpoint.sessionsMap.get(clientMessage.getToName());
            String sendMessage = MessageUtil.sendMessage(false, fromName, clientMessage.getMessage());
            session.getBasicRemote().sendText(sendMessage);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 当websocket连接断开时,此方法会被处罚
     */
    @OnClose
    public void onClose(Session session) {
// 从在线用户集合中剔除断开连接的用户
        String userName = (String) this.httpSession.getAttribute("userName");
        ChatEndpoint.sessionsMap.remove(userName);
        // 通知其他用户当前用户下线
        String sendMessage = MessageUtil.sendMessage(true, null, userName + "上线");
        boardcast(sendMessage);
    }
}

4. 前端代码(非视频源码)

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/jquery.min.js"></script>
</head>
<body>
<p>【fromUserId】:<div><input id="userId" name="userId" type="text" value="10"></div>
<p>【toUserId】:<div><input id="toUserId" name="toUserId" type="text" value="20"></div>
<p>【发送的消息】:<div><input id="contentText" name="contentText" type="text" value="hello websocket"></div>
<p>【收到的内容】:<div><textarea id="receText"></textarea></div>
  <button id="openSocket">开启socket</button>
  <button id="sendMessage">发送消息</button>
  <button id="closeWs">关闭连接</button>
<script>
    $(function (){
        console.log('加兹阿勒')
        const urlParams = new URLSearchParams(window.location.search);
        console.log(urlParams)
        var fromUserId = urlParams.get('fromUserId');
        $('#userId').val(fromUserId)
    })


    var ws ;
    function openSocket() {
        if(typeof(WebSocket) == "undefined") {
            console.log("您的浏览器不支持WebSocket");
        }else{
            console.log("您的浏览器支持WebSocket");
            //实现化WebSocket对象,指定要连接的服务器地址与端口  建立连接
            //等同于socket = new WebSocket("ws://localhost:8888/xxxx/im/25");
            //var socketUrl="${request.contextPath}/im/"+$("#userId").val();
            var socketUrl="http://localhost:8081/chat";
            socketUrl=socketUrl.replace("https","wss").replace("http","ws");
            console.log(socketUrl);
            if(ws !=null){
                ws .close();
                ws =null;
            }
            ws = new WebSocket(socketUrl);
            // 当 WebSocket 成功建立连接时触发
            ws.onopen  = function(event) {
                console.log("WebSocket is connected now.");
                // 在这里你可以发送一些初始消息到服务器
                // ws.send("Hello, Server!");
            };

            // 当接收到来自服务器的消息时触发
            ws.onmessage = function(event) {
                console.log("Received data from server: " + event.data);
                // 处理从服务器接收到的数据
                var parse = JSON.parse(event.data);
                $('#receText').val(parse.message);
            };

            // 当 WebSocket 连接关闭时触发
            ws.onclose = function(event) {
                if (event.wasClean) {
                    console.log("WebSocket connection closed cleanly, code=" + event.code + " reason=" + event.reason);
                } else {
                    // 例如,服务器进程被终止而关闭
                    console.log("WebSocket connection died");
                }
            };

            // 当 WebSocket 连接出现错误时触发
            ws.onerror = function(error) {
                console.error("WebSocket Error: " + error);
            };
        }
    }
    function sendMessage() {
        if(typeof(WebSocket) == "undefined") {
            console.log("您的浏览器不支持WebSocket");
        }else {
            console.log("您的浏览器支持WebSocket");
            console.log('{"toUserId":"'+$("#toUserId").val()+'","contentText":"'+$("#contentText").val()+'"}');
            ws.send('{"toName":"'+$("#toUserId").val()+'","message":"'+$("#contentText").val()+'"}');
        }
    }
    function closeWs(){
        ws.close();
    }
    $("#openSocket").on('click', openSocket);
    $("#sendMessage").on('click',sendMessage);
    $("#closeWs").on('click', closeWs);
</script>
</body>
</html>
相关推荐
CSDN_PBB4 小时前
[STM32 - 野火] - - - 固件库学习笔记 - - - 十五.设置FLASH的读写保护及解除
笔记·stm32·学习
bdawn7 小时前
深度集成DeepSeek大模型:WebSocket流式聊天实现
python·websocket·openai·api·实时聊天·deepseek大模型·流式输出
鸡啄米的时光机7 小时前
vscode的一些实用操作
vscode·学习
Kai HVZ7 小时前
《深度学习》——调整学习率和保存使用最优模型
人工智能·深度学习·学习
守护者1708 小时前
JAVA学习-练习试用Java实现“使用Apache Ignite对大数据进行内存计算和快速筛查”
java·学习
夜流冰8 小时前
编程参考 - C语言可变参数
笔记
格雷亚赛克斯9 小时前
Qt笔记31-69
数据库·笔记·qt
Long_poem9 小时前
【自学笔记】版本控制与持续集成基础知识点总览-持续更新
笔记·ci/cd
Stream٩( 'ω' )و10 小时前
109~133笔记
笔记
weixin_5025398510 小时前
rust学习笔记2-rust的包管理工具Cargo使用
笔记·学习·rust