一、什么是websocket
websocket != socket。
我猜有些人一看标题websocket就联想到socket,其实二者之间并没多大关系,这就好比javascript和java,千万不要混淆了。
那websocket是什么呢?
websocket是一个协议,它仅仅就是一个协议而已,跟我们所了解的http协议、https协议、ftp协议等等一样,都是一种单纯的协议。
websocket是一种怎样的协议呢?换句话说它有什么特点呢?
相对于Http这种非持久连接而言,websocket协议是一种持久化连接,它是一种独立的,基于TCP的协议。基于websocket,我们可以实现客户端和服务端双向通信。
你肯定听说过服务器推技术,在websocket出现之前,为了解决此类问题,常用的解决方法有轮询和long pull,这两种技术都是客户端和服务端建立源源不断的HTTP连接,非常消耗带宽和服务器资源。
websocket是双向持久连接,客户端和服务端只需要第一次建立连接即可实现双向通信,说到这里,你肯定明白我们学习websocket要做什么了。没错,基于websocket,我们可以做一些通讯,推送相关的服务。
swoole内置的websocket服务器,异步非阻塞多进程,牛逼的swoole!
二、创建websocket服务器
我们看一下在swoole中如何创建websocket服务器。
swoole的websocket服务器的创建同样非常简单,只需要我们处理好相应的回调即可。
class WebSocketServer
{
private $_serv;
public function __construct()
{
$this->_serv = new swoole_websocket_server("0.0.0.0", 9501);
$this->_serv->set([
"worker_num" => 1,
]);
$this->_serv->on("open", [$this, "onOpen"]);
$this->_serv->on("message", [$this, "onMessage"]);
$this->_serv->on("close", [$this, "onClose"]);
}
/**
* @param $serv
* @param $request
*/
public function onOpen($serv, $request)
{
echo "server: handshake success with fd{$request->fd}.\n";
}
/**
* @param $serv
* @param $frame
*/
public function onMessage($serv, $frame)
{
$serv->push($frame->fd, "server received data :{$frame->data}");
}
public function onClose($serv, $fd)
{
echo "client {$fd} closed.\n";
}
public function start()
{
$this->_serv->start();
}
}
$server = new WebSocketServer;
$server->start();
来看看我们创建的这个websocket服务器,我们介绍两个回调
onOpen回调:客户端与服务端建立连接的时候将触发该回调,回调的第二个参数是swoole_http_request对象,包括了http握手的一些信息,比如GET\COOKIE等
onMessage回调:这个是服务端收到客户端信息后回调,在该回调内我们调用了swoole_websocket_server::push方法向客户端推送了数据,注意哦,push的第一个参数只能是websocket客户端的标识
对应的,swoole是否也有提供websocket客户端呢?有,不过我们不准备使用,浏览器天生内置js版的websocket客户端,简单方便好用!当然,不包括低版本的IE浏览器,原因你懂得。
在js中,有一套操作websocket的API,我们可以用这个API创建websocket对象,建立与websocket服务端的连接,并且可以向server发送消息,接收server的消息,当然也少不了关闭连接的操作。我们看一个例子
首先我们创建一个index.html文件,写一段js代码,代码如下:
var ws = new WebSocket("ws://139.199.201.210:9501");
ws.onopen = function(event) {
// 发送消息
ws.send("This is websocket client.");
};
// 监听消息
ws.onmessage = function(event) {
console.log("Client received a message: ", event.data);
};
ws.onclose = function(event) {
console.log("Client has closed.\n", event);
};
js代码写好之后,回车之前记得先在CLI下把websocket服务器运行起来,不然哪能连接的上呢。
二者都准备完善之后,打开控制台,刷新下浏览器,我们看到控制台输出的结果
结果可能不是很明显,单独来看
客户端先发送给server : This is websocket client.
server收到消息后,在onMessage回调内向客户端推送了 server received data :This is websocket client.
客户端在onmessage回调内收到server的信息,并在server发过来的消息的前面增加了 Client received a message: ,这才有了控制台上面展示的信息
整个过程,完美的交互,至此,客户端和服务端便建立了持久化的双向连接。二者可以互发消息。
有些同学可能没转过弯来,这样就互通了,可以双向交互了?我们把js代码稍稍完善一下,做一个界面上的交互
创建一个文本框、一个点击发送的按钮和一个用于展示消息的div
然后我们看看js操作
var ws = new WebSocket("ws://139.199.201.210:9501");
ws.onopen = function(event) {
ws.send("This is websocket client.");
};
ws.onmessage = function(event) {
var data = event.data;
var ul = document.getElementById("ul");
var li = document.createElement("li");
li.innerHTML = data;
ul.appendChild(li);
};
ws.onclose = function(event) {
console.log("Client has closed.\n", event);
};
function send() {
var obj = document.getElementById("content");
var content = obj.value;
ws.send(content);
}
简单的说一下client处理的过程:连接上server之后,ws就发送了一条消息,然后onmessage回调中,会把接收自server的消息追加显示在我们创建的div#ul上,当我们在文本框输入消息内容的时候,点击发送,send方法会被调用,结果就是这个内容会被发送到server。
注:SSL的server,如果你的swoole安装的时候未配置ssl,那么必须重新编译安装。
$this->_serv = new swoole_websocket_server("0.0.0.0", 9501,SWOOLE_PROCESS, SWOOLE_SOCK_TCP | SWOOLE_SSL);
$this->_serv->set([
"worker_num" => 1,
"ssl_cert_file" => __DIR__."/source/test.lulublog.cn.crt",
"ssl_key_file" => __DIR__."/source/test.lulublog.cn.key",
]);
注:SSL 客户端连接
var ws = new WebSocket("wss://test.lulublog.cn:9501");
我们看服务端做了哪些改动,server在前面的基础之上,只对onMessage回调做了修改,修改后的代码如下
public function onMessage($serv, $frame)
{
// 循环当前的所有连接,并把接收到的客户端信息全部发送
foreach ($serv->connections as $fd) {
$serv->push($fd, $frame->data);
}
}
还记得我们之前讲的swoole_server的一系列属性吗,比如setting,worker_id。今天我们再讲一个swoole_server::connections属性,这个属性是一个迭代器对象,记录着当前server所有的连接fd,所以我们这里循环所有的fd,并把客户端接收的消息给每一个客户端。
swoole_server::connections——此属性在1.7.16以上版本可用连接迭代器依赖pcre库(不是PHP的pcre扩展),未安装pcre库无法使用此功能,安装完成后重新编译swoole(可能需要安装新版本)。然后使用php --ri swoole查看swoole扩展相关信息中是否有pcre => enabled。
yum install -y pcre pcre-devel
为了对演示的结果看的更明显一些,我们同时打开两个客户端页面操作,看动图
再往大了说,我们这个是不是可以扩展为二者的聊天了呢?
有兴趣的可以先捣鼓捣鼓,没兴趣的也可以捣鼓捣鼓了,因为后面我们基于websocket的实例可能不是聊天,而是web通知,敬请期待。