WebSocket 为浏览器和服务器端提供异步通信的功能,即
浏览器可以向服务器发送消息
,服务器也可以向浏览器发送消息
。WebSocket 需要浏览器的支持,如IE 10+、Chrome 13+、Firefox 6+。因为js插件对浏览器版本有要求。
Websocket 是通过一个socket来实现双工异步通讯的能力,开发时使用STOMP协议。
本事例基于springboot
。
@SendTo
: 一问一答的方式,结合@MessageMapping
一起使用。即当客户端给服务端发送消息时,服务端给出对应的推送回复。但是,凡是长连接到服务端的客户端都会收到一个回复。@MessageMapping
与@RequestMapping
用法相同。SimpMessagingTemplate
:用消息模板来推送消息,不需要客户端的触发,所有与服务端建议长连接的客户端都能收到推送消息。websocket
依赖如下:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
因为用到spring和thymeleaf,故还要添加
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
import org.springframework.context.annotation.Configuration;
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;
/**
* @描叙: 通过EnableWebSocketMessageBroker 开启使用STOMP协议来传输基于代理(message broker)的消息,
* 此时浏览器支持使用@MessageMapping 就像支持@RequestMapping一样。
*/
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) { //endPoint 注册协议节点,并映射指定的URl
//注册一个Stomp 协议的endpoint,并指定 SockJS协议
registry.addEndpoint("/endpointWisely").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {//配置消息代理(message broker)
//广播式应配置一个/topic 消息代理
registry.enableSimpleBroker("/topic");
}
}
新建一个controller,
import com.sqlb.websocket.bean.Message;
import com.sqlb.websocket.bean.Response;
import com.sqlb.websocket.service.WebSocketService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class WebSocketController {
@MessageMapping("/welcome") /** 浏览器发送请求通过@messageMapping 映射/welcome 这个地址。 message from browser to background*/
@SendTo("/topic/getResponse") /** 服务器端有消息时,会对 订阅@SendTo 中的路径 的浏览器 发送消息。 message from background to browser */
public Response say(Message message) throws Exception {
Thread.sleep(1000);
System.out.println("------------------------------" + message.getName() + "------------------------------");
return new Response("Welcome, " + message.getName() + "!");
}
}
@MessageMapping
browser 客户端发送的消息,被这个注解下的函数接收到。
@SendTo
注解下的函数返回的值,会发送给建立长连接的browser 客户端。
其中Response
和 Message
都是自定义的消息对象pojo:
/**
* @描叙: 浏览器向服务器发送的消息使用此类接受
*/
public class Message {
private String name;
public String getName(){
return name;
}
}
/**
* @描叙: 服务器向浏览器发送的此类消息。
*/
public class Response {
private String responseMessage;
public Response(String responseMessage) {
this.responseMessage = responseMessage;
}
public Response() {
}
public String getResponseMessage() {
return responseMessage;
}
public void setResponseMessage(String responseMessage) {
this.responseMessage = responseMessage;
}
}
同时别忘了 为HTML 提供便捷的路径映射。
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @描叙: 为HTML 提供便捷的路径映射。
*/
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/ws").setViewName("/ws");
}
}
这里要引入sockjs.min.js
、stomp.min.js
模块,因为使用Jquery,也引入。新建一个wx.html
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Spring Boot+WebSocket+广播式title>
head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">貌似你的浏览器不支持websocketh2>noscript>
<div>
<div>
<button id="connect" onclick="connect();">连接button>
<button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接button>
div>
<div id="conversationDiv">
<label>输入你的名字label><input type="text" id="name"/>
<button id="sendName" onclick="sendName();">发送button>
<p id="response">p>
div>
div>
<script th:src="@{sockjs.min.js}">script>
<script th:src="@{stomp.min.js}">script>
<script th:src="@{jquery.js}">script>
<script type="text/javascript">
var stompClient = null;
function setConnected(connected) {
document.getElementById('connect').disabled = connected;
document.getElementById('disconnect').disabled = !connected;
document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
$('#response').html();
}
function connect() {
var socket = new SockJS('/endpointWisely'); /** 链接SockJS 的endpoint 名称为"/endpointWisely" */
stompClient = Stomp.over(socket); /** 使用stomp子协议的WebSocket 客户端 */
stompClient.connect({}, function (frame) { /** 链接Web Socket的服务端。 */
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/getResponse', function (respnose) { /** 订阅/topic/getResponse 目标发送的消息。这个是在控制器的@SendTo中定义的。*/
showResponse(JSON.parse(respnose.body).responseMessage);
});
});
}
function disconnect() {
if (stompClient != null) {
stompClient.disconnect();/** 断开连接 */
}
setConnected(false);
console.log("Disconnected");
}
function sendName() {
var name = $('#name').val();
/** 通过stompClient.send 向/welcome 目标 发送消息,这个是在控制器的@messageMapping 中定义的。*/
stompClient.send("/Welcome", {}, JSON.stringify({'name': name}));
}
function showResponse(message) {
var response = $("#response");
response.html(message);
}
script>
body>
html>
上面注释写得很清楚了,就不讲解了。以上是利用@SendTo
实现服务端给客户端推送消息。
在上面的基础上,再添加一个依赖:
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-messagingartifactId>
<version>4.2.3.RELEASEversion>
dependency>
代码实现:
import com.sqlb.websocket.bean.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
/**
* @创建时间:17:23 2018/4/24
* @描叙: 向浏览器端发送数据
*/
@Service
public class WebSocketService {
@Autowired
private SimpMessagingTemplate template;
/** 从服务端发送消息到客户端浏览器 */
public void sendMessage() throws Exception {
for (int i = 0; i < 10; i++) {
Thread.sleep(1500);
template.convertAndSend("/topic/getResponse", new Response("Welcome,websocket!" + i));
System.out.println("----------------------send" + i + "------------------------");
}
}
}
凡是客户端监听了/topic/getResponse
的,都可以接收到来至服务端推广的消息。
本篇文章参考了 spring boot +WebSocket 广播式(一) 和 spring boot +WebSocket 广播式(二)