一、介绍
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
其他特点包括:
(1)建立在 TCP 协议之上,服务器端的实现比较容易。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,也可以发送二进制数据。
(5)没有同源限制,客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
二、代码实现
一个集成在springboot中的小demo
maven依赖
<!-- webSocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
配置类和常量类
package com.huaguoguo.constant;
/**
* @Author:huaguoguo
* @Description: websocket常量类
* @Date: 2018/5/21 16:40
*/
public class WebSocketConstant {
/**
* 链接地址
*/
public static String WEBSOCKETPATHPERFIX = "/ws-push";
public static String WEBSOCKETPATH = "/endpointWisely";
/**
* 消息代理路径
*/
public static String WEBSOCKETBROADCASTPATH = "/topic";
/**
* 前端发送给服务端请求地址
*/
public static final String FORETOSERVERPATH = "/welcome";
/**
* 服务端生产地址,客户端订阅此地址以接收服务端生产的消息
*/
public static final String PRODUCERPATH = "/topic/getResponse";
/**
* 点对点消息推送地址前缀
*/
public static final String P2PPUSHBASEPATH = "/user";
/**
* 点对点消息推送地址后缀,最后的地址为/user/用户识别码/msg
*/
public static final String P2PPUSHPATH = "/msg";
}
package com.huaguoguo.config;
import com.huaguoguo.constant.WebSocketConstant;
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;
@Configuration
//注解开启使用STOMP协议来传输基于代理(message broker)的消息,这时控制器支持使用@MessageMapping,就像使用@RequestMapping一样
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{
/**
* 注册STOMP协议的节点(endpoint),并映射指定的url
* @param stompEndpointRegistry
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
//注册一个STOMP的endpoint,并指定使用SockJS协议
stompEndpointRegistry.addEndpoint(WebSocketConstant.WEBSOCKETPATH).withSockJS();
}
/**
* 配置消息代理(Message Broker)
* @param registry
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
//广播式应配置一个/topic消息代理
registry.enableSimpleBroker(WebSocketConstant.WEBSOCKETBROADCASTPATH);
//定义一对一推送的时候前缀
registry.setUserDestinationPrefix(WebSocketConstant.P2PPUSHBASEPATH);
//定义websoket前缀
registry.setApplicationDestinationPrefixes(WebSocketConstant.WEBSOCKETPATHPERFIX);
}
}
controller
package com.huaguoguo.controller;
import com.huaguoguo.constant.WebSocketConstant;
import com.huaguoguo.entity.websocket.WiselyMessage;
import com.huaguoguo.entity.websocket.WiselyResponse;
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 java.util.ArrayList;
import java.util.List;
/**
* webSocket控制器
*/
@Controller
public class WebSocketController {
@MessageMapping(WebSocketConstant.FORETOSERVERPATH) //当浏览器向服务端发送请求时,通过@MessageMapping映射/welcome这个地址,类似于@ResponseMapping
@SendTo(WebSocketConstant.PRODUCERPATH)//当服务器有消息时,会对订阅了@SendTo中的路径的浏览器发送消息
public WiselyResponse say(WiselyMessage message) {
return new WiselyResponse("Welcome, " + message.getName() + "!");
}
}
html页面
springboot中,html引擎使用thymeleaf
需要加入thymeleaf的maven依赖
<!-- thymeleaf模板引擎 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
在application.properties中,加入thymeleaf的配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.cache=false
将html页面放在resource/templates目录下
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>SpringBoot实现广播式WebSocket</title>
<!--<script th:src="@{sockjs.min.js}"></script>
<script th:src="@{stomp.min.js}"></script>
<script th:src="@{jquery.js}"></script>-->
<script src="https://cdn.bootcss.com/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdn.bootcss.com/stomp.js/2.3.3/stomp.min.js"></script>
<script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<noscript><h2 style="color:#ff0000">抱歉,您的浏览器不支持改功能!</h2></noscript>
<div>
<div>
<button id="connect" οnclick="connect();">连接</button>
<button id="disconnect" disabled="disabled" οnclick="disconnect();">断开连接</button>
</div>
<div id="conversationDiv">
<label>请输入您的姓名</label><input type="text" id="name"/>
<button id="sendName" οnclick="sendName();">发送</button>
<p id="response"></p>
</div>
</div>
</body>
<script type="text/javascript">
var stompClient = null;
var userId = 'd892bf12bf7d11e793b69c5c8e6f60fb';
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);//使用STMOP子协议的WebSocket客户端
stompClient.connect({}, function (frame) {//连接WebSocket服务端
setConnected(true);
console.log('Connected:' + frame);
//通过stompClient.subscribe订阅/topic/getResponse 目标(destination)发送的消息,这个是在控制器的@SentTo中定义的
stompClient.subscribe('/topic/getResponse', function (response) {
showResponse(JSON.parse(response.body).responseMessage);
});
//通过stompClient.subscribe()订阅服务器的目标是'/user/' + userId + '/msg'接收一对一的推送消息,其中userId由服务端传递过来,用于表示唯一的用户,通过此值将消息精确推送给一个用户
stompClient.subscribe('/user/' + userId + '/msg', function(respnose){
console.log(respnose);
showResponse1(JSON.parse(respnose.body).responseMessage);
});
});
}
function disconnect() {
if (stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendName() {
var name = $("#name").val();
alert(name);
//通过stompClient.send向/welcome 目标(destination)发送消息,这个是在控制器的@MessageMapping中定义的
stompClient.send("/ws-push/welcome", {}, JSON.stringify({'name': name}));
}
function showResponse(message) {
alert(message);
var response = $("#response");
response.html(message);
}
</script>
</html>
还需要一个controller来做页面跳转
package com.huaguoguo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @Author:huaguoguo
* @Description:页面跳转controller
* @Date: 2018/5/21 17:14
*/
@Controller
public class PageController {
@RequestMapping(value = "toWebSocket",method = RequestMethod.GET)
public String toWebSocket(){
return "webSocket";
}
}
三、效果展示
启动springboot工程,跳转到页面
点击“连接”
SUBSCRIBE已经出现订阅日志了,这时服务器推送的消息就可以被浏览器接收了
在输入框输入一个字符串,发送
MESSAGE就是服务器推送的消息
这样整个demo就成功了。
想看完整的代码,可以访问我的GitHub地址是
https://github.com/huaguoguo555/hellohuaguoguo