1 是什么

WebSocket是一种基于HTTP的长链接技术。传统的HTTP协议是一种请求-响应模型,如果浏览器不发送请求,那么服务器无法主动给浏览器推送数据。如果需要定期或者不定期给浏览器推送数据,只能依靠浏览器的JavaScript定时轮询,效率很低且实时性不高。

WebSocket在HTTP协议的基础上做了一个简单的升级,即建立TCP连接后,浏览器发送请求时,附带几个头,表示客户端希望升级连接,变成长连接的WebSocket。

GET /chat HTTP/1.1        # /chat服务器上处理WebSocket连接的端点(Endpoint)地址
Host: www.example.com     # 要访问的服务器主机名
Upgrade: websocket        # Upgrade 头字段表示客户端想要将当前连接升级到其他协议
Connection: Upgrade       # 把这个连接当做一次升级连接处理,不要按HTTP 请求关闭连接

服务器返回升级成功的响应

HTTP/1.1 101 Switching Protocols    # 同意切换协议
Upgrade: websocket                  # 确认和回应websokect协议
Connection: Upgrade                 # 不要按HTTP 请求关闭连接

2 Spring中怎么用

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

创建配置类

@Configuration
@ComponentScan
@EnableWebMvc
@EnableWebSocket // 启用WebSocket支持
public class AppConfig {
    @Bean
    WebSocketConfigurer createWebSocketConfigurer(
            @Autowired ChatHandler chatHandler,
            @Autowired ChatHandshakeInterceptor chatInterceptor){
        return new WebSocketConfigurer() {
            public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
                // 把URL与指定的WebSocketHandler关联,可关联多个:
                registry
                .addHandler(chatHandler, "/chat")
                .addInterceptors(chatInterceptor);
            }
        };
    }
}

当浏览器请求一个WebSocket连接后,如果成功建立连接,Spring会自动调用afterConnectionEstablished()方法,任何原因导致WebSocket连接中断时,Spring会自动调用afterConnectionClosed方法,

@Component
public class ChatHandler extends TextWebSocketHandler {
    // 保存所有Client的WebSocket会话实例:
    private Map<String, WebSocketSession> clients = new ConcurrentHashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // 新会话根据ID放入Map:
        clients.put(session.getId(), session);
        session.getAttributes().put("name", "Guest1");
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        clients.remove(session.getId());
    }
}

创建拦截器

@Component
public class ChatHandshakeInterceptor extends HttpSessionHandshakeInterceptor {
    public ChatHandshakeInterceptor() {
        // 指定从HttpSession复制属性到WebSocketSession:
        // 这样,在ChatHandler中,可以从WebSocketSession.getAttributes()中获取到复制过来的属性。
        super(List.of(UserController.KEY_USER));
    }
}