问题描述
目前使用到的框架是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 += " "
}
// 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
}
}
你期待的结果是什么?实际看到的错误信息又是什么?
连接是可以的,发送消息后台也在执行,但是就是客户端接收不到消息
希望可以指定用户发送消息,可以接收到。
再提供一个此插件的官方地址:
http://plugins.grails.org/plu...
希望对你回答此问题有帮助。