前言:
之前写过一篇关于swoole的安装搭建的文章。也测试了搭建TCP协议的服务。但是今天我要介绍的是WebSocket协议,WebSoket协议的出现,解决了http协议的很大的一个缺陷。那就是服务器端无法直接向客户端或者说是浏览器端发送信息。而是要等待客户端的请求,才能发送数据给他。要想达到实时数据更新的话,就不得不使用轮询。但是轮询其实非常的损耗性能,解决起来也不够优雅。
WebSocket基类介绍
之前有介绍过如何搭建Swoole服务,其实swoole的类的继承,非常契合协议之间的关系。这里再次感叹大神就是大神,写的东西,简单易懂又透露出高级感。
通信协议首先要讲解下,通信协议的基础
也就是说,WebSocke协议和http协议都是基于TCP的协议。也就是三次握手,来保证通信的成功和完整。
同样的在swoole里面,有一个基类就是server类,还有两个类分别是httpServer类和WebSoket类继承子server类。就和他们之间网络协议的关系一样的。
也就是说server类中的属性,方法,回调方法在webSocket类中都是被继承,可以使用的
搭建一个WebSocket服务
这次我写的这个服务,采用面向对象的形式来写,代码读起来会比较清晰。
- 我们创建一个聊天室类
class Chat
{
const HOST = '0.0.0.0';//ip地址 0.0.0.0代表接受所有ip的访问
const PART = 81;//端口号
private $server = null;//单例存放websocket_server对象
}
- 根据手册了解到,我们需要调用websocket对象上的on方法来监听事件,并且书写回调函数。我们将这一部分代码写到构造函数中去,也就是在实例化对象的时候,就开启服务
public function __construct()
{
//实例化swoole_websocket_server并存储在我们Chat类中的属性上,达到单例的设计
$this->server = new swoole_websocket_server(self::HOST, self::PART);
//监听连接事件
$this->server->on('open', [$this, 'onOpen']);
//监听接收消息事件
$this->server->on('message', [$this, 'onMessage']);
//监听关闭事件
$this->server->on('close', [$this, 'onClose']);
//设置允许访问静态文件
$this->server->set([
'document_root' => '/grx/swoole/public',//这里传入静态文件的目录
'enable_static_handler' => true//允许访问静态文件
]);
//开启服务
$this->server->start();
}
- 刚才说了,这里不光要监听事件,还要编写回调方法。让事件被触发的时候,调用这个方法
/**
* 连接成功回调函数
* @param $server
* @param $request
*/
public function onOpen($server, $request)
{
echo $request->fd . '连接了' . PHP_EOL;//打印到我们终端
}
/**
* 接收到信息的回调函数
* @param $server
* @param $frame
*/
public function onMessage($server, $frame)
{
echo $frame->fd . '来了,说:' . $frame->data . PHP_EOL;//打印到我们终端
foreach ($server->connections as $fd) {//遍历TCP连接迭代器,拿到每个在线的客户端id
//将客户端发来的消息,推送给所有用户,也可以叫广播给所有在线客户端
$server->push($fd, json_encode(['no' => $frame->fd, 'msg' => $frame->data]));
}
}
/**
* 断开连接回调函数
* @param $server
* @param $fd
*/
public function onClose($server, $fd)
{
echo $fd . '走了' . PHP_EOL;//打印到我们终端
}
- 当还有最后的一步,就是实例化我们聊天室类
$obj = new Chat();
经过思_2c04的提醒。之前使用一个类的成员变量的方式来储存客户端的句柄是错误且低效的。swoole为我们提供了很好的TCP连接迭代器,专门用于广播等场景。之前的代码块里已经修正。以下是swoole源码中对TCP的注释。还举例说明了用法。亲测可用。
/**
* TCP连接迭代器,可以使用foreach遍历服务器当前所有的连接,此属性的功能与swoole_server->connnection_list是一致的,但是更加友好。遍历的元素为单个连接的fd
*
* 连接迭代器依赖pcre库,未安装pcre库无法使用此功能
*
* foreach($server->connections as $fd)
* {
* $server->send($fd, "hello");
* }
*
* echo "当前服务器共有 ".count($server->connections). " 个连接\n";
*
* @var \Swoole\Connection\Iterator
*/
public $connections;
流程图.png那么我先简单介绍下流程,因为里面很多方法的使用,在官网的手册中已经写的很详细了所以这里就不做过多的解释了。只描述下流程
这里最关键的一步就是,将消息发给每个在线的客户端。聊天室的关键其实就是,让自己的发言,能让别人看到,别的人发言,我也能看到。所以,我们需要websocket这样的服务器主动发送消息给客户端才可以办到
客户端部分
由于博主本身是搞php后端的,前端只是略懂,加之在公司都是前后端分离,很久没写前端代码了。大家看看就好,我就不过多介绍了
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>色情聊天室</title>
<script src="http://libs.baidu.com/jquery/1.9.1/jquery.min.js"></script>
</head>
<body>
<textarea class="log" style="width: 100%; height: 500px;">
=======色情聊天室======
</textarea>
<input type="button" value="连接" onClick="link()">
<input type="button" value="断开" onClick="dis()">
<input type="text" id="text">
<input type="button" value="发送" onClick="send()">
<script>
function link(){
var url='ws://gongruixiang.club:81';
socket=new WebSocket(url);
socket.onopen=function(){log1('连接成功')}
socket.onmessage=function(msg){log(msg.data);console.log(msg);}
socket.onclose=function(){log1('断开连接')}
}
function dis(){
socket.close();
socket=null;
}
function log1(var1) {
$('.log').append(var1+'\r\n');
}
function log(var1){
var v=$.parseJSON(var1)
$('.log').append('用户'+v['no']+'说:'+v['msg']+'\r\n');
}
function send(){
var text=$('#text').val();
socket.send(text);
}
function send2(){
var json = JSON.stringify({'type':'php','msg':$('#text2').attr('value')})
socket.send(json);
}
</script>
</body>
</html>
前端效果
服务器终端效果
好了,今天就先写到这里,如果有不对的地方希望获得指点。关于swoole的文章我会持续更新。谢谢
以上
网友评论