欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

springboot集成websocket点对点推送、广播推送

程序员文章站 2022-07-02 15:19:07
一、什么都不用说,导入个依赖先 二、推送到前端的消息实体类 三、因为要实现点对点的推送,所以需要创建一个监听器来获取到websocket的session,如下: 四、最重要的配置类 代码中有详细的解释,认真看可以看明白的。 五、controller 六、前端页面 最好,来试试点对点推送。 第一个页面 ......

一、什么都不用说,导入个依赖先

<dependency>
  <groupid>org.springframework.boot</groupid>
  <artifactid>spring-boot-starter-websocket</artifactid>
</dependency>

 

二、推送到前端的消息实体类

import lombok.allargsconstructor;
import lombok.builder;
import lombok.data;
import lombok.noargsconstructor;
import java.io.serializable;


@data
@builder
@noargsconstructor
@allargsconstructor
public class notifybean<t> implements serializable {

    private static final long serialversionuid = 1l;

    private int type;
    private string message;
    private t data;

}

  

三、因为要实现点对点的推送,所以需要创建一个监听器来获取到websocket的session,如下:

import org.springframework.beans.factory.annotation.autowired;
import org.springframework.context.applicationlistener;
import org.springframework.messaging.simp.stomp.stompheaderaccessor;
import org.springframework.web.socket.messaging.sessionconnectevent;



public class stompconnecteventlistener implements applicationlistener<sessionconnectevent> {

    @autowired
    private redishelper redishelper;

    @override
    public void onapplicationevent(sessionconnectevent event) {
        stompheaderaccessor sha = stompheaderaccessor.wrap(event.getmessage());
        //login get from browser
        if(sha.getnativeheader("userid")==null){
            return;
        }
        string userid = sha.getnativeheader("userid").get(0);
        string sessionid = sha.getsessionid();
        redishelper.redistemplate.opsforvalue().set("websocket:"+userid,sessionid);
    }
}

 

四、最重要的配置类

import lombok.extern.slf4j.slf4j;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.messaging.message;
import org.springframework.messaging.messagechannel;
import org.springframework.messaging.simp.config.channelregistration;
import org.springframework.messaging.simp.config.messagebrokerregistry;
import org.springframework.messaging.simp.stomp.stompcommand;
import org.springframework.messaging.simp.stomp.stompheaderaccessor;
import org.springframework.messaging.support.channelinterceptoradapter;
import org.springframework.messaging.support.messageheaderaccessor;
import org.springframework.web.socket.config.annotation.*;


@configuration
@enablewebsocketmessagebroker
@slf4j
public class websocketconfig extends abstractwebsocketmessagebrokerconfigurer {

    //stomp监听类
    @bean
    public stompconnecteventlistener applicationstartlistener(){
        return new stompconnecteventlistener();
    }


    @override
    public void registerstompendpoints(stompendpointregistry stompendpointregistry) {
        //建立连接端点,注册一个stomp的协议节点,并指定使用sockjs协议
        stompendpointregistry.addendpoint("/nmpsocketweb")
                .setallowedorigins("*")
                .withsockjs();
    }


    @override
    public void configuremessagebroker(messagebrokerregistry messagebrokerregistry) {
        //配置消息代理(messagebroker)。
        messagebrokerregistry.enablesimplebroker("/topic");// 推送消息前缀
        messagebrokerregistry.setapplicationdestinationprefixes("/app");// 应用请求前缀,前端发过来的消息将会带有“/app”前缀。
    }

    @override
    public void configureclientinboundchannel(channelregistration registration) {
        //token认证
        registration.setinterceptors(new channelinterceptoradapter() {
            @override
            public message<?> presend(message<?> message, messagechannel channel) {
                stompheaderaccessor accessor = messageheaderaccessor.getaccessor(message, stompheaderaccessor.class);
                if (stompcommand.connect.equals(accessor.getcommand()) || stompcommand.send.equals(accessor.getcommand())) {
                    string token = accessor.getfirstnativeheader("token");
                    try {
                        tokenvalidate(token);
                    } catch (exception e) {
                        log.error(e.tostring());
                        return null;
                    }
                }
                return message;
            }
        });
    }


    public boolean tokenvalidate(string token) throws exception {
        if (token == null || token.isempty()) {
            throw new exception("websocket:token为空!");
        }
        if (jwtutil.validatetoken(token)==null) {
            throw new exception("websoc:token无效!");
        }
        return true;
    }

}

  代码中有详细的解释,认真看可以看明白的。

 

五、controller

import io.swagger.annotations.api;
import io.swagger.annotations.apioperation;
import org.springframework.beans.factory.annotation.autowired;
import org.springframework.messaging.messageheaders;
import org.springframework.messaging.handler.annotation.messagemapping;
import org.springframework.messaging.handler.annotation.sendto;
import org.springframework.messaging.simp.simpmessageheaderaccessor;
import org.springframework.messaging.simp.simpmessagetype;
import org.springframework.messaging.simp.simpmessagingtemplate;
import org.springframework.stereotype.controller;
import org.springframework.web.bind.annotation.requestmapping;
import org.springframework.web.bind.annotation.responsebody;



@api(tags="websocket控制器",description="websocket控制器")
@controller
@requestmapping(value = "/websocket")
public class websocketcontroller extends basecontroller {

    @autowired
    private simpmessagingtemplate simpmessagingtemplate;

    @autowired
    private redishelper redishelper;


    @apioperation(value = "测试主动发送消息", notes = "测试主动发送消息", httpmethod = "get")
    @requestmapping(value = "/sendmsg")
    @responsebody
    public void sendmsg(){
        system.out.println("测试主动发送消息");
        notifybean notifybean = notifybean.builder().message("服务器给你发消息啦!").build();
        simpmessagingtemplate.convertandsend(webconstant.web_sc_topic_notify,notifybean);
    }


    @messagemapping("/test") //当浏览器向服务端发送请求时,通过@messagemapping映射/welcome这个地址,类似于@responsemapping
    @sendto(webconstant.web_sc_topic_notify)//当服务器有消息时,会对订阅了@sendto中的路径的浏览器发送消息
    public notifybean test(uservo uservo) {
        try {
            //睡眠1秒
            thread.sleep(1000);
        } catch (interruptedexception e) {
            e.printstacktrace();
        }
        notifybean notifybean = notifybean.builder().message("welcome!"+ uservo.getname()).build();
        return notifybean;
    }

    /**
     * 点对点发送消息demo
     * 根据用户key发送消息
     * @param uservo
     * @return
     * @throws exception
     */
    @messagemapping("/test/toone")
    public void toone(uservo uservo) throws exception {
        string sessionid=(string)redishelper.redistemplate.opsforvalue().get("websocket:"+uservo.getid());
        notifybean notifybean = notifybean.builder().message("welcome!"+ uservo.getname()).build();
        //convertandsendtouser该方法会在订阅路径前拼接"/user",所以前端订阅的路径全路径是"/user/topic/notify"
        simpmessagingtemplate.convertandsendtouser(sessionid, webconstant.web_sc_topic_notify,notifybean,createheaders(sessionid));
    }

    private messageheaders createheaders(string sessionid) {
        simpmessageheaderaccessor headeraccessor = simpmessageheaderaccessor.create(simpmessagetype.message);
        headeraccessor.setsessionid(sessionid);
        headeraccessor.setleavemutable(true);
        return headeraccessor.getmessageheaders();
    }

}

 

六、前端页面

<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">
<link lang="en" xmlns:th="http://www.w3.org/1999/xhtml"></link>
<link href="/webjars/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"></link>
<head>
    <script th:src="@{sockjs.min.js}"></script>
    <script th:src="@{stomp.min.js}"></script>
    <script th:src="@{jquery-1.11.3.min.js}"></script>
</head>
<body>
<blockquote class="layui-elem-quote">/user/topic-message</blockquote>

<div id="main-content" class="container">
    <div class="row">
        <div class="col-md-6">
            <form class="form-inline">
                <div class="form-group">
                    <label for="connect">websocket connection:</label>
                    <button id="connect" class="btn btn-default" type="submit">connect</button>
                    <button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">disconnect
                    </button>

                </div>
            </form>
        </div>
        <div class="col-md-6">
            <form class="form-inline">
                <div class="form-group">
                    <label for="name">what is your name?</label>
                    <input type="text" id="name" class="form-control" placeholder="your name here..."></input>
                </div>
                <button id="send" class="btn btn-default" type="submit">send</button>
            </form>
        </div>
    </div>
    <div class="row">
        <div class="col-md-12">
            <table id="conversation" class="table table-striped">
                <thead>
                <tr>
                    <th>greetings</th>
                </tr>
                </thead>
                <tbody id="greetings">
                </tbody>
            </table>
        </div>
        <div id="message"></div>
    </div>
</div>

<script>
    //  /msg/sendcommuser
    var stompclient = null;
    //传递用户key值
    var login = "ricky";
    function setconnected(connected) {
        $("#connect").prop("disabled", connected);
        $("#disconnect").prop("disabled", !connected);
        if (connected) {
            $("#conversation").show();
        }
        else {
            $("#conversation").hide();
        }
        $("#greetings").html("");
    }

    function connect() {
        var socket = new sockjs('/nmpsocketweb');
        stompclient = stomp.over(socket);
        stompclient.connect({login:login}, function (frame) {
            setconnected(true);
            console.log('connected: ' + frame);
            stompclient.subscribe('/user/topic/greetings', function (greeting) {
                setmessageinnerhtml(json.parse(greeting.body).message);
                console.log(json.parse(greeting.body).message)
            });
        });
    }

    function disconnect() {
        if (stompclient != null) {
            stompclient.disconnect();
        }
        setconnected(false);
        console.log("disconnected");
    }

    function sendname() {
        stompclient.send("/app/test/toone", {}, json.stringify({'name': $("#name").val(),'id':'ricky'}));
    }


    function showgreeting(message) {
        $("#greetings").append("<tr><td>" + message + "</td></tr>");
    }

    $(function () {
        $("form").on('submit', function (e) {
            e.preventdefault();
        });
        $( "#connect" ).click(function() { connect(); });
        $( "#disconnect" ).click(function() { disconnect(); });
        $( "#send" ).click(function() { sendname(); });
    });

    //将消息显示在网页上
    function setmessageinnerhtml(innerhtml){
        console.log(innerhtml);
        document.getelementbyid('message').innerhtml += innerhtml + '<br/>';
    }
</script>
</body>
</html>

  

最好,来试试点对点推送。

第一个页面:

springboot集成websocket点对点推送、广播推送

第二个页面:

springboot集成websocket点对点推送、广播推送

可以看到,后台推送的消息只有一个页面接收到,完事!