WebSocket 是一种网络通讯协议,一个TCP连接的两端,可以同时给对方发送消息,即支持全双工通信。比较常见的应用场景就是服务端给客户端推送信息,相对于轮询,WebSocket减少了宽带资源浪费,实时性更强。
WebSocket利用了HTTP协议,建立连接之后,会先发一个特殊的HTTP请求过去,服务器发现这是个WebSocket的请求之后,就按照协议要求返回一个特殊的HTTP响应,告诉客户端“我知道你想用WebSocket了,我们按照它的协议来工作吧”。然后TCP链接就不会断开了,留着发消息。所以说WebSocket只利用你原来的80端口(ws://)和443端口(wss://)就可以工作,不需要再开个端口。
在Spring Boot中,你可以添加WebSocket Starter来引入WebSocket的支持:
在你的某个 @Configuration
类上,添加 @EnableWebSocket
注解开启功能:
开启了之后,还需要配置下,你要么自己提供个 WebSocketConfigurer
类型的Bean,要么让 @Configuration
配置类实现 WebSocketConfigurer
接口。
registerWebSocketHandlers
方法告诉框架,哪个路径是WebSocket的路径,哪个对象用来处理连接和收发信息。按照上图配置过之后,可以通过 ws://localhost:8080/api/websocket
来连接。上图还提供了一个 WSHandler
对象,它是用来处理连接和发送消息的:
WSHandler
实现了 WebSocketHandler
接口,在WebSocket连接建立或者断开、收到消息或者收到错误的时候这些方法会被调用。如果你感觉回调太多或者不好用,可以继承 AbstractWebSocketHandler
类,只实现自己想要的方法:
WebSocket可以发送文本消息或者二进制消息,如果你只关心文本消息的话,只需要保留 handleTextMessage
方法就行。好了,到这一步,你的WebSocket已经配置好了,可以使用了:
wsc 是一个简易的命令行WebSocket客户端,图中显示连接成功,已经可以发送数据或者接收数据了。通常来说,你的客户端可能是Android或者iOS应用,也很有可能是网页,我们在浏览器的console里试一试:
好像不行哎。图中提示为“Unexpected response code: 403”,403代表禁止访问。为啥禁止访问呢?明明服务器已经搭建好了呀。查看debug log,你会发现出问题的地方:Handshake request rejected, Origin header value https://www.google.com not allowed
在上一篇文章中,我们介绍了浏览器的同源策略和跨域访问的问题。Spring Boot的WebSocket支持也是需要配置跨域访问的,否则会返回403。还好配置很简单:
setAllowOrigins
还可以传入多个域(协议,域名,端口)。传入 *
表示允许所有域连接。通常你哪个网站需要连接,就传入哪个域,比如上面图片中的 “https://google.com”
。
既然两边可以收发消息了,那么你就自己定义消息格式就行了,比较简单的做法是使用文本消息,发送JSON对象。你也可以使用STOMP来作为两边的信息交换协议, Spring MVC官方文档 也作了介绍,我因为没有实践过,就不多说了。
再说个实际应用中的问题吧,很多公司可能使用nginx作为反向代理,将请求发送到实际的内网服务器上。但是nginx默认情况下不会转发 Connection
和 Upgrade
首部(Header),导致WebSocket无法正常工作, nginx官方文档
里已经说明了如何通过配置来解决。同时,你肯定会遇到连接60s后自动断开的问题,因为nginx默认60秒发现没有数据传送,就会关闭连接,你可以通过 proxy_read_timeout
指令把这一时间延长。