一、什么是 WebSocket
WebSocket协议是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工(full-duplex)通信,即允许服务器主动发送信息给客户端。因此,在WebSocket中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输,客户端和服务器之间的数据交换变得更加简单。
全双工(Full Duplex)是通讯传输的一个术语。通信允许数据在两个方向上同时传输,它在能力上相当于两个单工通信方式的结合。全双工指可以同时(瞬时)进行信号的双向传输(A→B且B→A)。指A→B的同时B→A,是瞬时同步的。
单工就是在只允许甲方向乙方传送信息,而乙方不能向甲方传送 。(比喻汽车的单行道。)
(查看更多:全双工——百度百科)
【注】所谓的“浏览器和服务器只需要完成一次握手”,是说在三次握手完成TCP连接后,还会传输一次握手数据;
二、WebSocket的特点
WebSocket既然是全双工通信,自然是能实现聊天软件、订阅、游戏等;其特点是:
- 建立在 TCP 协议之上,处于OSI模型中的应用层,服务器端的实现比较容易。
- 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
- 数据格式比较轻量,性能开销小,通信高效。
- 可以发送文本,也可以发送二进制数据。
- 没有同源限制,客户端可以与任意服务器通信。
- 协议标识符是
ws
(如果加密,则为wss
),服务器网址就是 URL。
三、Springboot 整合 WebSocket
1、pom.xml 引入依赖包
<!--websocket-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2、暴露 WebSocket 接口
@Slf4j
@Component
@ServerEndpoint(value = "/playBack")
public class WebSocketServerPlayBack {
/** 记录链接在线数量 **/
private static final AtomicInteger onlineCount = new AtomicInteger(0);
/** 存放每个客户端对应的 WebSocketServer 对象 **/
private static CopyOnWriteArraySet<WebSocketServerPlayBack> webSocketSet = new CopyOnWriteArraySet<>();
/** 与某个客户端的连接会话,需要通过它来给客户端发送数据 **/
private Session session;
/** 心跳报文 **/
private static final String HEARTBEAT_PACKETS = "The heartbeat packets";
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session) {
this.session = session;
// 加入set中
webSocketSet.add(this);
// 在线数加1
onlineCount.getAndIncrement();
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
// 从set中删除
webSocketSet.remove(this);
// 在线数减1
onlineCount.getAndDecrement();
}
/**
* 发生错误时调用
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("[历史数据回放] - WS 异常断开", session, error);
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) {
if (HEARTBEAT_PACKETS.equals(message)) {
log.debug("[消息订阅] - 心跳.");
return;
}
// TODO 接收前端入参后的业务处理
}
/**
* 群发自定义消息
*/
public static void sendInfo(String message) {
for (WebSocketServerPlayBack item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
log.error("[NVR 数据对接] - 数据推送异常, 数据: [{}].", message, e);
continue;
}
}
}
/**
* 指定会话推送
* @param message
*/
public static void sendInfo(Session session, String message) {
for (WebSocketServerPlayBack item : webSocketSet) {
try {
if (null != session && item.session.equals(session)) {
item.sendMessage(message);
}
} catch (IOException e) {
log.error("[数据对接] - 数据推送异常, 数据: [{}].", message, e);
continue;
}
}
}
/**
* 向链接推送消息
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
}
3、灵活配置 WebSocket ,避免报文太大,接收时报错
@Configuration
public class WebSocketConfig {
/**
* 自动注册使用了@ServerEndpoint注解声明的Websocket endpoint
*
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
/**
* 通信文本消息和二进制缓存区大小
* 避免对接 第三方 报文过大时,Websocket 1009 错误
* @return
*/
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
// 在此处设置bufferSize
container.setMaxTextMessageBufferSize(10240000);
container.setMaxBinaryMessageBufferSize(10240000);
container.setMaxSessionIdleTimeout(15 * 60000L);
return container;
}
}
4、调用接口调试
ws://216.1.1.7:2713/playBack
测试 webSocket 收发小工具:websocket.html