原生websocket实现方式需要新版浏览器的支持。为兼容不支持websocket的浏览器,可以使用sockjs和stomp,当浏览器不支持时,会自动改用轮询等方式来模拟websocket实现。这里介绍基于sockjs和stomp实现websocket的广播通信。
sockjs是javascript库,提供了服务器和浏览器间的双向通信,优先使用websocket通信,当浏览器不支持时,则自动改为轮询与服务器通信。
stomp(streaming text orientated messaging protocal),即面向流文本的消息协议,定义了服务端与客户端进行消息传递的文本格式,其使用基于帧(frame)的格式定义消息。
开启stomp协议,使用基于消息代理(message broker)来传输,消息的接收支持注解@MessageMapping;
定义来自客户端用于配置消息处理如stomp协议的方法,关键方法有:
服务端接收消息的地址(即是客户端发送消息的地址)
服务端广播的地址(即是客户端监听接收消息的地址);
需要下载sockjs.js和stomp.min.js
连接服务端,如:
var socket = new SockJS("/endpoint");
获取stomp对象,如:
client = Stomp.over(socket);
连接并订阅消息, 如:
client.connect(headers, function (frame) {
client.subscribe("/topicTalkClient", function (response) {..})
});
发送消息,如:
client.send("/topicTalkServer", headers, JSON.stringify({'uid': 1, 'msg': msg}));
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
package com.dragon.public_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/");
}
}
package com.dragon.public_talk.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
/
* 注册stomp的endpoint端点
* @param registry
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//addEndpoint客户端连接地址,setAllowedOrigins允许连接的域名,withSockJS支持以SockJS连接服务器
registry.addEndpoint("/endpoint").setAllowedOrigins("*").withSockJS();
}
/
* 设置消息代理,默认会配置一个简单的内存消息代理
* @param registry
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//配置发送给客户端消息的地址前缀,这是"/topicTalkClient",可配置多个,对就@SendTo中的地址
registry.enableSimpleBroker("/topicTalkClient");
}
}
package com.dragon.public_talk.bean;
import lombok.Data;
/
* 消息载体
*/
@Data
public class MsgBean {
private String uid;
private String msg;
}
package com.dragon.public_talk.controller;
import com.dragon.public_talk.bean.MsgBean;
import org.springframework.messaging.Message;
import org.springframework.messaging.handler.annotation.*;
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 java.util.Map;
@Controller
public class TopicWebSocketController {
/
* 对话页面
*
* @param model
* @param uid
* @return
*/
@RequestMapping("/topicTalk/{uid}")
public String topicTalk(Model model, @PathVariable String uid) {
model.addAttribute("uid", uid);
return "topicTalk";
}
/
* 接收和广播消息
*
* @param msg
* @return
*/
@MessageMapping("/topicTalkServer") //客户端发到服务端
@SendTo("/topicTalkClient") //服务端发到客户端,客户端订阅
public MsgBean topicTalk(MsgBean msg, //消息
StompHeaderAccessor accessor, //所有消息头信息
@Headers Map<String, Object> headers, //所有头部值
@Header(name = "simpSessionId") String sessionId, //指定头部的值 ,这里指sessionId
Message message, //完整消息,包含消息头和消息体(即header和body)
@Payload String body) { //消息体内容
//消息直接返回
return msg;
}
}
server:
port: 8010
package com.dragon.public_talk;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class PublicTalkApplication {
public static void main(String[] args) {
SpringApplication.run(PublicTalkApplication.class, args);
}
}
添加前端静态文件jquery-3.1.1.min.js、sockjs.js、stomp.min.js
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Titletitle>
<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">sendbutton>
<button id="closeId">closebutton>
<button id="clearId">clearbutton>
<button id="connectId">connectbutton>
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 () {
//stomp协议客户端对象
var client;
//连接
var headers = {'uid': [[${uid}]]};
var connect = function () {
//连接endpoint地址,这里完整地址为: http://localhost:8010/endpoint
var socket = new SockJS("/endpoint");
//获取stomp协议的客户端对象
client = Stomp.over(socket);
client.connect(headers, function (frame) {
//订阅消息,对应后端@SendTo中地址
client.subscribe("/topicTalkClient", function (response) {
var obj = JSON.parse(response.body)
$("#historyId").append(""
+ obj.uid + ":" + obj.msg + "");
$("#historyId").scrollTop($("#historyId")[0].scrollHeight);
})
});
$("#tipId").html("connect success");
};
var sendMsg = function () {
var msg = $("#msgId").val();
if (/^\s*$/.test(msg)) {
$("#msgId").val("");
return;
}
//发送消息,对应@MessageMapping中地址
client.send("/topicTalkServer", 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>
至此,示例websocket广播结束。
访问地址:
http://localhost:8010/topicTalk/1
http://localhost:8010/topicTalk/2
所在页面均会收到消息