@IT·互联网全栈程序猿的成长

websocket点对点消息推送

2024-09-09  本文已影响0人  小尘哥

上一篇写了《若依(ruoyi)使用websocket推送数据到前端 - 简书 (jianshu.com)
》,猿友指出缺少身份识别,无法精准命中给谁推送,本文来继续解决这个问题。

既然想知道消息推送给谁,那肯定需要知道发送者、接受者、消息内容,三个最基本的东西,我简单定义为sendUserId、receiveUserId、msg。假设有三个人(路飞、索隆、艾斯)互相发消息,为了方便,我将发送者放到url中,实际项目上使用需要根据各项目情况从token、request等解析获取,这里只聊思路。

场景

三个人可以互相发消息,也可以给自己发消息,界面如下,接收到的消息会在“消息接收面板”展示出来。


初始化面板

具体实现

1、添加websocket依赖

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

2、添加配置

@Configuration
@EnableWebSocket
public class WebSocketConfig {

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

}

3、消息体

非常简单,包括消息内容,发送者和接受者

@Data
public class MsgEntity implements Serializable {

    String msg;

    String userId;

    String receiveUserId;
}

4、定向发送

@Component
@ServerEndpoint(value = "/mos/websocket/{userId}")
@Slf4j
public class WebSocket {
    private static Map<String, Session> livingSession = new ConcurrentHashMap<>();

    /**
     * 客户端与服务端连接成功
     *
     * @param session
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        livingSession.put(userId, session);
    }

    /**
     * 客户端与服务端连接关闭
     *
     * @param session
     */
    @OnClose
    public void onClose(Session session, @PathParam("userId") String userId) {
        livingSession.remove(userId);
    }

    /**
     * 客户端与服务端连接异常
     *
     * @param error
     * @param session
     */
    @OnError
    public void onError(Throwable error, Session session) {
        error.printStackTrace();
    }

    /**
     * 客户端向服务端发送消息
     * 主要消息发送在这里,找到接受者的session,并推送消息内容
     * @param message
     * @throws IOException
     */
    @OnMessage
    public void onMsg(String msg) throws IOException {
        MsgEntity msgEntity = JSONUtil.toBean(msg,MsgEntity.class);
        Session session = livingSession.get(msgEntity.getReceiveUserId());
        session.getAsyncRemote().sendText(msgEntity.getMsg());
    }

}

5、前端

前端为了界面稍微好看一些,使用了layui,无论什么都是相通的,vue、jquery都可以。

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>socket消息推送</title>
    <div th:replace="~{common/links::header}"></div>
    <div th:replace="~{common/script::js_footer}"></div>
</head>
<body>
<div class="layui-bg-gray" style="padding: 16px;">
    <div class="layui-row layui-col-space15">
        <div class="layui-col-md6">
            <div class="layui-card">
                <div class="layui-card-header">消息发送面板(发送人:<span id="sender"></span>)</div>
                <div class="layui-card-body">
                    <form class="layui-form layui-form-pane" id="searchForm">
                        <div class="layui-form-item" pane>
                            <label class="layui-form-label">发送给</label>
                            <div class="layui-input-block">
                                <input type="radio" name="receiveUserId" value="路飞" title="路飞" checked>
                                <input type="radio" name="receiveUserId" value="索隆" title="索隆">
                                <input type="radio" name="receiveUserId" value="艾斯" title="艾斯">
                            </div>
                        </div>
                        <div class="layui-form-item layui-form-text">
                            <div class="layui-input-block">
                                <textarea placeholder="请输入需要发送的消息" class="layui-textarea" name="msg" ></textarea>
                            </div>
                        </div>
                        <div class="layui-form-item">
                            <button class="layui-btn" lay-submit="" lay-filter="add" >发送消息</button>
                        </div>
                    </form>
                </div>
            </div>
        </div>
        <div class="layui-col-md6">
            <div class="layui-card">
                <div class="layui-card-header">消息接收面板</div>
                <div class="layui-card-body">
                    <span id="content"></span>
                </div>
            </div>
        </div>
    </div>
</div>



</body>
<script th:inline="javascript">
    layui.use('form', function () {

        var $ = layui.jquery,form = layui.form;
        const urlParams = new URLSearchParams(window.location.search);
        const sendUserId = urlParams.get('sendUserId');
        const receiveUserId = urlParams.get('receiveUserId');
        $("#sender").html(sendUserId)

        form.on('submit(add)', function (data) {
            let param = {userId:sendUserId}
            Object.assign(param,data.field);
            Common.ajaxFormSubmit('/websocket/send', param, function (data) {
                layer.msg('消息已发送')
            });
            return false;
        });
        let webSocket = null;
        if ('WebSocket' in window){
            webSocket = new WebSocket("ws://localhost:8888/mos/websocket/"+sendUserId);
        }else{
            layer.msg('您的浏览器不支持websocket')
        }
        webSocket.onopen = function () {
            layer.msg('连接成功')
        }
        webSocket.onerror = function (error) {
            layer.msg('连接失败',error)
        }
        webSocket.onclose = function () {
            layer.msg('连接关闭')
        }
        webSocket.onmessage = function (event) {
            $("#content").html(event.data)
        }

    });
</script>
</html>

其他说明

  1. 没有进行鉴权,需要视具体项目情况而定
  2. 默认三个会话都已创建,否则会报“ because "session" is null”的错,只是简单演示使用,实际项目可能还需要考虑接收方离线时怎么办等等。
  3. 代码已上传gitee,需要的童鞋可以自行获取。
    MosSimple: 功能示例 (gitee.com)
上一篇 下一篇

猜你喜欢

热点阅读