在 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.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.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(); } }
1.xml 方式
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:websocket="http://www.springframework.org/schema/websocket" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd"> <websocket:handlers> <websocket:mapping path="/myHandler" handler="myHandler"/> </websocket:handlers> <bean id="myHandler" class="org.springframework.samples.MyHandler"/> </beans>
更多配置细节可以参考:Spring WebSocket 文档
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>org.eclipse.jetty.websocket</groupId> <artifactId>javax-websocket-server-impl</artifactId> <version>${jetty-version}</version> </dependency> <!-- To run javax.websocket client --> <dependency> <groupId>org.eclipse.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) request.getHttpSession(); sec.getUserProperties().put(HttpSession.class.getName(), httpSession); } }
然后就没有然后了,就是这么简单。
WebSocket 代理
如果把 WebSocket 的通信看成是电话连接,Nginx 的角色则像是电话接线员,负责将发起电话连接的电话转接到指定的客服。
Nginx 从 1.3 版开始正式支持 WebSocket 代理。如果你的 web 应用使用了代理服务器 Nginx,那么你还需要为 Nginx 做一些配置,使得它开启 WebSocket 代理功能。
以下为参考配置:
server { # this section is specific to the WebSockets proxying location /socket.io { proxy_pass http://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; } }
更多配置细节可以参考:Nginx 官方的 websocket 文档
FAQ
HTTP 和 WebSocket 有什么关系?
Websocket 其实是一个新协议,跟 HTTP 协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是 HTTP 协议上的一种补充。
Html 和 HTTP 有什么关系?
Html 是超文本标记语言,是一种用于创建网页的标准标记语言。它是一种技术标准。Html5 是它的最新版本。
Http 是一种网络通信协议。其本身和 Html 没有直接关系。
完整示例
如果需要完整示例代码,可以参考我的 Github 代码:
Spring 对 WebSocket 支持的示例
嵌入式 Jetty 服务器的 WebSocket 示例
spring-websocket 和 jetty 9.3 版本似乎存在兼容性问题,Tomcat则木有问题。
我尝试了好几次,没有找到解决方案,只好使用 Jetty 官方的嵌入式示例在 Jetty 中使用 WebSocket 。
资料
知乎高票答案——WebSocket是什么原理 by Ovear
对 WebSocket 原理的阐述简单易懂。
WebSocket 教程 by ruanyf
阮一峰大神的科普一如既往的浅显易懂。
WebSockets by fullstackpython
Nginx 官方的 websocket 文档
Spring WebSocket 文档
Tomcat7 WebSocket 文档
Jetty WebSocket 文档