上一篇:SpringBoot如何整合ElasticSearch? | 带你读《SpringBoot实战教程》之四十
下一篇:SpringBoot如何整合AngularJS? | 带你读《SpringBoot实战教程》之四十二
本文来自于千锋教育在阿里云开发者社区学习中心上线课程《SpringBoot实战教程》,主讲人杨红艳,点击查看视频内容。
SpringBoot整合WebSocket
HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。
这种单向请求的缺点,如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数 Web 应用程序将通过频繁的异步JavaScript和XML(AJAX)请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。
WebSocket 连接允许客户端和服务器之间进行全双工通信,以便任一方都可以通过建立的连接将数据推送到另一端。WebSocket 只需要建立一次连接,就可以一直保持连接状态。这相比于轮询方式的不停建立连接显然效率要大大提高。
那么如何使用呢?
添加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
WebSocket的配置类:
//WebSocket的配置类
@Configuration
//开启对WebSocket的支持
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{
//注册一个STOMP协议的节点,并映射到指定的URL
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
//注册一个STOMP的endpoint,并指定使用SockJS协议
registry.addEndpoint("/endpointSocket").withSockJS();
}
//配置消息代理
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//配置一个广播式的消息代理
registry.enableSimpleBroker("/topic");
}
}
新建包:com.qianfeng.pojo:
浏览器向服务器发送消息实体类:
public class SocketMessage {
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
服务器向浏览器响应数据的封装实体类:
public class SocketResponse {
private String responseMessage;
public SocketResponse(String responseMessage) {
this.responseMessage = responseMessage;
}
public String getResponseMessage() {
return responseMessage;
}
}
新建包:com.qianfeng.controller
控制类:
@Controller
public class WebSocketController {
private SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//当浏览器向服务器端发送STOMP请求时,通过@MessageMapping注解来映射/getServerTime地址
@MessageMapping(value = "/getServerTime")
//当服务端有消息时,会对订阅了@SendTo中的路径的客户端发送消息
@SendTo(value = "/topic/getResponse")
public SocketResponse serverTime(SocketMessage message) throws InterruptedException {
return new SocketResponse(message.getMessage() + sf.format(new Date()));
}
@RequestMapping("/index")
public String toPage() {
return "webSocket";
}
}
创建页面templates:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"></meta>
<title>集成集成WebSocket示例</title>
</head>
<body>
<div>
<button id="connect" onclick="connect();">连接</button>
<button id="disconnect" onclick="disconnect();">断开连接</button>
<button id="serverTimeId" onclick="getServerTime();">获取服务器端时间</button>
<hr/>
<span id="showServerTime"></span>
</div>
<script type="text/javascript" src="sockjs.min.js"></script>
<script type="text/javascript" src="stomp.min.js"></script>
<script type="text/javascript" src="jquery-3.1..min.js"></script>
<script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
<script type="text/javascript">
var stompClient = null;
$(function(){
setConnect(false);
});
function setConnect(connected) {
$("#connect").attr({disabled:connected});
$("#disconnect").attr({disabled:!connected});
}
function connect() {
var socket = new SockJS('/endpointSocket');
//创建STOMP客户端连接,目标地址为/endpointSocket的STOMP代理
stompClient = stomp.over(socket);
//打印stomp输出信息
stompClient.debug = function(str) {
console.log(str+ "\n");
};
//建立连接
stompClient.connect({},function(frame)) {
setConnect(true);
//连接成功后订阅/topic/getResponse目标发送的消息,该地址在Controller中用@SendTo指定
stompClient.subscribe('/topic/getResponse',function(response) {
showResponse(JSON.parse(response.body).responseMessage);
});
});
}
function disconnect() {
if(stompClient != null) {
stompClient.disconnect();
}
setConnect(false);
}
function getServerTime() {
var message = "The server time is : ";
//发送消息到服务端,/getServerTime地址是由Controller中的@MessageMapping指定
stompClient.send("/getServerTime",{},JSON.stringify({'message':message}));
}
function showResponse(message) {
var response = $("#showServerTime");
response.html(message);
}
</script>
</body>
</html>
执行结果: