spring websocket

原文地址:https://www.cnblogs.com/jingmoxukong/p/7755643.html

概述

websocket是什么?

websocket是一种网络通信协议,RFC6455定义了它的通信标准。

websocket是html5开始提供的一种在单个tcp连接上进行全双工通讯的协议。

为什么需要websocket?

了解计算机网络协议的人,应该都知道:http协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。

这种通信模型有一个弊端:http协议无法实现服务器主动向客户端发起消息。

这种单向请求的特点,注定了如果服务器有连续的状态变化,客户端要获知就非常麻烦。大多数web应用程序将通过频繁的异步javascript和xml(ajax)请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者http连接始终打开)。

因此,工程师们一直在思考,有没有更好的方法。websocket就是这样发明的。websocket连接允许客户端和服务器之间进行全双工通信,以便任一方都可以通过建立的连接将数据推送到另一端。websocket只需要建立一次连接,就可以一直保持连接状态。这相比于轮询方式的不停建立连接显然效率要大大提高。

websocket如何工作?

web浏览器和服务器都必须实现websocket协议来建立和维护连接。由于websocket连接长期存在,与典型的http连接不同,对服务器有重要的影响。

基于多线程或多进程的服务器无法适用于websocket,因为它旨在打开连接,尽可能快的处理请求,然后关闭连接。任何实际的websocket服务器端实现都需要一个异步服务器。

websocket客户端

在客户端,没有必要为websocket使用javascript库。实现websocket的web浏览器将通过websocket对象公开所有必须的客户端功能(主要指支持html5的浏览器)。

客户端api

以下api用于创建websocket对象:

var socket = new WebSocket(url, [protocol]);

以上代码中的第一个参数url,指定连接的URL。第二个参数protocol是可选的,指定了可接受的子协议。

websocket属性

以下是websocket对象的属性,假定我们使用了以上代码创建了socket对象:

属性 描述
Socket.readyState 只读属性readyState表示连接状态,可以使用以下值:0 - 表示连接尚未建立;1 - 表示连接已建立,可以进行通信;2 - 表示连接正在进行关闭;3 - 表示连接已经关闭或者连接不能打开
Socket.bufferedAmount 只读属性bufferedAmount是指已被send()放入队列中等待传输但是还没有发出的UTF-8文本字节数

websocket事件

以下是websocket对象的相关事件,假定我们使用了以上代码创建了socket对象:

事件 事件处理程序 描述
open Socket.onopen 连接建立时触发
message Socket.onmessage 客户端接收服务端数据时触发
error Socket.onerror 通信发生错误时触发
close Socket.onclose 连接关闭时触发

websocket方法

以下是websocket对象的相关方法,假定我们使用了以上代码创建了socket对象:

方法 描述
Socket.send() 使用连接发送数据
Socket.close() 关闭连接

示例:

// 初始化一个websocket对象
var ws = new WebSocket('ws://localhost:9998/echo');

// 建立websocket连接成功触发事件
ws.onopen = function() {
    // 使用send()方法发送数据
    ws.send('发送数据');
    alert('数据发送中...');
};

// 接收服务端数据时触发事件
ws.onmessage = function(evt) {
    var received_msg = evt.data;
    alert('数据已经接收...');
};

// 断开websocket连接成功触发事件
ws.onclose = function() {
    alert('连接已关闭...');
};

websocket服务端

websocket在服务端的实现非常丰富,node.js、java、c++、python等多种语言都有自己的解决方案。

node.js

常用的node实现有以下三种:

java

java的web一般都依托于servlet容器。

servlet容器有:tomcat、jetty、resin。其中tomcat7、jetty7以及以上版本均开始支持websocket(推荐较新的版本,因为随着版本的更迭,对websocket的支持可能有变更)。

此外,spring框架对websocket也提供了支持。

虽然,以上应用对于websocket都有各自的实现。但是,它们都遵循RFC6455的通信标准,并且java api统一遵循JSR356-JavaTM API for WebSocket规范。所以,在实际编码中,API差异不大。

spring

spring对于websocket的支持基于下面的jar包:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket<artifactId>
    <version>${spring.version}</version>
</dependency>

在spring实现websocket服务器大概分为以下几步:

创建websocket处理器

扩展 TextWebSocketHandlerBinaryWebSocketHandler ,你可以覆盖指定的方法。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

配置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 WebSocket 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;
    }
}

FAQ

http和websocket有什么关系?

websocket其实是一个新协议,跟http协议基本没有关系,只是为了兼容现有浏览器的握手规范而已,也就是说它是http协议上的一种补充。

html和http有什么关系?

html是超文本标记语言,是一种用于创建网页的标准标记语言。它是一种技术标准。html5是它的最新版本。

http是一种网络通信协议,其本身和html没有直接关系。

posted @ 2020-04-24 22:16  aplsnb  阅读(209)  评论(0)    收藏  举报