About the problem that grails uses grails-spring-websocket-2.3.0 to specify users to send messages and cannot receive messages.

problem description

the framework currently in use is grails
when using the grails-spring-websocket-2.3.0 plug-in
when sending a message to a specified user through brokerMessagingTemplate.convertAndSendToUser, the client cannot receive the message

the environmental background of the problems and what methods you have tried

if you don"t specify a user to use it directly

client.connect(, function() {
                client.subscribe("/topic/hello", function(message) {
                    console.log("aaaaaaaaaaaaaaaaaaaa");
                    let data = JSON.parse(message.body);
                    if(sid == data.sid) {
                        $("-sharpshow_msg").append("<li class="right"><div style="width:60%;display:inline-block;word-wrap: break-word;word-break: normal;"><section>

" + data.msg + "

</section></div><div style="width:10%;display:inline-block;margin:-28px auto;"><img src="${request.contextPath}/assets/global_anon.png"/></div></li>"); }else{ $("-sharpshow_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>

"+data.msg+"

</section></div></li>"); } }); });

it is possible to receive code in this way, but now I want to challenge the specified user to send a message, but it is not successful.
I wrote the code
https://blog.csdn.net/u011943.

according to this blog.

related codes

/ / Please paste the code text below (do not replace the code with pictures)
here is all my code
front-end code

<!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;}
        -sharpsend_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: -sharpef960e;
            -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 -sharpef960e;
            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: -sharpef960e;
                -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 -sharpef960e;
                border-bottom: 13px solid transparent;
            }
            .right section {
                right: 48px;
                margin: 15px 0px;
                position: relative;
                padding: 2px 15px;
                background: -sharp1E9FFF;
                -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 -sharp1E9FFF;
                border-bottom: 13px solid transparent;
            }
        }

        .right section {
            margin: 15px 0px;
            position: relative;
            padding: 2px 15px;
            background: -sharp1E9FFF;
            -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 -sharp1E9FFF;
            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 = $("-sharpsend_message").val().trim();
                        if(msg=="") {
                            parent.layer.msg("",{icon:5});
                            return false;
                        }
//                        timer = setInterval(function () {
                            client.send("/app/hello", {
                                name:"admin"
                            }, JSON.stringify({"msg":$("-sharpsend_message").val().trim(),"sid":sid}));
//                        },500);
                        $("-sharpsend_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) {
                        $("-sharpshow_msg").append("<li class="right"><div style="width:60%;display:inline-block;word-wrap: break-word;word-break: normal;"><section>

" + data.msg + "

</section></div><div style="width:10%;display:inline-block;margin:-28px auto;"><img src="${request.contextPath}/assets/global_anon.png"/></div></li>"); }else{ $("-sharpshow_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>

"+data.msg+"

</section></div></li>"); } }); }); }); </script> </head> <body> <p2>domo:<a href="https://gitee.com/lj18883588608/chat" target="_blank"></a></p2> </body> </html>

backend code
Custom configuration item file 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
    }
}

Control layer code

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 += ""
        }
//        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;iPP){
            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 Code

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; iPP){
            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 entity class code

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
    }
}

what result do you expect? What is the error message actually seen?

The

connection is OK, and the sending message background is also executing, but the client cannot receive the message
wants to specify the user to send the message and can receive it.

Oct.26,2021

provide the official address of this plugin:
http://plugins.grails.org/plu.
hope to help you answer this question.

Menu