【后端】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>
相关推荐
黑叶白树13 分钟前
简单的签到程序 python笔记
笔记·python
@小博的博客16 分钟前
C++初阶学习第十弹——深入讲解vector的迭代器失效
数据结构·c++·学习
幸运超级加倍~1 小时前
软件设计师-上午题-15 计算机网络(5分)
笔记·计算机网络
南宫生1 小时前
贪心算法习题其四【力扣】【算法学习day.21】
学习·算法·leetcode·链表·贪心算法
懒惰才能让科技进步2 小时前
从零学习大模型(十二)-----基于梯度的重要性剪枝(Gradient-based Pruning)
人工智能·深度学习·学习·算法·chatgpt·transformer·剪枝
love_and_hope2 小时前
Pytorch学习--神经网络--搭建小实战(手撕CIFAR 10 model structure)和 Sequential 的使用
人工智能·pytorch·python·深度学习·学习
Chef_Chen2 小时前
从0开始学习机器学习--Day14--如何优化神经网络的代价函数
神经网络·学习·机器学习
芊寻(嵌入式)2 小时前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
准橙考典3 小时前
怎么能更好的通过驾考呢?
人工智能·笔记·自动驾驶·汽车·学习方法
hong1616883 小时前
跨模态对齐与跨领域学习
学习