随着网际网络的发展,传统的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
程序实作
前端建立与监听
// 创建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
安装方法(源码编译安装)
- 下载源代码包
https://github.com/swoole/swoole-src/releases
https://pecl.php.net/package/swoole
https://gitee.com/swoole/swoole/tags
- 在终端进入源码目录,执行下面的命令进行编译和安装。
//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 发布时间)
- 执行以下代码
pecl install swoole
- 添加 Swoole 到 php.ini
修改 php.ini 加入 extension=swoole.so
代码范例
具体参数可查看 swoole 方法参数文档
//创建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 对象
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事件
Websocket.addEventListener('open',function(event){
//do something
});