SpringBoot 全家桶 | WebSocket服务端与客户
本文源码:Gitee·点这里
介绍
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
参考
Server端
Server端我们使用SpringBoot的一个包spring-boot-starter-websocket
,我们来引入它
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
WebSocket处理器
WebSocket处理器用来处理客户端发送的消息,Spring
中常用的是两个TextWebSocketHandler
和BinaryWebSocketHandler
,我们使用TextWebSocketHandler
import org.springframework.web.socket.handler.TextWebSocketHandler;
public class ChatWebSocketHandler extends TextWebSocketHandler {
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String msg = message.getPayload();
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
}
}
重写TextWebSocketHandler
的四个方法:
-
afterConnectionEstablished
成功创建连接后调用 -
handleTextMessage
收到客户端消息后调用 -
handleTransportError
连接异常时调用 -
afterConnectionClosed
连接关闭后调用
WebSocketSession
是客户端与服务端建立的回话,可以通过close()
方法主动关闭连接
TextMessage
为收到的消息,可以通过getPayload()
方法获取消息内容
WebSocket配置
有了处理器了,就可以将此处理器映射到指定path
上了,这需要增加一些配置,Spring
提供一个配置接口WebSocketConfigurer
,我们来实现它,并启用@EnableWebSocket
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
/**
* 用于将WebSocket处理程序映射到特定URL
*
* @param registry
*/
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(chatWebSocketHandler(), "chat") // 添加消息处理器
.setAllowedOrigins("*"); // 设置跨域
}
/**
* 自定义消息处理器
*
* @return
*/
@Bean
public ChatWebSocketHandler chatWebSocketHandler() {
return new ChatWebSocketHandler();
}
}
重写registerWebSocketHandlers
方法,通过registry.addHandler()
将消息处理器添加,并指定映射的path
,服务端WebSocket地址为 ws://host:port/path
注:需要注意的一点,这里要设置跨域,使用setAllowedOrigins
方法即可。
至此WebSocket的服务端就可以使用了,还有一些其他骚操作继续往下看
WebSocket握手拦截器
在建立连接前,我们可以通过握手拦截器来拦截非法请求,需要实现Spring
的HandshakeInterceptor
接口
import org.springframework.web.socket.server.HandshakeInterceptor;
public class ChatHandshakeInterceptor implements HandshakeInterceptor {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
log.info("--------------握手前拦截");
return true;
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {
log.info("--------------完成握手");
}
}
实现两个方法:
-
beforeHandshake
握手前,该方法返回true
表示继续建立连接,返回false
则终止 -
afterHandshake
握手后
WebSocketConfig
配置文件增加
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(chatWebSocketHandler(), "chat") // 添加消息处理器
.addInterceptors(chatHandshakeInterceptor()) // 添加握手拦截器
.setAllowedOrigins("*"); // 设置跨域
}
/**
* 自定义消息拦截器
*
* @return
*/
@Bean
public ChatHandshakeInterceptor chatHandshakeInterceptor() {
return new ChatHandshakeInterceptor();
}
Session空闲失效时间配置
一个会话连接后不可能一直不断开,这需要增加一些配置来约束超时时间;还可以设置消息缓冲区大小等(如果Nginx中配置了超时时间,此处可以忽略),上代码
WebSocketConfig
配置文件增加
/**
* 其他配置,如session空闲失效时间,消息缓冲区大小
*
* @return
*/
@Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxSessionIdleTimeout(10 * 60 * 1000L);
return container;
}
Nginx配置
大部分服务器都使用了Nginx代理,那么需要增加一些配置来支持WS协议
# 支持WS协议
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 会话超时时间
proxy_connect_timeout 620;
proxy_send_timeout 620;
proxy_read_timeout 620;
Client端
浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
创建WebSocket连接
连接协议使用ws
,path
为服务端消息处理器映射的path
var ws = new WebSocket("ws://127.0.0.1:8080/chat");
WebSocket事件与方法
if (typeof (WebSocket) == "undefined") {
alert("您的浏览器不支持WebSocket")
return
}
// 打开一个 web socket
var ws = new WebSocket("ws://127.0.0.1:8080/chat");
ws.onopen = function()
{
// Web Socket 已连接上,使用 send() 方法发送数据
ws.send("发送数据");
alert("数据发送中...");
};
ws.onmessage = function (evt)
{
var received_msg = evt.data;
alert("数据已接收...");
};
ws.onclose = function()
{
// 关闭 websocket
alert("连接已关闭...");
};
ws.onerror = function () {
// 连接错误
alert("连接错误...");
ws.close();
}
四个监听事件
-
onopen
与服务端连接成功 -
onmessage
接收服务端发送的消息 -
onclose
连接关闭 -
onerror
连接异常
两个方法
-
send()
向服务端发送消息 -
close()
关闭与服务端的连接
聊天Demo演示
本demo简单实现用户A向用户B发送消息,用户B也可以回复消息,具体内容请移步我的码云!
demo.png