Spring-WebSocket教程
WebSocket 教程
概述
WebSocket 是什么?
是⼀种⽹络通信协议。定义了它的通信标准。
WebSocket 是 HTML5 开始提供的⼀种在单个 TCP 连接上进⾏全双⼯通讯的协议。
为什么需要 WebSocket ?
了解计算机⽹络协议的⼈,应该都知道:HTTP 协议是⼀种⽆状态的、⽆连接的、单向的应⽤层协议。它采⽤了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。
这种通信模型有⼀个弊端:HTTP 协议⽆法实现服务器主动向客户端发起消息。
这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就⾮常⿇烦。⼤多数 Web 应⽤程序将通过频繁的异步JavaScript和XML(AJAX)请求实现长轮询。轮询的效率低,⾮常浪费资源(因
为必须不停连接,或者 HTTP 连接始终打开)。
ajax-long-polling.png
因此,⼯程师们⼀直在思考,有没有更好的⽅法。WebSocket 就是这样发明的。WebSocket 连接允许客户端和服务器之间进⾏全双⼯通信,以便任⼀⽅都可以通过建⽴的连接将数据推送到另⼀端。WebSocket 只需要建⽴⼀次连接,就可以⼀直保持连接状态。这相⽐于轮询⽅式的不停建⽴连接显然效率要⼤⼤提⾼。
websockets-flow.png
WebSocket 如何⼯作?
Web浏览器和服务器都必须实现 WebSockets 协议来建⽴和维护连接。由于 WebSockets 连接长期存在,与典型的HTTP连接不同,对服务器有重要的影响。
基于多线程或多进程的服务器⽆法适⽤于 WebSockets,因为它旨在打开连接,尽可能快地处理请求,然后关闭连接。任何实际的 WebSockets 服务器端实现都需要⼀个异步服务器。
WebSocket 客户端
在客户端,没有必要为 WebSockets 使⽤ JavaScript 库。实现 WebSockets 的 Web 浏览器将通过 WebSockets 对象公开所有必需的客户端功能(主要指⽀持 Html5 的浏览器)。
客户端 API
以下 API ⽤于创建 WebSocket 对象。
var Socket = new WebSocket(url, [protocol] );
以上代码中的第⼀个参数 url, 指定连接的 URL。第⼆个参数 protocol 是可选的,指定了可接受的⼦协议。
WebSocket 属性
以下是 WebSocket 对象的属性。假定我们使⽤了以上代码创建了 Socket 对象:
属性描述
Socket.bufferedAmount只读属性 bufferedAmount 已被 send() 放⼊正在队列中等待传输,但是还没有发出的 UTF-8 ⽂本字节数。
WebSocket 事件
以下是 WebSocket 对象的相关事件。假定我们使⽤了以上代码创建了 Socket 对象:
事件事件处理程序描述
pen连接建⽴时触发
ssage客户端接收服务端数据时触发
r通信发⽣错误时触发
lose连接关闭时触发
WebSocket ⽅法
以下是 WebSocket 对象的相关⽅法。假定我们使⽤了以上代码创建了 Socket 对象:
⽅法描述
Socket.send()使⽤连接发送数据
Socket.close()关闭连接
/
/ 初始化⼀个 WebSocket 对象
var ws = new WebSocket("ws://localhost:9998/echo");
// 建⽴ web socket 连接成功触发事件
// 使⽤ send() ⽅法发送数据
ws.send("发送数据");
alert("数据发送中...");
};
// 接收服务端数据时触发事件
var received_msg = evt.data;
alert("数据已接收...");
};
// 断开 web socket 连接成功触发事件
alert("连接已关闭...");
};
WebSocket 服务端
WebSocket 在服务端的实现⾮常丰富。Node.js、Java、C++、Python 等多种语⾔都有⾃⼰的解决⽅案。
以下,介绍我在学习 WebSocket 过程中接触过的 WebSocket 服务端解决⽅案。
Node.js
常⽤的 Node 实现有以下三种。
Java
Java 的 web ⼀般都依托于 servlet 容器。
我使⽤过的 servlet 容器有:Tomcat、Jetty、Resin。其中Tomcat7、Jetty7及以上版本均开始⽀持 WebSocket(推荐较新的版本,因为随着版本的更迭,对 WebSocket 的⽀持可能有变更)。
此外,Spring 框架对 WebSocket 也提供了⽀持。
虽然,以上应⽤对于 WebSocket 都有各⾃的实现。但是,它们都遵循的通信标准,并且 Java API 统⼀遵循规范。所以,在实际编码中,API 差异不⼤。
Spring
Spring 对于 WebSocket 的⽀持基于下⾯的 jar 包:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
在 Spring 实现 WebSocket 服务器⼤概分为以下⼏步:
创建 WebSocket 处理器
扩展TextWebSocketHandler或BinaryWebSocketHandler,你可以覆写指定的⽅法。Spring 在收到 WebSocket 事件时,会⾃动调⽤事件对应的⽅法。
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.TextMessage;
public class MyHandler extends TextWebSocketHandler {
@Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
// ...
}
}
WebSocketHandler源码如下,这意味着你的处理器⼤概可以处理哪些 WebSocket 事件:
public interface WebSocketHandler {
/**
* 建⽴连接后触发的回调
*/
void afterConnectionEstablished(WebSocketSession session) throws Exception;
/**
* 收到消息时触发的回调
*/
void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception;
/**
* 传输消息出错时触发的回调
*/
void handleTransportError(WebSocketSession session, Throwable exception) throws Exception;
* 断开连接后触发的回调
*/
void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception;  /**
* 是否处理分⽚消息
*/
boolean supportsPartialMessages();
}
配置 WebSocket
配置有两种⽅式:注解和 xml 。其作⽤就是将 WebSocket 处理器添加到注册中⼼。1. 实现WebSocketConfigurer
import org.springframework.fig.annotation.EnableWebSocket;
import org.springframework.fig.annotation.WebSocketConfigurer;
import org.springframework.fig.annotation.WebSocketHandlerRegistry;
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/myHandler");
}
@Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
2. xml ⽅式
<beans xmlns="/schema/beans"
xmlns:xsi="/2001/XMLSchema-instance"
xmlns:websocket="/schema/websocket"
xsi:schemaLocation="
/schema/beans
/schema/beans/spring-beans.xsd
/schema/websocket
/schema/websocket/spring-websocket.xsd">
<websocket:handlers>
<websocket:mapping path="/myHandler" handler="myHandler"/>
</websocket:handlers>
<bean id="myHandler" class="org.springframework.samples.MyHandler"/>
</beans>
更多配置细节可以参考:
javax.websocket
如果不想使⽤ Spring 框架的 WebSocket API,你也可以选择基本的 javax.websocket。⾸先,需要引⼊ API jar 包。
<!-- To write basic javax.websocket against -->
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.0</version>
</dependency>
如果使⽤嵌⼊式 jetty,你还需要引⼊它的实现包:
<!-- To run javax.websocket in embedded server -->
<dependency>
<groupId&lipse.jetty.websocket</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
websocket和socket
<version>${jetty-version}</version>
</dependency>
<!-- To run javax.websocket client -->
<dependency>
<groupId&lipse.jetty.websocket</groupId>
<artifactId>javax-websocket-client-impl</artifactId>
<version>${jetty-version}</version>
</dependency>
@ServerEndpoint
这个注解⽤来标记⼀个类是 WebSocket 的处理器。
然后,你可以在这个类中使⽤下⾯的注解来表明所修饰的⽅法是触发事件的回调
// 收到消息触发事件
@OnMessage
public void onMessage(String message, Session session) throws IOException, InterruptedException {
...
}
// 打开连接触发事件
@OnOpen
public void onOpen(Session session, EndpointConfig config, @PathParam("id") String id) {
...
}
// 关闭连接触发事件
@OnClose
public void onClose(Session session, CloseReason closeReason) {
...
}
// 传输消息错误触发事件
@OnError
public void onError(Throwable error) {
.
..
}
ServerEndpointConfig.Configurator
编写完处理器,你需要扩展 ServerEndpointConfig.Configurator 类完成配置:
public class WebSocketServerConfigurator extends ServerEndpointConfig.Configurator {
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
HttpSession httpSession = (HttpSession) HttpSession();
}
}
然后就没有然后了,就是这么简单。
WebSocket 代理
如果把 WebSocket 的通信看成是电话连接,Nginx 的⾓⾊则像是电话接线员,负责将发起电话连接的电话转接到指定的客服。
Nginx 从开始正式⽀持 WebSocket 代理。如果你的 web 应⽤使⽤了代理服务器 Nginx,那么你还需要为 Nginx 做⼀些配置,使得它开启 WebSocket 代理功能。以下为参考配置:
server {
# this section is specific to the WebSockets proxying
location /socket.io {
proxy_pass app_server_wsgiapp/socket.io;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 600;
}
}
更多配置细节可以参考:
FAQ
HTTP 和 WebSocket 有什么关系?
Websocket 其实是⼀个新协议,跟 HTTP 协议基本没有关系,只是为了兼容现有浏览器的握⼿规范⽽已,也就是说它是 HTTP 协议上的⼀种补充。
Html 和 HTTP 有什么关系?
Html 是超⽂本标记语⾔,是⼀种⽤于创建⽹页的标准标记语⾔。它是⼀种技术标准。Html5 是它的最新版本。
Http 是⼀种⽹络通信协议。其本⾝和 Html 没有直接关系。