websocket(三)–基于sockjs和stomp实现点对点通信
文章目录
一、简介
这里将介绍基于sockjs和stomp实现的点对点通信,即客户端发消息到服务端,服务端处理后返回消息给原客户端(或者发送消息给指定客户端),点对点通信,不是广播。由于点对点通信和广播有类似,这里仅对需要使用到的新内容进行介绍,以及完整的点对点通信示例。
二、新知识点
2.1 org.springframework.messaging.simp.SimpMessagingTemplate
提供给用户发送消息的方法。
2.2 @SendToUser
点对点通信中,客户端订阅的地址。
2.3 java.security.Principal
用户身份标识,需要在WebSocketMessageBrokerConfigurer.configureClientInboundChannel方法设置用户信息。
2.4 org.springframework.messaging.simp.stomp.StompHeaderAccessor
请求消息头信息。
三、完整示例
3.1 添加maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
3.2 spring mvc配置
package com.dragon.single_talk.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
/
* 添加静态文件
*
* @param registry
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/").addResourceLocations("classpath:/static/");
}
}
3.3 websocket配置
package com.dragon.single_talk.config;
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.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import java.security.Principal;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//addEndpoint客户端连接地址,setAllowedOrigins允许连接的域名,withSockJS支持以SockJS连接服务器
registry.addEndpoint("/endpoint").setAllowedOrigins("*").withSockJS();
}
/
* 设置消息代理,默认会配置一个简单的内存消息代理
*
* @param registry
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//表示在/singleTalkClient地址前缀上,服务端可以向客户端发送消息(也是客户端订阅地址前缀),即是@SendToUser中地址对应
registry.enableSimpleBroker("/singleTalkClient");
//点对点推送时,客户端订阅消息的前缀,/user为默认值
registry.setUserDestinationPrefix("/user");
}
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
@Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
String uid = accessor.getNativeHeader("uid").get(0);
Principal principal = () -> uid;
//设置用户信息
accessor.setUser(principal);
return message;
}
return message;
}
});
}
}
3.4 定义消息载体类
package com.dragon.single_talk.bean;
import lombok.Data;
/
* 消息载体
*/
@Data
public class MsgBean {
private String uid;
private String msg;
}
3.5 定义转发页面和消息的controller
package com.dragon.single_talk.controller;
import com.dragon.single_talk.bean.MsgBean;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.SendToUser;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
import java.security.Principal;
import java.util.Map;
@Controller
public class SingleWebSocketController {
//提供给用户发送消息处理
@Resource
private SimpMessagingTemplate simpMessagingTemplate;
@RequestMapping("/singleTalk/{uid}")
public String simpleTalk(Model model, @PathVariable String uid){
model.addAttribute("uid", uid);
return "singleTalk";
}
@MessageMapping("/singleTalkServer") //客户端发到服务端
@SendToUser(value = "/singleTalkClient", broadcast = true) //服务端发到客户端,客户端订阅, broadcast是否推送到同一session的不同终端页面中
public MsgBean singleTalk(MsgBean msg,
StompHeaderAccessor accessor, //所有消息头信息
@Headers Map<String, Object> headers, //所有头部值
Principal principal , //登录验证信息
@Header(name="simpSessionId") String sessionId, //指定头部的值 ,这里指sessionId
Message message, //完整消息,包含消息头和消息体(即header和body)
@Payload String body){ //消息体内容
//给指定客户端发消息
// simpMessagingTemplate.convertAndSendToUser("1", "/singleTalkClient", msg);
//消息直接返回
return msg;
}
}
3.6 spring boot配置文件application.properties
server.port=8080
3.7 spring boot启动类
package com.dragon.single_talk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SingleTalkApplication {
public static void main(String[] args) {
SpringApplication.run(SingleTalkApplication.class, args);
}
}
3.8 添加前端静态文件
添加前端静态文件jquery-3.1.1.min.js、sockjs.js、stomp.min.js
3.9 前端页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
#historyId {
border: 1px black solid;
height: 370px;
width: 500px;
overflow-y: scroll;
word-break: break-all;
}
#msgId {
width: 500px;
height: 130px;
}
#talkId {
/*text-align: center;*/
}
</style>
</head>
<body>
<section id="talkId">
<header>
tip:<span id="tipId" th:text="${uid}"></span>
</header>
<article id="historyId">
</article>
<article>
<textarea id="msgId"></textarea>
</article>
<footer>
<button id="sendId">send</button>
<button id="closeId">close</button>
<button id="clearId">clear</button>
<button id="connectId">connect</button>
</footer>
</section>
<script src="../static/js/jquery-3.1.1.min.js"></script>
<script src="../static/js/sockjs.js"></script>
<script src="../static/js/stomp.min.js"></script>
<script type="text/javascript" th:inline="javascript">
$(function () {
var client;
var headers = {
uid: [[${uid}]]
};
var connect = function () {
var socket = new SockJS("http://localhost:8080/endpoint");
client = Stomp.over(socket);
client.connect(headers, function (frame) {
//订阅地址要以/user为前缀,标识是点对点通信,只有当前用户才会收到
client.subscribe("/user/singleTalkClient",
function (response) {
var obj = JSON.parse(response.body);
console.log("receive: " + JSON.stringify(response));
$("#historyId").append("<p>" + obj.uid + ":" + obj.msg + "</p>");
$("#historyId").scrollTop($("#historyId")[0].scrollHeight);
},
function (err) {
console.log("err: " + err);
}
);
});
$("#tipId").html("connect success");
}
var sendMsg = function () {
var msg = $("#msgId").val();
client.send("/singleTalkServer", headers, JSON.stringify({'uid': [[${uid}]], 'msg': msg}));
$("#msgId").val("")
};
//点按钮发送消息
$("#sendId").click(function () {
sendMsg();
});
//回车发消息
$(document).keyup(function (e) {
if (e.keyCode == 13) {
sendMsg();
}
});
//清空记录
$("#clearId").click(function () {
$("#historyId").empty();
});
//连接
$("#connectId").click(function () {
connect();
});
//关闭连接
$("#closeId").click(function () {
client.disconnect();
$("#tipId").html("connect close");
});
})
</script>
</body>
</html>
至此,点对点通信示例完成。
http://localhost:8080/singleTalk/1
http://localhost:8080/singleTalk/2
使用上面地址测试,可验证只有当前客户端才会收到发出的消息。