WebSocket介绍

StarMoon2023/10/05WebSocketWebSocketTCP

一、什么是 WebSocket

1.1 WebSocket 诞生背景

早期,很多网站为了实现推送技术,所用的技术都是轮询。轮询是指由浏览器每隔一段时间向服务器发出 HTTP 请求,然后服务器返回最新的数据给客户端。常见的轮询方式分为轮询与长轮询,它们的区别如下图所示:

img

这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而 HTTP 请求与响应可能会包含较长的头部,其中真正有效的数据可能只是很小的一部分,所以这样会消耗很多带宽资源。

比较新的轮询技术是 Comet。这种技术虽然可以实现双向通信,但仍然需要反复发出请求。而且在 Comet 中普遍采用的 HTTP 长连接也会消耗服务器资源。

在这种情况下,HTML5 定义了 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。Websocket 使用 ws 或 wss 的统一资源标志符(URI),其中 wss 表示使用了 TLS 的 Websocket。如:

ws://echo.websocket.org
wss://echo.websocket.org

WebSocket 与 HTTP 和 HTTPS 使用相同的 TCP 端口,可以绕过大多数防火墙的限制。默认情况下,WebSocket 协议使用 80 端口;若运行在 TLS 之上时,默认使用 443 端口。

1.2 WebSocket 简介

WebSocket 是一种网络传输协议,可在单个 TCP 连接上进行全双工通信,位于 OSI 模型的应用层。WebSocket 协议在 2011 年由 IETF 标准化为 RFC 6455open in new window,后由 RFC 7936open in new window 补充规范。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。

介绍完轮询和 WebSocket 的相关内容之后,接下来我们来看一下 XHR Polling 与 WebSocket 之间的区别:

img

1.3 WebSocket 优点

  • 较少的控制开销。在连接创建后。服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。

  • 更强的实时性。由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。相对于 HTTP 请求需要等待客户端发起请求服务端才能响应,延迟明显更少。

  • 保持连接状态。与 HTTP 不同的是,WebSocket 需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。

  • 更好的二进制支持。WebSocket 定义了二进制帧,相对 HTTP,可以更轻松地处理二进制内容。

  • 可以支持扩展。WebSocket 定义了扩展,用户可以扩展协议、实现部分自定义的子协议。

由于 WebSocket 拥有上述的优点,所以它被广泛地应用在即时通信、实时音视频、在线教育和游戏等领域。

1.4 WebSocket 协议

本协议有两部分:握手和数据传输。

握手是基于http协议的。

来自客户端的握手看起来像如下形式:

GET ws://localhost/chat HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Version: 13

来自服务器的握手看起来像如下形式:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Extensions: permessage-deflate

字段说明:

头名称说明
Connection:Upgrade标识该HTTP请求是一个协议升级请求
Upgrade: WebSocket协议升级为WebSocket协议
Sec-WebSocket-Version: 13客户端支持WebSocket的版本
Sec-WebSocket-Key:客户端采用base64编码的24位随机字符序列,服务器接受客户端HTTP协议升级的证明。要求服务端响应一个对应加密的Sec-WebSocket-Accept头信息作为应答
Sec-WebSocket-Extensions协议扩展类型

二、客户端(浏览器)实现

2.1 WebSocket对象

实现 WebSockets 的 Web 浏览器将通过 WebSocket 对象公开所有必需的客户端功能(主要指支持 Html5 的浏览器)。

以下 API 用于创建 WebSocket 对象:

var ws = new WebSocket(url);

参数url格式说明: ws://ip地址:端口号/资源名称

2.2 WebSocket事件

WebSocket 对象的相关事件

事件事件处理程序描述
openwebsocket对象.onopen连接建立时触发
messagewebsocket对象.onmessage客户端接收服务端数据时触发
errorwebsocket对象.onerror通信发生错误时触发
closewebsocket对象.onclose连接关闭时触发

2.3 WebSocket方法

WebSocket 对象的相关方法:

方法描述
send()使用连接发送数据

三、服务端实现

Tomcat的7.0.5 版本开始支持WebSocket,并且实现了Java WebSocket规范(JSR356)。

Java WebSocket应用由一系列的WebSocketEndpoint组成。Endpoint 是一个java对象,代表WebSocket链接的一端,对于服务端,我们可以视为处理具体WebSocket消息的接口, 就像Servlet之与http请求一样。

我们可以通过两种方式定义Endpoint:

  • 第一种是编程式, 即继承类 javax.websocket.Endpoint并实现其方法。

  • 第二种是注解式, 即定义一个POJO, 并添加 @ServerEndpoint相关注解。

Endpoint实例在WebSocket握手时创建,并在客户端与服务端链接过程中有效,最后在链接关闭时结束。在Endpoint接口中明确定义了与其生命周期相关的方法, 规范实现者确保生命周期的各个阶段调用实例的相关方法。生命周期方法如下:

方法含义描述注解
onClose当会话关闭时调用。@OnClose
onOpen当开启一个新的会话时调用, 该方法是客户端与服务端握手成功后调用的方法。@OnOpen
onError当连接过程中异常时调用。@OnError

服务端如何接收客户端发送的数据呢?

通过为 Session 添加 MessageHandler 消息处理器来接收消息,当采用注解方式定义Endpoint时,我们还可以通过 @OnMessage 注解指定接收消息的方法。

服务端如何推送数据给客户端呢?

发送消息则由 RemoteEndpoint 完成, 其实例由 Session 维护, 根据使用情况, 我们可以通过Session.getBasicRemote 获取同步消息发送的实例 , 然后调用其 sendXxx()方法就可以发送消息, 可以通过Session.getAsyncRemote 获取异步消息发送实例。

服务端代码:

@ServerEndpoint("/robin")
public class ChatEndPoint {

    private static Set<ChatEndPoint> webSocketSet = new HashSet<>();

    private Session session;

    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        System.out.println("接收的消息是:" + message);
        System.out.println(session);
        //将消息发送给其他的用户
        for (Chat chat : webSocketSet) {
            if(chat != this) {
                chat.session.getBasicRemote().sendText(message);
            }
        }
    }

    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);
    }

    @OnClose
    public void onClose(Session seesion) {
        System.out.println("连接关闭了。。。");
    }

    @OnError
    public void onError(Session session,Throwable error) {
        System.out.println("出错了。。。。" + error.getMessage());
    }
}

四、常见问题

4.1 WebSocket 与 HTTP 有什么关系

WebSocket 是一种与 HTTP 不同的协议。两者都位于 OSI 模型的应用层,并且都依赖于传输层的 TCP 协议。 虽然它们不同,但是 RFC 6455 中规定:WebSocket 被设计为在 HTTP 80 和 443 端口上工作,并支持 HTTP 代理和中介,从而使其与 HTTP 协议兼容。 为了实现兼容性,WebSocket 握手使用 HTTP Upgrade 头,从 HTTP 协议更改为 WebSocket 协议。

OSI 模型示意图:

img

4.2 WebSocket 与长轮询有什么区别

长轮询就是客户端发起一个请求,服务器收到客户端发来的请求后,服务器端不会直接进行响应,而是先将这个请求挂起,然后判断请求的数据是否有更新。如果有更新,则进行响应,如果一直没有数据,则等待一定的时间后才返回。

长轮询的本质还是基于 HTTP 协议,它仍然是一个一问一答(请求 — 响应)的模式。而 WebSocket 在握手成功后,就是全双工的 TCP 通道,数据可以主动从服务端发送到客户端。

4.3 什么是 WebSocket 心跳

网络中的接收和发送数据都是使用 SOCKET 进行实现。但是如果此套接字已经断开,那发送数据和接收数据的时候就一定会有问题。可是如何判断这个套接字是否还可以使用呢?这个就需要在系统中创建心跳机制。所谓 “心跳” 就是定时发送一个自定义的结构体(心跳包或心跳帧),让对方知道自己 “在线”。 以确保链接的有效性。

而所谓的心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在而已。代码就是每隔几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息,如果服务端几分钟内没有收到客户端信息则视客户端断开。

在 WebSocket 协议中定义了 心跳 Ping心跳 Pong 的控制帧:

  • 心跳 Ping 帧包含的操作码是 0x9。如果收到了一个心跳 Ping 帧,那么终端必须发送一个心跳 Pong 帧作为回应,除非已经收到了一个关闭帧。否则终端应该尽快回复 Pong 帧。
  • 心跳 Pong 帧包含的操作码是 0xA。作为回应发送的 Pong 帧必须完整携带 Ping 帧中传递过来的 “应用数据” 字段。如果终端收到一个 Ping 帧但是没有发送 Pong 帧来回应之前的 Ping 帧,那么终端可以选择仅为最近处理的 Ping 帧发送 Pong 帧。此外,可以自动发送一个 Pong 帧,这用作单向心跳。

4.4 Socket 是什么

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个 socket(套接字),因此建立网络通信连接至少要一对端口号。socket 本质是对 TCP/IP 协议栈的封装,它提供了一个针对 TCP 或者 UDP 编程的接口,并不是另一种协议。通过 socket,你可以使用 TCP/IP 协议。

关于 Socket,可以总结以下几点:

  • 它可以实现底层通信,几乎所有的应用层都是通过 socket 进行通信的。
  • 对 TCP/IP 协议进行封装,便于应用层协议调用,属于二者之间的中间抽象层。
  • TCP/IP 协议族中,传输层存在两种通用协议: TCP、UDP,两种协议不同,因为不同参数的 socket 实现过程也不一样。

五、参考资源

阿宝哥open in new window

最后更新时间 2023/10/10 14:36:02