WebSocket学习


=Start=

缘由:

之前只是简单听说过WebSocket这个名词,但是一直都不清楚它到底是个什么东西,今天刚好在看某个需求文档的时候里面要求使用WebSocket实现双向通信,所以就想着好好看一下相关概念,方便以后的进一步学习。

正文:

参考解答:
HTTP短轮询 & HTTP长轮询 & WebSocket和HTTP的不同

由于历史原因,在创建一个具有双向通信机制的 Web 应用程序时,需要利用到 HTTP 轮询的方式。围绕轮询产生了 “短轮询” 和 “长轮询”。

HTTP短轮询

浏览器赋予了脚本网络通信的编程接口 XMLHttpRequest,以及定时器接口 setTimeout。因此,客户端脚本可以每隔一段时间就主动的向服务器发起请求,询问是否有新的信息产生:

  1. 客户端向服务器发起一个请求,询问 “有新信息了吗”;
  2. 服务端接收到客户端的请求,但是此时没有新的信息产生,于是直接回复 “没有”,并关闭链接;
  3. 客户端知道了没有新的信息产生,那么就暂时什么都不做;
  4. 间隔 5 秒钟之后,再次从步骤 1 开始循环执行;

HTTP长轮询

使用短轮询的方式有一个缺点,由于客户端并不知道服务器端何时会产生新的消息,因此它只有每隔一段时间不停的向服务器询问 “有新信息了吗”。而长轮询的工作方式可以是这样:

  1. 客户端向服务器发起一个请求,询问 “有新信息了吗”;
  2. 服务器接收到客户端的请求,此时并没有新的信息产生,不过服务器保持这个链接,像是告诉客户端 “稍等”。于是直到有了新的信息产生,服务端将新的信息返回给客户端;
  3. 客户端接收到消息之后显示出来,并再次由步骤 1 开始循环执行;

可以看到 “长轮询” 相较于 “短轮询” 可以减少大量无用的请求,并且客户端接收到新消息的时机将会有可能提前。

Ajax & Comet

WebSocket

WebSocket是HTML5下一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的。它与HTTP一样通过已建立的TCP连接来传输数据,但是它和HTTP最大不同是:

  • WebSocket是一种双向通信协议。在建立连接后,WebSocket服务器端和客户端都能主动向对方发送或接收数据,就像Socket一样;
  • WebSocket需要像TCP一样,先建立连接,连接成功后才能相互通信。

相比HTTP长连接,WebSocket有以下特点:

  • 是真正的全双工方式,建立连接后客户端与服务器端是完全平等的,可以互相主动请求。而HTTP长连接基于HTTP,是传统的客户端对服务器发起请求的模式。
  • HTTP长连接中,每次数据交换除了真正的数据部分外,服务器和客户端还要大量交换HTTP header,信息交换效率很低。Websocket协议通过第一个request建立了TCP连接之后,之后交换的数据都不需要发送 HTTP header就能交换数据,这显然和原有的HTTP协议有区别所以它需要对服务器和客户端都进行升级才能实现(主流浏览器都已支持HTML5)。此外还有 multiplexing、不同的URL可以复用同一个WebSocket连接等功能。这些都是HTTP长连接不能做到的。
协议概览

协议分为两部分:“握手” 和 “数据传输”。

客户端发出的握手信息类似:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

服务端回应的握手信息类式:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat

客户端的握手请求由 请求行(Request-Line) 开始。客户端的回应由 状态行(Status-Line) 开始。请求行和状态行的生产式见 RFC2616。

开始握手

握手部分的设计目的就是兼容现有的基于 HTTP 的服务端组件(web 服务器软件)或者中间件(代理服务器软件)。这样一个端口就可以同时接受普通的 HTTP 请求或则 WebSocket 请求了。为了这个目的,WebSocket 客户端的握手是一个 HTTP 升级版的请求(HTTP Upgrade request):

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

为了遵循协议 RFC2616,握手中的头字段是没有顺序要求的。

关闭握手

关闭握手的操作也很简单。

任意一端都可以选择关闭握手过程。需要关闭握手的一方通过发送一个特定的控制序列去开始一个关闭握手的过程。一端一旦接受到了来自另一端的请求关闭控制帧后,接收到关闭请求的一端如果还没有返回一个作为响应的关闭帧的话,那么它需要先发送一个关闭帧。在接受到了对方响应的关闭帧之后,发起关闭请求的那一端就可以关闭连接了。

在发送了请求关闭控制序列之后,发送请求的一端将不可以再发送其他的数据内容;同样的,一但接收到了一端的请求关闭控制序列之后,来自那一端的其他数据内容将被忽略。注意这里的说的是数据内容,控制帧还是可以响应的。否则就下面一句就没有意义了。

两边同时发起关闭请求也是可以的。

之所以需要这样做,是因为客户端和服务器之间可能还存在其他的中间件。一端关闭之后,也需要通知另一端也和中间件断开连接。

WebSocket的设计理念

WebSocket 协议的设计理念就是提供极小的帧结构(帧结构存在的目的就是使得协议是基于帧的,而不是基于流的,同时帧可以区分 Unicode 文本和二进制的数据)。它期望可以在应用层中使得元数据可以被放置到 WebSocket 层上,也就是说,给应用层提供一个将数据直接放在 TCP 层上的机会,再简单的说就可以给浏览器脚本提供一个使用受限的 Raw TCP 的机会。

从概念上来说,WebSocket 只是一个建立于 TCP 之上的层,它提供了下面的功能:

  1. 给浏览器提供了一个基于源的安全模型(origin-based security model)
  2. 给协议提供了一个选址的机制,使得在同一个端口上可以创立多个服务,并且将多个域名关联到同一个 IP
  3. 在 TCP 层之上提供了一个类似 TCP 中的帧的机制,但是没有长度的限制
  4. 提供了关闭握手的方式,以适应存在中间件的情况

从概念上将,就只有上述的几个用处。不过 WebSocket 可以很好的和 HTTP 协议一同协作,并且可以充分的利用现有的 Web 基础设施,比如代理。WebSocket 的目的就是让简单的事情变得更加的简单。

WebSocket 和 TCP 以及 HTTP 之间的关系

WebSocket 是一个独立的基于 TCP 的协议,它与 HTTP 之间的唯一关系就是它的握手请求可以作为一个升级请求(Upgrade request)经由 HTTP 服务器解释(也就是可以使用 Nginx 反向代理一个 WebSocket)。

默认情况下,WebSocket 协议使用 80 端口作为一般请求的端口,端口 443 作为基于传输加密层连(TLS)RFC2818 接的端口。

参考链接:

WebSocket 是什么原理?为什么可以实现持久连接?
https://www.zhihu.com/question/20215561

WebSocket 协议 1~4 节
http://www.jianshu.com/p/867274a5e054

WebSocket
https://zh.wikipedia.org/wiki/WebSocket

Websocket协议的学习、调研和实现
http://www.cnblogs.com/lizhenghn/p/5155933.html
http://cpper.info/2016/01/24/websocket.html

学习WebSocket协议—从顶层到底层的实现原理(修订版) #22
https://github.com/abbshr/abbshr.github.io/issues/22

WebSocket原理说明
https://www.qcloud.com/document/product/214/4150

WebSocket 实战
https://www.ibm.com/developerworks/cn/java/j-lo-WebSocket/

WebSocket
http://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001472780997905c8f293615c5a42eab058b6dc29936a5c000

Websocket 协议解析
http://www.king-liu.net/?p=772

=END=


《 “WebSocket学习” 》 有 17 条评论

  1. Web 实时推送技术的总结
    https://segmentfault.com/a/1190000018496938
    `
    前言

    一、双向通信
    1.轮询(polling)
    2.长轮询(long-polling)
    3.iframe流(streaming)

    二、WebSocket
    1.什么是websocket
    2.HTTP的局限性
    3.WebSocket的特点

    三、Web 实时推送技术的比较

    参考文章
    `

  2. 【严选-高质量文章】开发者必知必会的 WebSocket 协议
    https://juejin.im/post/5d4cbc0cf265da038f47fa37
    `
    文章介绍
      开始
      WebSocket 协议的来源
      WebSocket 的优点
      WebSocket 协议规范
        双端交互流程
        保持连接和关闭连接
    插个广告
      实际代码解读-Python
        客户端握手
        数据转换为数据帧
        数据帧转换为数据
    总结
    `

    Python如何爬取实时变化的WebSocket数据
    https://juejin.im/post/5c80b768f265da2dae514d4f

  3. 利用 WebSocket 判断是否使用了代理
    https://blog.xlab.app/p/f7c5c068/
    `
    1. 前情提要
    2. 检测原理
    3. 尝试
    4. 服务端
    5. 参考
    `
    检测浏览器是否存在代理
    https://github.com/ttttmr/checkproxy
    `
    通过 Websocket 的 bufferedAmount 来探测用户是否采用来代理

    1. Client 通过 Websocket 与 Server 建立连接
    2. Server 监听到 connect 事件后,将本次 TCP 的 window size 设置为 0,这也就意味着 Client 无法继续将数据包传给 server
    3. Client 使用 websocket.send () 持续发送几个包
    4. 在 Client 上观察 websocket.bufferedAmount 值,如果过了一会,这个值一直在增大,说明无代理,否则存在代理

    为啥可以通过这个值来判断呢?这是因为代理工具一般不会转发 TCP 的设置,也就是说,开启了代理的 Client 发出的包会被代理给吃掉
    `

  4. Command-line client for WebSockets
    https://github.com/vi/websocat
    `
    $ websocat ws://ws.vi-server.org/mirror
    123
    123
    ABC
    ABC

    # Serve and connect
    A$ websocat -s 1234
    Listening on ws://127.0.0.1:1234/
    ABC
    123

    B$ websocat ws://127.0.0.1:1234/
    ABC
    123
    `
    https://stackoverflow.com/questions/40907888/linux-bash-how-to-open-a-websocket-connection-as-client

    websocket-client is a WebSocket client for Python. It provides access to low level APIs for WebSockets.
    https://pypi.org/project/websocket-client/

    Python websocket之 websocket-client 库的使用
    https://blog.csdn.net/tz_zs/article/details/119363470

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注