spring WebSocket详解

场景

websocket是Html5新增加特性之一,目的是浏览器与服务端建立全双工的通信方式,解决http请求-响应带来过多的资源消耗,同时对特殊场景应用提供了全新的实现方式,比如聊天、股票交易、游戏等对对实时性要求较高的行业领域。

背景

在浏览器中通过http仅能实现单向的通信,comet可以一定程度上模拟双向通信,但效率较低,并需要服务器有较好的支持; flash中的socket和xmlsocket可以实现真正的双向通信,通过 flex ajax bridge,可以在javascript中使用这两项功能. 可以预见,如果websocket一旦在浏览器中得到实现,将会替代上面两项技术,得到广泛的使用.面对这种状况,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽并达到实时通讯。目前各大主流浏览器都支持websocket,IE浏览器要IE10+

一、POM依赖

POM依赖,spring4.1.4.RELEASE,spring核心依赖请自行添加,下面是websocket相关jar

<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.0</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>4.1.4.RELEASE</version>
</dependency>

二、WebSocket入口

@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        //允许连接的域,只能以http或https开头
        String[] allowsOrigins = {"http://www.xxx.com"};
        
       //WebIM WebSocket通道
        registry.addHandler(chatWebSocketHandler(),"/           webSocketIMServer").setAllowedOrigins(allowsOrigins).addInterceptors(myInterceptor());
        registry.addHandler(chatWebSocketHandler(), "/sockjs/w          ebSocketIMServer").setAllowedOrigins(allowsOrigins).addInterceptors(myInterceptor()).withSockJS();
    }
    @Bean
    public ChatWebSocketHandler chatWebSocketHandler() {
        return new ChatWebSocketHandler();
    }
    @Bean
    public WebSocketHandshakeInterceptor myInterceptor(){
        return new WebSocketHandshakeInterceptor();
    }
}
  1. 实现WebSocketConfigurer接口,重写registerWebSocketHandlers方法,这是一个核心实现方法,配置websocket入口,允许访问的域、注册Handler、SockJs支持和拦截器。
  2. registry.addHandler注册和路由的功能,当客户端发起websocket连接,把/path交给对应的handler处理,而不实现具体的业务逻辑,可以理解为收集和任务分发中心。
  3. setAllowedOrigins(String[] domains),允许指定的域名或IP(含端口号)建立长连接,如果只允许自家域名访问,这里轻松设置。如果不限时使用"*"号,如果指定了域名,则必须要以http或https开头。
  4. addInterceptors,顾名思义就是为handler添加拦截器,可以在调用handler前后加入我们自己的逻辑代码。
  5. spring websocket也支持STOMP协议,下回再分享。

三、拦截器实现

public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {

    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object
                > attributes) throws Exception {
        if (request instanceof ServletServerHttpRequest) {
            attributes.put("username",userName);
        }
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {

    }
}

beforeHandshake,在调用handler前处理方法。常用在注册用户信息,绑定WebSocketSession,在handler里根据用户信息获取WebSocketSession发送消息。

四、Handler处理类

public class ChatWebSocketHandler extends TextWebSocketHandler{
    
    private final static List<WebSocketSession> sessions = Collections.synchronizedList(new ArrayList<WebSocketSession>());
    //接收文本消息,并发送出去
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        chatTextMessageHandler(message.getPayload());
        super.handleTextMessage(session, message);
    }
    //连接建立后处理
    @SuppressWarnings("unchecked")
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        logger.debug("connect to the websocket chat success......");
        sessions.add(session);
        //处理离线消息
    }
    //抛出异常时处理
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        if(session.isOpen()){
            session.close();
        }
        logger.debug("websocket chat connection closed......");
        sessions.remove(session);
    }
    //连接关闭后处理
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        logger.debug("websocket chat connection closed......");
        sessions.remove(session);
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }
}

五、客户端连接

var host = window.location.host;
var websocket;
if ('WebSocket' in window) {
    websocket = new ReconnectingWebSocket("ws://"
        + host + "/webSocketIMServer", null, {debug:true, maxReconnectAttempts:4});
} else if ('MozWebSocket' in window) {
    websocket = new MozWebSocket("ws://" + host
        + "/webSocketIMServer");
} else {
    websocket = new SockJS("http://" + host
            + "/sockjs/webSocketIMServer");
}
websocket.onopen = function(evnt) {
    console.log("websocket连接上");
};
websocket.onmessage = function(evnt) {
    messageHandler(evnt.data);
};
websocket.onerror = function(evnt) {
    console.log("websocket错误");
};
websocket.onclose = function(evnt) {
    console.log("websocket关闭");
}

这里用到了ReconnectingWebSocket.js,对浏览器自带websocket添加了扩展,例如重连,连接超时时间,失败重连间隔,尝试连接最大次数等。
项目主页:ReconnectingWebSocket

 

failed: Error during WebSocket handshake: Unexpected response code: 400

问题描述:在项目中引用Socket.Io,在项目部署后报错,本地运行不报错

错误原因:需要在配置文件nginx.conf中配置相关信息

解决方案:

       在nginx文件的location中添加

                            proxy_http_version 1.1;    
                            proxy_set_header Upgrade $http_upgrade;
                            proxy_set_header Connection "upgrade";

       第一行告诉Nginx在与Node后端通信时使用HTTP / 1.1,这是WebSockets所必需的。接下来的两行告诉Nginx响应升级请求,当浏览器想要使用WebSocket时,该请求由HTTP启动。这三行都是必须添加的。

例如:

server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  localhost;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
             proxy_pass  http://localhost:3100;
             proxy_http_version 1.1;
             proxy_set_header Upgrade $http_upgrade;
             proxy_set_header Connection "upgrade";
             proxy_set_header Host $host;
        }
}
 

<think>嗯,用户让我详细介绍一下Spring WebSocket和SockJS。首先,我需要回忆一下这两个技术的基本概念以及它们之间的关系。Spring WebSocketSpring框架对WebSocket协议的实现,而SockJS则是一个客户端库,用于在不支持WebSocket的环境下提供类似的功能。接下来,我应该先解释WebSocket的基本概念,为什么它被使用,然后过渡到Spring WebSocket的具体实现,包括相关的类和配置步骤。 然后,我需要考虑用户可能对WebSocket和传统HTTP协议的区别不太清楚,所以应该简单比较一下,比如全双工通信和持久连接的优势。另外,用户可能想知道在哪些场景下适合使用WebSocket,比如实时聊天、通知等。 接下来是SockJS的部分。这里要说明SockJS的作用,即作为WebSocket的降级方案,当浏览器不支持WebSocket时,自动切换到其他传输方式,比如长轮询。需要解释SockJS如何工作,以及它与Spring WebSocket的集成方式。 可能还需要提到STOMP协议,因为Spring WebSocket通常结合STOMP来简化消息传递模型。用户可能对STOMP不太了解,所以需要简要说明STOMP的作用,比如在WebSocket之上提供一个消息代理,支持订阅和发布模式。 在结构上,应该按照用户的要求分步骤讲解,先介绍WebSocketSpring的实现,再介绍SockJS,接着是整合使用,最后可能给出一个简单的示例代码,帮助用户理解配置过程。同时要注意使用中文,避免技术术语过于晦涩,必要时进行解释。 需要确保回答的结构清晰,每个部分有明确的标题,比如Spring WebSocket概述、SockJS的作用、整合步骤、优缺点和适用场景等。另外,按照系统级指令,数学公式要用正确的LaTeX格式,虽然在这个问题中可能用不到数学公式,但如果有涉及变量或代码的地方需要正确使用代码块。 还要检查是否有需要纠正的常见误解,比如WebSocket的兼容性问题,SockJS如何解决,以及Spring中的相关配置注意事项。比如,在配置拦截器时,如何注册STOMP端点,添加SockJS支持等。 最后,总结部分需要概括两者的优势,帮助用户决定在项目中是否采用这些技术。可能还需要提到性能考虑,比如SockJS的降级策略对性能的影响,以及如何根据目标用户的使用环境选择合适的方案。</think>### 详解 Spring WebSocket 与 SockJS #### 1. **WebSocket 基础** - **核心概念**: WebSocket 是一种基于 TCP 的双向通信协议,支持客户端与服务器持久连接,实现实时数据传输(如聊天、实时监控)。 **对比 HTTP**: - HTTP 是无状态、短连接;WebSocket 是全双工、长连接。 - WebSocket 减少重复握手开销,适合高频交互场景。 --- #### 2. **Spring WebSocket** Spring 提供了对 WebSocket 的抽象支持,核心组件如下: ##### 关键类与接口 - `WebSocketHandler`:定义消息处理方法(需实现接口或继承 `TextWebSocketHandler`)。 - `WebSocketSession`:表示一个 WebSocket 连接会话。 - `@EnableWebSocket`:启用 WebSocket 支持的注解。 - `WebSocketConfigurer`:配置 WebSocket 端点与处理器。 ##### 配置步骤 ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(myHandler(), "/ws-endpoint") .addInterceptors(new HttpSessionHandshakeInterceptor()); } @Bean public WebSocketHandler myHandler() { return new MyWebSocketHandler(); } } ``` --- #### 3. **SockJS 的作用** ##### 解决的问题 - 浏览器兼容性:旧浏览器或防火墙可能阻止 WebSocket,SockJS 提供降级方案。 - 传输方式:自动选择最佳协议(优先级:WebSocket > XHR Streaming > XHR Polling 等)。 ##### 工作原理 1. 客户端通过 SockJS 尝试建立 WebSocket 连接。 2. 若失败,自动切换至其他传输方式(如长轮询)。 3. 提供与 WebSocket 相同的 API,对上层透明。 --- #### 4. **Spring 整合 SockJS** ##### 配置调整 ```java registry.addHandler(myHandler(), "/ws-endpoint") .addInterceptors(new HttpSessionHandshakeInterceptor()) .withSockJS(); // 启用 SockJS 支持 ``` ##### 客户端示例(JavaScript) ```javascript const socket = new SockJS('/ws-endpoint'); const stompClient = Stomp.over(socket); stompClient.connect({}, (frame) => { stompClient.subscribe('/topic/messages', (response) => { console.log(response.body); }); }); ``` --- #### 5. **STOMP 协议** - **定位**:在 WebSocket 之上定义消息格式,简化订阅-发布模型。 - **Spring 支持**: - `@MessageMapping`:处理客户端发送的消息。 - `@SendTo`:定义消息转发目标。 - 消息代理(如 RabbitMQ、ActiveMQ)支持集群扩展。 ##### 配置 STOMP ```java @Configuration @EnableWebSocketMessageBroker public class StompConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/stomp-endpoint").withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } } ``` --- #### 6. **优缺点与适用场景** | **优势** | **局限性** | |-----------------------------------|-------------------------------| | 实时性高,减少延迟 | 需要维护长连接,资源消耗较大 | | 支持多种传输方式(SockJS) | 复杂场景需结合消息队列 | | 与 Spring 生态无缝集成 | 需处理断线重连逻辑 | **适用场景**:实时聊天、股票行情推送、协同编辑、游戏同步等。 --- #### 7. **常见问题** - **跨域问题**:需配置 `setAllowedOrigins("*")`(生产环境应限制域名)。 - **心跳机制**:SockJS 默认发送心跳包维持连接。 - **安全性**:结合 Spring Security 限制端点访问权限。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值