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