Websocket 简介

  随着网际网络的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了。近年来,随着HTML5的完善,WebSocket协议被提出,它实现了浏览器与服务器的全双工通讯,扩充套件了浏览器与服务端的通讯功能,使服务端也能主动向客户端传送资料。

介绍

  • WebSocket 是一种标准协议,用于在客户端和服务端之间进行双向数据传输。但它跟 HTTP 没什么关系,它是基于 TCP 的一种独立实现。是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。
  • WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
  • 在HTTP协议中,客户端与服务器端的通信是靠客户端发起请求,然后服务器端收到请求再进行回应,这个过程中,客户端是主动的,服务器端是被动的。 而Websocket协议就不一样了,它是基于TCP的一种新的网络协议,它与Http协议不同之处就在于Websocket能实现服务器端主动推送消息到客户端,服务器端与客户端都能发起通信,这一次,服务器端终于也拥有了主动权。
  • 简单概括:WebSocket 是基于TCP的长连接协议,只有一个通信状态不做数据传输,属于应用层协议。

浏览器支持

应用场景

  • 带有实时通讯、实时资料、订阅推送等功能的应用。

为什么要使用 WebSocket

  • 因为HTTP1.0的生命周期是以request作为界定的,也就是一个request,一个response,对于HTTP来说,本次client与server的会话到此结束;而在HTTP1.1中,稍微有所改进,即添加了keep-alive,也就是在一个HTTP连接中可以进行多个request请求和多个response接受操作。
  • 然而在实时通信中,并没有多大的作用,HTTP只能由client发起请求,server才能返回信息,即server不能主动向client推送信息,无法满足实时通信的要求。

WebSocket

HTTP

通信方向

双向:但是第一次必须由客户端发起

单向:只能由客户端发送

连接周期

不会断开,可以一直通信。

每完成一次通信就会断开

WebSocket、轮询、Comet比较

  • 在WebSocket规范提出之前,开发人员若要实现这些实时性较强的功能,经常会使用折衷的解决方法:轮询(polling)和Comet技术。其实后者本质上也是一种轮询,只不过有所改进。
  • 轮询是最原始的实现实时Web应用的解决方案。轮询技术要求客户端以设定的时间间隔周期性地向服务端传送请求,频繁地查询是否有新的资料改动。
  • Comet技术又可以分为长轮询和流技术。长轮询改进了上述的轮询技术,减小了无用的请求。它会为某些资料设定过期时间,当资料过期后才会向服务端传送请求;这种机制适合资料的改动不是特别频繁的情况。流技术通常是指客户端使用一个隐藏的视窗与服务端建立一个HTTP长连线,服务端会不断更新连线状态以保持HTTP长连线存活;这样的话,服务端就可以通过这条长连线主动将资料传送给客户端;流技术在大并发环境下,可能会考验到服务端的效能。
  • 由于这两种技术都是基于请求-应答模式,都不算是真正意义上的实时技术;它们的每一次请求、应答,都浪费了一定流量在相同的头部信息上,并且开发复杂度也较大。若客户端想知道服务端的处理进度,不需不停的向服务端发送请求,明显地,这种方法会导致过多不必要的请求,浪费流量和服务器资源。
  • 而当服务器完成协议升级后( HTTP -> WebSocket ),WebSocket可以进行持久化连接,即client只需进行一次握手,成功后即可持续进行数据通信,值得关注的是WebSocket实现client与server之间全双工通信,即server端有数据更新时可以主动推送给client端。解决了轮询造成的同步延迟问题。而且 WebSocket 只需要一次 HTTP 握手,服务端就能一直与客户端保持通信,直到关闭连接,这样就解决了服务器需要反复解析 HTTP 协议的问题,减少了资源的开销。

连接原理

  • HTML5推出的WebSocket,真正实现了Web的实时通讯,使B/S模式具备了C/S模式的实时通讯能力。
  • WebSocket的原理是这样的:浏览器通过JavaScript向服务端发出建立WebSocket连线的请求,在WebSocket连线建立成功后,客户端和服务端就可以通过TCP连线传输资料。因为WebSocket连线本质上是TCP连线,不需要每次传输都带上重复的头部资料,所以它的资料传输量比轮询和Comet技术小了很多。

工作流程

  • 客户端client准备向服务端发送Handshake Request
  • client建立WebSocket时向服务器端请求的信息
  • 服务器获取到client请求的信息后,根据WebSocket协议对数据进行处理并返回,其中要对Sec-WebSocket-Key进行加密等操作。
  • 服务端server向客户端返回Hanshake Response
  • 通信建立完成,进行全双工通信(系统允许二台装置间同时进行双向资料传输。)。
  • 完成通信后客户端client可以向服务端发送Close Connection

程序实作

前端建立与监听

代码语言:javascript
复制
// 创建websocket
var ws = new WebSocket("wss://域名+端口");//[http时为ws://][https时为wss://]
// websocket 创建成功事件
ws.onopen = function (evt) {
  console.log("系统消息:建立连接成功");
};
// websocket 关闭事件
ws.onclose = function (evt) {
  console.log("系统消息:关闭连接成功");
};
// websocket 接收到消息事件
ws.onmessage = function (e) {
  var data = JSON.parse(e.data);
  switch (data.type) {
    case 'handShake':
      //do something
      break;
    case 'login':
      //do something
      break;
    case 'logout':
      //do something
      break;
    case 'user':
      //do something
      break;
    case 'system':
      //do something
      break;
      ......
  }
};
// websocket 错误事件
ws.onerror = function (evt, e) {
  alert("系统消息: 出错了,请退出重试。");
};
// websocket 发送事件
ws.send(data);
//$(document).ready(function(){
  //console.log(websocket.readyState ? `${websocket.readyState}:连接success` : `${websocket.readyState}:连接ing`);//查看websocket readySate
//});

后端建立服务

  • 原生php实践较为复杂,此处采用swoole插件简单介绍
Swoole
  • PHP的socket扩展是一套socket api,仅此而已。
  • Swoole,用C语言实现,它的socket是C 库的socket,更加底层可控。
  • 原生php socket实践较为复杂,而使用swoole扩展可以省去很多步骤如:握手、编码、解码等等,并且简化了广播等操作,所以此处采用swoole插件简单介绍。
  • Swoole 扩展是按照 PHP 标准扩展构建的。使用 phpize 来生成编译检测脚本,./configure 来做编译配置检测,make 进行编译,make install 进行安装。(如无特殊需求,请务必编译安装 Swoole 的最新 release 版本。)
注意事项
  • 如果当前用户不是 root,可能没有 PHP 安装目录的写权限,安装时需要 sudo 或者 su
  • 如果是在 git 分支上直接 git pull 更新代码,重新编译前务必要执行 make clean
  • 仅支持 Linux(2.3.32 以上内核)、FreeBSD、MacOS 三种操作系统,低版本 Linux 系统(如 CentOS 6)可以使用 RedHat 提供的 devtools 编译,参考文档, 在 Windows 平台,可使用 CygWin 或 WSL(Windows Subsystem for Linux)
  • 部分扩展与 Swoole 扩展不兼容,参考 扩展冲突。
安装准备
  • php-7.1 或更高版本
  • gcc-4.8 或更高版本
  • make
  • autoconf
安装方法(源码编译安装)
  • 下载源代码包
代码语言:javascript
复制
https://github.com/swoole/swoole-src/releases
https://pecl.php.net/package/swoole
https://gitee.com/swoole/swoole/tags
  • 在终端进入源码目录,执行下面的命令进行编译和安装。
代码语言:javascript
复制
//ubuntu 没有安装 phpize 可执行命令:sudo apt-get install php-dev 来安装 phpize
cd swoole-src && \
phpize && \
./configure && \
make && sudo make install
  • 启用扩展

编译安装到系统成功后,需要在 php.ini 中加入一行 extension=swoole.so 来启用 Swoole 扩展。

安装方法(PECL,注意: PECL 发布时间晚于 Github 发布时间)
  • 执行以下代码
代码语言:javascript
复制
pecl install swoole
  • 添加 Swoole 到 php.ini

修改 php.ini 加入 extension=swoole.so

代码范例

具体参数可查看 swoole 方法参数文档

代码语言:javascript
复制
//创建websocket 服务器
$ws = new swoole_websocket_server(域名, 端口);

//open 建立连接 ws 服务器,request:客户端信息
ws->on('open', function(ws, $request) {
var_dump($request);
//do something
ws->push(request->fd, "welcome to mychatroom \n");
});

//message 接收信息
ws->on('message', function(ws, $request) {
echo "message: $request->data";
//do something
ws->push(request->fd, "get it message \n");
});

//close 关闭连接
ws->on('close', function(ws, $request) {
//do something
echo "close \n";
});

//启动服务
$ws->start();

WebSocket 速查

WebSocket 对象

代码语言:javascript
复制
var Socket = new WebSocket(url, [protocol] );

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

WebSocket 属性

以下是 WebSocket 对象的属性。

属性

描述

Socket.readyState

只读属性readyState表示连接状态,可以是以下值:0-表示连接尚未建立。1-表示连接已建立,可以进行通信。2-表示连接正在进行关闭。3-表示连接已经关闭或者连接不能打开。

Socket.bufferedAmount

只读属性bufferedAmount已被send()放入正在队列中等待传输,但是还没有发出的UTF-8文本字节数。

  • 0-表示连接尚未建立。
  • 1-表示连接已建立,可以进行通信。
  • 2-表示连接正在进行关闭。
  • 3-表示连接已经关闭或者连接不能打开。

Socket.bufferedAmount只读属性bufferedAmount已被send()放入正在队列中等待传输,但是还没有发出的UTF-8文本字节数。

WebSocket 事件

以下是 WebSocket 对象的相关事件。

事件

事件处理程序

描述

open

Socket.onopen

连接建立时触发

message

Socket.onmessage

客户端接收服务端数据时触发

error

Socket.onerror

通信发生错误时触发

close

Socket.onclose

连接关闭时触发

WebSocket 方法

以下是 WebSocket 对象的相关方法。

方法

描述

Socket.send()

使用连接发送数据

Socket.close()

关闭连接

监听websocket事件

代码语言:javascript
复制
Websocket.addEventListener('open',function(event){
//do something
});

其他