WebSocket

WebSocket

WebSocket 简介

WebSocket是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得CS之间的数据交换变得更加简单,允许 S 主动向 C 推送数据。在WebSocket API中,C 和 S 只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

WebSocket 产生背景

现在上网经常用到的是HTTPHTTPS协议,HTTP 协议和 HTTPS 协议通信过程都是 C 通过 S 发出一个请求,S 接受请求后进行处理并返回结果给 C,C 处理结果。

这种机制对于信息变化不是特别频繁的应用可以良好支撑,但对于实时要求高、海量并发的应用来说显得捉襟见肘,尤其在移动互联网蓬勃发展的趋势下,高并发与用户实时响应是 Web 应用经常面临的问题,比如金融证券的实时信息、社交网络的实时消息推送等。WebSocket 出现前实现推送技术,用的都是Ajax轮询,在特定的时间间隔,C 自动发出请求,将 S 的消息主动的拉回来,这种情况下需要不断的向 S 发送请求,并且 HTTP 请求的 header 非常长,里面包含的数据可能只是一个很小的值,这样会占用很多的带宽和服务器资源,并且 S 不能主动向 C 推送数据。在这种情况下需要一种高效节能的双向通信机制来保证数据的实时传输,于是基于 HTML5 规范的 WebSocket 应运而生。

WebSocket 与 Socket
  • WebSocket:本质仍是一个基于 TCP 的应用层协议,为传统的 HTTP 补充了全双工通信的功能。
  • Socket:套接字,是一个抽象层,位于应用层和传输层之间,应用层可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将 I/O 插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是 IP 地址与端口的组合。
WebSocket 与 HTTP(S)
  • WebSocket 与 HTTP 协议一样都是基于 TCP 的,所以都是可靠的协议,调用的 WebSocket 的 send 函数在实现中最终都是通过 TCP 的系统接口进行传输。
  • WebSocket 和 HTTP 协议一样都属于应用层的协议,WebSocket 在建立握手连接时,数据是通过 HTTP 协议传输的,但是在建立连接之后,真正的数据传输阶段是不需要 HTTP 协议参与的。
Websocket 与 HTTP 轮询
HTTP 轮询

HTTP 实现实时推送用到的轮询,轮询分两种:短轮询(HTTP1.0)和长轮询(HTTP1.1)。但是无论是长轮询还是短轮询,浏览器都要先发起对服务器的连接,才能接收数据,并且实时交互性很低。

  • 短轮询:C 定时向 S 发送请求,S 收到请求不管是否有数据到达都直接响应请求,隔特定时间,C 又会发送相同的请求到 S, 获取数据响应。
    • 缺点:数据交互的实时性较低,服务端到浏览器端的数据反馈效率低。
  • 长轮询:C 发起请求到 S,S 一直保持连接打开,直到有数据可发送。发送完数据之后,C 关闭连接,随即又发起一个到 S 的新请求,这一过程在页面打开期间一直持续不断。
    • HTTP1.1 也就是所谓的keep-alive,把多个请求合并为一个。
    • 缺点:服务器没有数据到达时,HTTP 连接会停留一段时间,造成服务器资源浪费,数据交互的实时性也很低。
WebSocket 通信

创建了 WebSocket 后,会有一个 HTTP 请求发送到 S 用来握手。取得 S 响应后,建立的连接使用 HTTP 升级,从 HTTP 协议交换为 WebSocket 协议。即使用标准的 HTTP 服务器无法实现 WebSocket,只有支持这种协议的专门服务器才能正常工作。

一旦 WebSocket 连接建立后,后续数据都以帧序列的形式传输。在 C 断开连接或 S 端中断连接前,不需要 C 和 S 重新发起连接请求。

  • WebSocket 使用了自定义的协议,未加密的连接不再是http://,而是ws://,默认端口为 80,加密的连接也不是https://,而是wss://,默认端口为 443。
  • 性能优势:在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗。
  • 实时性优势:客户端发送和接受消息是在同一个持久连接上发起。
对比总结

WebSocket 是真正的全双工方式,建立连接后客户端与服务器端是完全平等的,可以互相主动请求。而 HTTP 长连接基于 HTTP,是传统的客户端对服务器发起请求的模式。

WebSocket 优点

  • 支持全双工通信,实时性好。
  • 支持文本和二进制。
  • 没有同源策略的限制,不存在跨域问题。
  • 较小的控制开销。CS 互相通信时,协议控制数据包头部相较于 HTTP 较小。
  • 支持扩展。可以自定义子协议等。

WebSocket 原理

WebSocket 并不是全新的协议。首先利用 HTTP 协议来建立握手,之后使用 WebSocket 协议来进行全双工通信。

握手

首先,WebSocket 连接必须由 C 发起,因为请求协议是一个标准的 HTTP 请求,格式如下:

GET ws://localhost:3000/ws/chat HTTP/1.1
Host: localhost
Upgrade: websocket
Connection: Upgrade
Origin: http://localhost:3000
Sec-WebSocket-Key: client-random-string
Sec-WebSocket-Version: 13
  • GET 请求的地址不是类似/path/,而是以ws://开头的地址。
  • 请求头Upgrade: websocketConnection: Upgrade表示这个连接将要被转换为 WebSocket 连接。
  • Sec-WebSocket-Key是用于标识这个连接,并非用于加密数据。
  • Sec-WebSocket-Version指定了 WebSocket 的协议版本。

之后,S 如果接受请求,就会返回如下响应:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: server-random-string
  • 状态码101表示本次连接的 HTTP 协议即将被更改。
  • Upgrade: websocket就是指定更改后的协议是 WebSocket 协议。

WebSocket API

WebSocket 只是一个应用层的协议规范,可以使用不同的编成语言实现客户端或者服务器端,下面介绍的是基于 JavaScript 语言的 WebSocket API,其他语言的实现大体一致。

构造函数

// url: 要连接的URL
// protocals: 一个协议字符串或者一个包含协议字符串的数组
var aWebSocket = new WebSocket(url [, protocols]);

属性

  • onopen,用于指定连接成功后的回调函数。
  • onmessage,用于指定当从服务器接受到信息时的回调函数。
  • onerror,用于指定连接失败后的回调函数。
  • onclose,用于指定连接关闭后的回调函数。
  • url,只读属性,用于返回所连接服务器的 url 地址,这是在构造函数中指定的。
  • protocol,只读属性,用于返回子协议的名称,这是在构造函数中指定的。
  • bufferedAmount,只读属性,用于返回已经被send()方法放入队列中但还没有被发送的数据字节数。
  • readyState,只读属性,用于返回当前的链接状态,有 4 种状态:

onmessage的回调函数有event对象,收到的数据保存在event.data中。
onclose的回调函数也有event对象,有三个额外属性:event.wasClean, code, reason,分别表示连接是否明确的关闭,服务器关闭的状态码以及原因。

Constantvalue
WebSocket.CONNECTING0
WebSocket.OPEN1
WebSocket.CLOSING2
WebSocket.CLOSED3

方法

  • close([code[, reason]]),关闭当前连接。
    • code是一个数字状态码,它解释了连接关闭的原因,默认是 1005 。
    • reason是一个字符串,文字补充说明关闭原因。
  • send(data),向服务器发送数据。

示例

var socket = new WebSocket("ws://localhost:8080");

// 正在建立连接
console.log("[readyState]-" + socket.readyState); //0

// 连接建立成功回调
socket.onopen = function() {
  console.log("Connection established.");
  console.log("[readyState]-" + socket.readyState); //1
  // 发送消息
  // socket.send('hello world');
};

// 连接失败回调
socket.onerror = function() {
  console.log("[readyState]-" + socket.readyState); //3
  console.log("Connection error.");
};

// 连接关闭回调
socket.onclose = function(event) {
  var code = event.code;
  var reason = event.reason;
  var wasClean = event.wasClean;
  console.log("[readyState]-" + socket.readyState); //3
  console.log("Connection closed.");
  console.log(code, reason, wasClean);
};

// 收到消息回调
socket.onmessage = function(event) {
  var data = event.data;
  // 处理数据
};

socket.close();
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值