websocket
简述
大四狗,正在外边实习,前一段时间部门轮流做技术分享,轮到我了。恰好有一个项目要加功能,大概需求是任务发布者A向另一个人B发布任务。
- 如果B在线,则应该及时收到新任务提醒;
- 如果B不在,则在登陆时应显示新任务提醒。所有的提示均为系统级弹窗。
大概的要求就是这些,第二点很简单,登陆后扫一遍就OK。第一个就有些问题了,在不刷新页面的情况下获得通知,在以前都是后台使用ajax轮询的,对服务器的压力比较大。boss说用WebSocket搞一下。我在9月中旬看了两天的websocket,后来就放下了,这几天又捡了捡。下面将WebSocket和轮询作比较,从为什么用WebSocket,WebSocket工作原理,基于Spring MVC的WebSocket搭建三个方面说一下。
正题
为什么用WebSocket
几种解决方案
轮询
轮询分为长轮询和短轮询。
- 短轮询指的是浏览器端每隔一定时间向服务器发出ajax请求,查询状态有没有变化。服务器直接返回结果。
- 长轮询指的是浏览器发出ajax请求查询状态,如果状态没有变化【没有新消息】,服务器不立刻返回,而是保持连接直到超时之前,在这一段时间如果有新消息则立刻返回,如果没有新消息,则在超时之前返回一个没有新消息的response。浏览器端在每接收到一个回复后都立刻发起下一次ajax查询,以此来完成监听。
短轮询过程图
1.png长轮询过程图
2.png长连接
- 长连接是和短连接对应的。
- 长连接指的是在一次TCP连接中传输多个数据包,在连接保持期间,如果没有数据包发送,需要双方发送链路检测包【又叫心跳包】
- 短连接指的是当双方有数据交互时,就建立连接,数据发送完成后马上断开连接。也就是说每次连接只完成一项业务
通俗的来说,长连接的连接是要保持一段时间的,短连接用完就丢。长短连接和上边的长短轮询是不一样的:
长短轮询指的是服务器端是否及时返回浏览器端消息。长短连接指的是建立的TCP连接是否保持。轮询是长还是短主要取决于服务器端的处理策略;连接是长是短取决于浏览器和服务器双方,这个要看通讯协议的。
举个栗子,拿HTTP1.0和HTTP1.1来说吧,在HTTP1.1中新加了一个keep-alive字段。在传统的HTTP1.0通讯协议中,用户发起HTTP请求时会经历一下过程:
- 用户和服务器三次握手,建立TCP连接。
- 用户发送HTTP请求request
- 服务器返回响应response
- 用户和服务器四次握手,断开TCP连接。
HTTP1.0时序图
3.png
在HTTP1.1中,有一个keep-alive,默认情况下,采用是长连接,我们不妨假设HTTP1.1的keep-alive设置的时间是m,对应的TCP也有一个keep-alive,设置的时间是n。此时用户发起HTTP请求的过程就变成了下面这样:
- 用户和服务器三次握手,建立TCP连接。
- 用户发送HTTP请求request。
- 服务器返回响应response。
- 从此开始计时,m时间内如果没有HTTP请求则四次握手断开TCP连接。如果之前已经有到计时了,则刷新计时为m
- 用户有新的HTTP请求,直接跳到第2步。
在2,3,4,5过程中,连接如果长时间没有发送或者接收消息,服务器和浏览器为了确定互相都在线,就在TCP中约定了一个keep-alive字段。上边设置keep-alive为n,也就是说当TCP连接在n时间内没有通讯的话,就用心跳包来保活。
HTTP1.1时序图
4.png
WebSocket工作原理
其实上边已经解释的差不多了,HTTP1.1虽然有keep-alive字段,但是长连接只是保持了TCP不立刻断开,在短时间内多次请求的情况下是TCP连接可以复用。但是HTTP通讯协议服务器只能被动接受,还是不能主动发送消息。
于是在H5规范中就提出来一个新的通讯协议:WebSocket协议。WebSocket是一个基于TCP连接的全双工的通讯协议,在2011年被列入标准。有人把WebSocker当作HTTP协议长连接的“大补丁”。
WebSocket,HTTP,TCP的关系
5.png图中WebSocket和HTTP之间有一些交互,这是因为WebSocket是通过HTTP协议进行一次握手。握手成功后就可以正常通讯了。
这里还有一个问题,也是学习了WebSocket后大家都经常问你的问题:
WebSocket和Socket的区别是什么?
网络分为七层:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。
IP是工作在网络层的,通过IP可以唯一确定网络中的一台主机,TCP/UDP工作在传输层,通过协议和端口号可以唯一确定主机中的一个进程。所以有了TCP/IP协议进行不同主机的进程间的通讯。但是TCP/IP协议很复杂,所有有了socket,把下层的复杂的TCP/IP协议屏蔽掉,向上层提供简单的接口。
WebSocket工作在应用层,是一个完整的通讯协议。
两者没有任何关系。
基于Spring MVC的WebSocket搭建
创建WebSocket处理类
Spring在org.springframework.web.socket
包里提供了WebSocketHandeler
接口。
并在org.springframework.web.socket.handler
包里提供了实现了此接口的可以继承的类:
TextWebSocketHandler
// ----------文本收发,可用来传信息
BinaryWebSocketHandler
// ----------二进制文件收发,可用作上传下载
AbstractWebSocketHandler
// ----------前两者的父类,实现了WebSocketHandeler接口,并列出了前两者的所有方法
//WebSocketHandeler接口
package org.springframework.web.socket;
public interface WebSocketHandler {
void afterConnectionEstablished(WebSocketSession var1) throws Exception;
void handleMessage(WebSocketSession var1, WebSocketMessage<?> var2) throws Exception;
void handleTransportError(WebSocketSession var1, Throwable var2) throws Exception;
void afterConnectionClosed(WebSocketSession var1, CloseStatus var2) throws Exception;
boolean supportsPartialMessages();
}
创建握手接口
Spring在org.springframework.web.socket
包里提供了HandshakeInterceptor
接口。
并在org.springframework.web.socket.server.support
包里提供了实现了此接口的可以继承的类:
<pre>HttpSessionHandshakeInterceptor
// ----------通过http握手时使用,
可以用来查验httpSession中的登陆数据
OriginHandshakeInterceptor
// ----------面向可访问来源的筛选</pre>
下面是HandshakeInterceptor的接口
<pre>public interface HandshakeInterceptor {
boolean beforeHandshake(ServerHttpRequest var1, ServerHttpResponse var2, WebSocketHandler var3, Map<String, Object> var4) throws Exception;
void afterHandshake(ServerHttpRequest var1, ServerHttpResponse var2, WebSocketHandler var3, Exception var4);}</pre>
处理类和握手协议的配置
Spring在org.springframework.web.socket.config.annotation
包里提供了WebSocketConfigurer
接口。实现这个接口并实现registerWebSocketHandlers(WebSocketHandlerRegistrywebSocketHandlerRegistry)
方法,配置websocket入口,允许访问的域、注册Handler、SockJs支持和拦截器。
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry
.addHandler(handler,"/aWebSocket")//添加一个处理器还有定义处理器的处理路径
.addInterceptors(handlerShakerInceptor)//添加一个过滤器
.setAllowedOrigins("*");
}
附录里有完整的项目代码。给整个项目我觉得比在这里粘代码要好得多,就不粘了。