关于grails使用grails-spring-websocket-2.3.0指定用户发送消息接收不到消息的问题

问题描述

目前使用到的框架是grails
在使用 grails-spring-websocket-2.3.0 这个插件的时候
通过brokerMessagingTemplate.convertAndSendToUser向指定用户发送消息的时候,客户端接收不到消息

问题出现的环境背景及自己尝试过哪些方法

如果不指定某个用户直接使用

client.connect(, function() {
                client.subscribe("/topic/hello", function(message) {
                    console.log("aaaaaaaaaaaaaaaaaaaa");
                    let data = JSON.parse(message.body);
                    if(sid == data.sid) {
                        $("#show_msg").append("<li class='right'><div style='width:60%;display:inline-block;word-wrap: break-word;word-break: normal;'><section><p>" + data.msg + "</p></section></div><div style='width:10%;display:inline-block;margin:-28px auto;'><img src='${request.contextPath}/assets/global_anon.png'/></div></li>");
                    }else{
                        $("#show_msg").append("<li class='left'><div style='width:10%;display:inline-block;margin:-28px auto;'><img src='${request.contextPath}/assets/pink_anon.png'/></div><div style='width:60%;display:inline-block;word-wrap: break-word;word-break: normal;'><section><p>"+data.msg+"</p></section></div></li>");
                    }
                });
            });

这样的方式是可以接收到代码,但是现在想挑战一下指定用户发送消息,但是就是不成功。
我是按照这个博客写的代码
https://blog.csdn.net/u011943...

相关代码

// 请把代码文本粘贴到下方(请勿用图片代替代码)
这里是我所有的代码
前端代码

<!DOCTYPE html>
<html>
<head>

    <title>在线聊天</title>
    <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
    <link rel="stylesheet" href="${request.contextPath}/assets/layer/mobile/need/layer.css" media="all">
    <link rel="stylesheet" href="${request.contextPath}/assets/layer/theme/default/layer.css" media="all">

    <script type="text/javascript" src="${request.contextPath}/assets/jquery-2.2.0.min.js?compile=false" ></script>
    <script type="text/javascript" src="${request.contextPath}/assets/layer/mobile/layer.js?compile=false" ></script>
    <script type="text/javascript" src="${request.contextPath}/assets/sockjs.js?compile=false" ></script>
    <script type="text/javascript" src="${request.contextPath}/assets/stomp.js?compile=false" ></script>
    <script type="text/javascript" src="${request.contextPath}/assets/spring-websocket.js?compile=false" ></script>
    <script type="text/javascript" src="${request.contextPath}/assets/layer/layer.js?compile=false" ></script>


    <style>
        *{
            font-family: Consolas;
        }
        ul{
            list-style: none;
        }
        ul li{text-align: right;margin-right:24px;}
        #send_message{width:60%;height:28px;margin:2px 20px;}
        .left {text-align: left;line-height: 40px;display:block;}
        .right {text-align: right;line-height:40px;display:block;}
        .left img {width: 50px;height:50px;float: left;display: inline-block}
        .right img {width: 50px;height:50px;float: right}
        .left section {
            margin: 15px 0px;
            position: relative;
            padding: 2px 15px;
            background: #ef960e;
            -moz-border-radius: 12px;
            -webkit-border-radius: 12px;
            border-radius: 12px;
        }
        .left section:before {
            position: absolute;
            content: "";
            width: 0;
            height: 0;
            right: 100%;
            top: 38px;
            border-top: 13px solid transparent;
            border-right: 26px solid #ef960e;
            border-bottom: 13px solid transparent;
        }

        @media only screen and (max-width: 735px) {
            .left section {
                left: 48px;
                margin: 15px 0px;
                position: relative;
                padding: 2px 15px;
                background: #ef960e;
                -moz-border-radius: 12px;
                -webkit-border-radius: 12px;
                border-radius: 12px;
            }
            .left section:before {
                position: absolute;
                content: "";
                width: 0;
                height: 0;
                right: 100%;
                top: 38px;
                border-top: 13px solid transparent;
                border-right: 26px solid #ef960e;
                border-bottom: 13px solid transparent;
            }
            .right section {
                right: 48px;
                margin: 15px 0px;
                position: relative;
                padding: 2px 15px;
                background: #1E9FFF;
                -moz-border-radius: 12px;
                -webkit-border-radius: 12px;
                border-radius: 12px;
            }

            .right section:before {
                position: absolute;
                content: "";
                width: 0;
                height: 0;
                left: 100%;
                top: 38px;
                border-top: 13px solid transparent;
                border-left: 26px solid #1E9FFF;
                border-bottom: 13px solid transparent;
            }
        }

        .right section {
            margin: 15px 0px;
            position: relative;
            padding: 2px 15px;
            background: #1E9FFF;
            -moz-border-radius: 12px;
            -webkit-border-radius: 12px;
            border-radius: 12px;
        }
        .right section:before {
            position: absolute;
            content: "";
            width: 0;
            height: 0;
            left: 100%;
            top: 38px;
            border-top: 13px solid transparent;
            border-left: 26px solid #1E9FFF;
            border-bottom: 13px solid transparent;
        }

        .right section p{display: block;}
        .right section span{display: block;font-size:10px;}

    </style>
    <script type="text/javascript">
        $(function() {
            var timer;

            let offset = [];

            if(true) {
                if(($(window).height()<1366)&&($(window).width()<1100)) {
                    offset[0] = '98%';
                    offset[1] = '90%';
                }else{
                    offset[0] = '50%';
                    offset[1] = '80%';
                }

                load();

                $(window).resize(function(){
                    layer.closeAll();
                    if(($(window).height()<1366)&&($(window).width()<1100)) {
                        offset[0] = '98%';
                        offset[1] = '90%';
                    }else{
                        offset[0] = '50%';
                        offset[1] = '80%';
                    }
                    load();
                });
            }

            let sid = '${sid}';

            function load() {
                layer.open({
                    type: 1,
                    title: '匿名畅聊',
                    closeBtn: 0, //不显示关闭按钮
                    area: offset, //宽高
                    btn: ['发送','关闭'],
                    content: "<div><ul id='show_msg'></ul></div>",
                    btn3: function (index) {
                        close(index);
                    },
                    btn2: function(index){
                        let msg = $("#send_message").val().trim();
                        if(msg=='') {
                            parent.layer.msg("输入消息后才能发送!",{icon:5});
                            return false;
                        }
//                        timer = setInterval(function () {
                            client.send("/app/hello", {
                                name:"admin"
                            }, JSON.stringify({'msg':$("#send_message").val().trim(),'sid':sid}));
//                        },500);
                        $("#send_message").val('');
                        return false;
                    }

                });
                if($(window).width()<330) {
                    $(".layui-layer-btn0").before("<span class='layui-form'><div style='display: inline;float:left;width:100px;'><textarea type='text' style='width:100px;' id='send_message'/></textarea></div><span>");
                    // $(".layui-layer-btn0").css({"float":"left","display":"inline"});
                    // $(".layui-layer-btn1").css({"float":"left","display":"inline"});
                }else if(offset[0]=="98%") {
                    $(".layui-layer-btn0").before("<span class='layui-form'><div class='layui-input-inline' style='display: inline;'><textarea type='text' style='width:120px;' class='form-control' id='send_message' /></textarea></div><span>");
                }else{
                    $(".layui-layer-btn0").before("<span class='layui-form'><div class='layui-input-inline' style='display: inline'><textarea type='text' class='form-control' id='send_message'></textarea></div><span>");
                }
            }

            // var socket = new SockJS("${createLink(uri: '/stomp')}");
            // var client = Stomp.over(socket);
            var url = "ws://localhost:8080/stomp";
            var client = Stomp.client(url);

            client.connect({
                name:"admin"
            }, function() {
                client.subscribe("/user/queue/hello", function(message) {
                    console.log("aaaaaaaaaaaaaaaaaaaa");
                    let data = JSON.parse(message.body);
                    if(sid == data.sid) {
                        $("#show_msg").append("<li class='right'><div style='width:60%;display:inline-block;word-wrap: break-word;word-break: normal;'><section><p>" + data.msg + "</p></section></div><div style='width:10%;display:inline-block;margin:-28px auto;'><img src='${request.contextPath}/assets/global_anon.png'/></div></li>");
                    }else{
                        $("#show_msg").append("<li class='left'><div style='width:10%;display:inline-block;margin:-28px auto;'><img src='${request.contextPath}/assets/pink_anon.png'/></div><div style='width:60%;display:inline-block;word-wrap: break-word;word-break: normal;'><section><p>"+data.msg+"</p></section></div></li>");
                    }
                });
            });

        });
    </script>
</head>
<body>
    <p2>domo码云地址:<a href="https://gitee.com/lj18883588608/chat" target="_blank">立即前往</a></p2>
</body>
</html>

后端代码
自定义配置项文件WebSocketConfig.groovy

package com

import grails.plugin.springwebsocket.GrailsSimpAnnotationMethodMessageHandler
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.messaging.MessageChannel
import org.springframework.messaging.simp.SimpMessagingTemplate
import org.springframework.messaging.simp.config.ChannelRegistration
import org.springframework.messaging.simp.config.MessageBrokerRegistry
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker
import org.springframework.web.socket.config.annotation.StompEndpointRegistry
import org.springframework.web.socket.config.annotation.WebSocketTransportRegistration

@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

    @Override
    void configureMessageBroker(MessageBrokerRegistry messageBrokerRegistry) {
        messageBrokerRegistry.enableSimpleBroker "/queue", "/topic"
        messageBrokerRegistry.setApplicationDestinationPrefixes "/app"
        messageBrokerRegistry.setUserDestinationPrefix("/user/")
    }

    @Override
    void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
        stompEndpointRegistry
                .addEndpoint("/stomp")
                .setAllowedOrigins("*")
                .addInterceptors(new SessionAuthHandshakeInterceptor())
//                .withSockJS()
    }

    /**
     * 配置客户端入站通道拦截器
     */
    @Override
    void configureClientInboundChannel(ChannelRegistration registration) {
        registration.setInterceptors(createUserInterceptor())
    }

    /*将客户端渠道拦截器加入spring ioc容器*/
    @Bean
    public UserInterceptor createUserInterceptor() {
        return new UserInterceptor()
    }

    @Override
    void configureWebSocketTransport(WebSocketTransportRegistration registration) {
        registration.setMessageSizeLimit(500 * 1024 * 1024)
        registration.setSendBufferSizeLimit(1024 * 1024 * 1024)
        registration.setSendTimeLimit(200000)
    }

    @Bean
    GrailsSimpAnnotationMethodMessageHandler grailsSimpAnnotationMethodMessageHandler(
            MessageChannel clientInboundChannel,
            MessageChannel clientOutboundChannel,
            SimpMessagingTemplate brokerMessagingTemplate
    ) {
        def handler = new GrailsSimpAnnotationMethodMessageHandler(clientInboundChannel, clientOutboundChannel, brokerMessagingTemplate)
        handler.destinationPrefixes = ["/app"]
        return handler
    }

}

UserInterceptor.groovy

package com

import org.springframework.boot.actuate.autoconfigure.ShellProperties
import org.springframework.messaging.Message
import org.springframework.messaging.MessageChannel
import org.springframework.messaging.simp.stomp.StompCommand
import org.springframework.messaging.simp.stomp.StompHeaderAccessor
import org.springframework.messaging.support.ChannelInterceptor
import org.springframework.messaging.support.MessageHeaderAccessor

/**
 * Created by Administrator on 2018/11/7.
 */
class UserInterceptor implements ChannelInterceptor{
    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class)
        if (StompCommand.CONNECT.equals(accessor.getCommand())) {
            User user = (User)accessor.getSessionAttributes().get(Constants.WEBSOCKET_USER_KEY)
            accessor.setUser(user)
        }
        return message
    }

    @Override
    void postSend(Message<?> message, MessageChannel channel, boolean sent) {

    }

    @Override
    void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) {

    }

    @Override
    public boolean preReceive(MessageChannel channel) {
        return false;
    }

    @Override
    public Message<?> postReceive(Message<?> message, MessageChannel channel) {
        return null;
    }

    @Override
    void afterReceiveCompletion(Message<?> message, MessageChannel channel, Exception ex) {

    }
}

SessionAuthHandshakeInterceptor.groovy

package com

import org.slf4j.Logger
import org.slf4j.LoggerFactory
import org.springframework.http.server.ServerHttpRequest
import org.springframework.http.server.ServerHttpResponse
import org.springframework.http.server.ServletServerHttpRequest
import org.springframework.web.socket.WebSocketHandler
import org.springframework.web.socket.server.HandshakeInterceptor

import javax.servlet.http.HttpSession

/**
 * Created by Administrator on 2018/11/7.
 */
class SessionAuthHandshakeInterceptor implements HandshakeInterceptor{

    private Logger logger = LoggerFactory.getLogger(SessionAuthHandshakeInterceptor.class)

    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        HttpSession session = getSession(request)
        if(session == null || session.getAttribute(Constants.SESSION_USER) == null){
            logger.error("websocket权限拒绝")
            return false
            // throw new CmiException("websocket权限拒绝")
        }
        attributes.put(Constants.WEBSOCKET_USER_KEY, session.getAttribute(Constants.SESSION_USER))
        return true
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {

    }

    private HttpSession getSession(ServerHttpRequest request) {
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest serverRequest = (ServletServerHttpRequest) request
            return serverRequest.getServletRequest().getSession(false)
        }
        return null
    }
}

控制层代码

package com.system

import com.Constants
import com.User
import com.common.JsoupUtils
import grails.converters.JSON
import grails.web.controllers.ControllerMethod
import org.springframework.http.server.ServerHttpRequest
import org.springframework.http.server.ServletServerHttpRequest
import org.springframework.messaging.handler.annotation.MessageMapping
import org.springframework.messaging.handler.annotation.Payload
import org.springframework.messaging.handler.annotation.SendTo
import org.springframework.messaging.simp.SimpMessageSendingOperations
import org.springframework.messaging.simp.annotation.SendToUser
import org.springframework.messaging.simp.user.SimpUserRegistry
import org.springframework.web.context.request.RequestContextHolder

import javax.servlet.http.HttpSession
import javax.websocket.OnMessage
import static grails.async.Promises.task

class MessageController {
    List content = []
    Boolean isOver = false
    MessageService messageService
    SimpUserRegistry userRegistry

//    SimpMessagingTemplate brokerMessagingTemplate
//    def messageService

    def index() {
        isOver = false
        println "正在请求index方法"
        String sid = getUid()
//        task{
//            update()
//        }
        HttpSession session = request.getSession()
        session.setAttribute(Constants.SESSION_USER,  new User("admin"))
        [sid:"admin"]
    }

    @ControllerMethod
    @MessageMapping("/hello")
    @SendTo("/topic/hello")
    @SendToUser("/queue/hello")
//    @Payload(required=false)
    protected String hello(Map<String,Object> message) {
        println "正在请求这里"
        def map = [:]
        def msg = JsoupUtils.cleanXss(message.msg)?:'<br/>'
        if(msg.contains("<img")){
            msg += "&nbsp;"
        }
//        def status = content.size()>0 ? 200 : 400
//        def msg = msg
//        content = []
        map.msg = msg
        map.sid = message.sid
//        map.status = status
//        map.end = isOver ? 1 : 2
//        simpMessageSendingOperations.convertAndSend("/topic/hello", map)
        // println "当前在线人数:${userRegistry.getUserCount()}"
        messageService.sendToUser(map)
//         return map as JSON
    }

    def update(){
        println "开始执行update方法"
        for (int i = 0;i<50;i++){
            println "加入了一句话哦"
            content.add("加入一句话${i}")
            hello(["msg":{}])
            Thread.sleep(1000)
        }
        isOver = true
    }

    @OnMessage
    def test(){
        println "message"
    }

    def user = {

    }

    public String getUid() {
        return RequestContextHolder?.currentRequestAttributes()?.sessionId.toString()
    }
}

service 代码

package com.system

import grails.converters.JSON
import org.springframework.messaging.simp.SimpMessagingTemplate

class MessageService {
    SimpMessagingTemplate brokerMessagingTemplate
    void hello(def msg) {
//        for(int i = 0;i<10; i++){
            brokerMessagingTemplate.convertAndSend "/topic/hello", (msg as JSON).toString()
//            Thread.sleep(1000)
//        }
    }

    void sendToUser(def msg) {
        println 222222
        brokerMessagingTemplate.convertAndSendToUser "admin", "/queue/hello", (msg as JSON).toString()
    }


}

User 实体类代码

package com

import java.security.Principal

/**
 * Created by Administrator on 2018/11/7.
 */
public final class User implements Principal{
    private final String name

    public User(String name) {
        this.name = name
    }

    @Override
    public String getName() {
        return name
    }
}

你期待的结果是什么?实际看到的错误信息又是什么?

连接是可以的,发送消息后台也在执行,但是就是客户端接收不到消息
希望可以指定用户发送消息,可以接收到。

阅读 3.4k
1 个回答
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏