弹幕(barrage),中文流行词语,原意指用大量或少许火炮提供密集炮击。大量以字幕弹(dàn)出形式显示的评论同时在屏幕上飘过的现象也被称为弹幕。
php
做为PHPer的咱们,看到如今各类网站都有酷炫的弹幕飞过,咱们是否是也想给本身的网站加入弹幕功能呢?html
首先弹幕的后端其实说白了和公共聊天室的后端原理十分类似,都是一个客户端发送消息给服务端,服务端再将收到的消息广播给其余的客户端。对于后端来讲他们几乎没区别,区别就在于前端。前端
好在咱们有一个前端弹幕插件,这个插件是一个jquery插件,github地址:https://github.com/chiruom/jquery.danmu.js,基本上会使用jquery语法,看看示例代码就能够傻瓜化使用。jquery
前端已经有了解决方案,可是后端呢?前端如何与后端通信?用传统的ajax轮询吗?不行,这样效率过低,想一想各大火爆的直播平台都是同一时间几万人在线,几千人同时发弹幕,若是靠ajax轮询一个php接口的话服务器会吃不消的。且弹幕消息存储方案略显复杂,有人问为何要存储呢?由于ajax使用的HTTP协议是无状态协议,A客户端和B客户端之间对于服务器来讲没有任何标志,若是服务器要确保A客户端和B客户端分别在两次请求的时候服务器只返回这两个客户端没有获取过的弹幕消息,那么服务器端就必须使用一个缓存来标识某某客户端看过哪条弹幕消息。综上所述ajax能够实现小规模的弹幕通讯方案,可是很麻烦。linux
好在最新的HTML5中加入了WebSocket协议,咱们能够经过WebSocket这种基于HTTP协议之上的即时通讯协议来替代ajax这种传统的我问你答的老旧通讯模式。而咱们是PHPer,对于咱们这种只懂PHP的人该如何编写WebSocket服务端呢?好在咱们又得知PHP有一个Swoole扩展,咱们在PHP语言中使用它能够很方便的构建一个WebSocket服务端。git
关于Swoole的介绍能够参照他的官网http://www.swoole.com/,下面引用官网对它的一段简短的介绍。github
PHP的异步、并行、高性能网络通讯引擎,使用纯C语言编写,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,异步Redis,数据库链接池,AsyncTask,消息队列,毫秒定时器,异步文件读写,异步DNS查询。 Swoole内置了Http/WebSocket服务器端/客户端、Http2.0服务器端。先别被Swoole这么多的功能吓到了。咱们先关注这里面的重点
Swoole能够普遍应用于互联网、移动通讯、企业软件、云计算、网络游戏、物联网(IOT)、车联网、智能家居等领域。 使用PHP+Swoole做为网络通讯框架,可使企业IT研发团队的效率大大提高,更加专一于开发创新产品。
Swoole内置了Http/WebSocket服务器端/客户端
意味着咱们能够经过它构建WebSocket的服务端。看到这里咱们是否是就急急忙忙去拿官网的WebSocket服务端代码作测试呢?不,Swoole是一个PHP扩展,意味着咱们还得去安装它。是否是直接去下载so文件而后在php.ini中加入extension=swoole.so就能够了呢?还不是,咱们先去看看Swoole扩展的依赖,这也是咱们使用任何语言的任何外部包,外部模块,外部扩展最早要了解的问题。web
参考官网:http://wiki.swoole.com/wiki/page/7.htmlajax
环境依赖
- 仅支持Linux,FreeBSD,MacOS,3类操做系统
- Linux内核版本2.3.32以上
- PHP5.3.10以上版本,包括PHP7
- gcc4.4以上版本或者clang
- cmake2.4+,编译为libswoole.so做为C/C++库时须要使用cmake
PHP版本依赖
- swoole仅支持PHP5.3.10或更高版本,建议使用PHP5.4+
- swoole不依赖php的stream、sockets、pcntl、posix、sysvmsg等扩展。PHP只需安装最基本的扩展便可
意味着咱们Windows下是没法使用这个扩展了(其实能够借助cygwin在win下使用swoole,可是考虑到咱们使用swoole扩展就是为了性能,也为了熟悉之后的生产环节部署作准备,强烈推荐在linux下开发),那么咱们把开发环境转移到Linux下进行吧。数据库
接着还要求Linux内核版本为2.3.32以上,PHP为5.3.10以上,那么咱们就用最新的CentOS吧,这个版本的yum安装的php直接就是PHP7最新版,根本无需考虑其余问题,固然你喜欢图形界面,用Ubuntu也能够。其余的基本上最新的Linux发行版都是符合版本要求的。
接着咱们便来安装这个扩展,推荐使用PECL来安装,只须要一条
pecl install swoole
便可,很是方便。固然你要编译安装,具体步骤请参考http://wiki.swoole.com/wiki/page/6.html
安装完扩展以后在命令行下输入
php -m
检查,若是有swoole那么说明安装成功了。
接下来就正式开始咱们的编码旅程了。
开始编码旅程以前咱们先看看最基础的效果原型是什么样子
没错就是这个样子,两个浏览器以前彻底独立使用Websocket链接服务端,所以对于服务端来讲这两个浏览器就至关于两个彻底处在不一样机器上的客户端。
效果看完了就开始来说代码吧。
咱们先看看官网的WebSocket服务端示例代码。
$serv = new Swoole\Websocket\Server("127.0.0.1", 9502); $serv->on('Open', function($server, $req) { echo "connection open: ".$req->fd; }); $serv->on('Message', function($server, $frame) { echo "message: ".$frame->data; $server->push($frame->fd, json_encode(["hello", "world"])); }); $serv->on('Close', function($server, $fd) { echo "connection close: ".$fd; }); $serv->start();
咱们看到这个代码的第一行先是new了一个WebSocket服务端对象,而且在构造方法中的第一个参数指定了服务端监听的IP,第二个参数指定了服务端监听的端口。而后使用on方法为每个事件设置了回调函数,最后一行start方法正式开始运行服务端。
这种写法很是像Javascript里面的异步调用,这也是Swoole中的事件驱动异步非阻塞特性,正由于是这种特性,每个独立的事件(请求)会在服务端接收到以后分别异步处理,他们之间无需互相等待,这也是Swoole性能高的缘由所在。
咱们来分别剖析一下每个事件的含义。
$serv->on('Open', function($server, $req) { echo "connection open: ".$req->fd; });
顾名思义,Open表示打开一个新的连接,而且在事件触发以后echo出链接上服务端的客户端id,该客户端惟一id为回调函数第二个参数中的fd字段。这也是服务端区分客户端的惟一id。
$serv->on('Message', function($server, $frame) { echo "message: ".$frame->data; $server->push($frame->fd, json_encode(["hello", "world"])); });
一样顾名思义,Message表示消息到达服务端的事件,而且在事件触发以后echo出发送给服务端的数据,该数据为回调函数第二个参数的data字段。另外咱们还看到它调用了$server->push,这是回调函数的第一个参数中的push方法,它是一个服务端给客户的发送数据的方法,第一个参数为要发送的客户端id,第二个为要发送的数据,这里的含义是向发给服务端消息的那个客户端发送["hello", "world"]这个数组(方括号写数组为PHP5.4的新特性,若是你是PHP5.3请使用传统的array工厂函数生成数组)通过json序列化以后的数据。
$serv->on('Close', function($server, $fd) { echo "connection close: ".$fd; });
最后一个事件Close更加容易理解,就是关闭事件,固然关闭的不是服务端,而是客户端,能够理解为客户端与服务端断开链接的事件。回调函数中的代码含义为echo出与服务端断开链接的那个客户端id。
基本的API都清楚了,下面就直接看代码吧,短短二十行而已。
https://github.com/cw1997/danmu-demo/blob/master/server.php
$server = new swoole_websocket_server("0.0.0.0", 1997); $server->on('open', function (swoole_websocket_server $server, $request) { echo "server: handshake success with fd{$request->fd}\n";//$request->fd 是客户端id }); $server->on('message', function (swoole_websocket_server $server, $frame) { echo "receive from {$frame->fd}:{$frame->data},opcode:{$frame->opcode},fin:{$frame->finish}\n"; //$frame->fd 是客户端id,$frame->data是客户端发送的数据 //服务端向客户端发送数据是用 $server->push( '客户端id' , '内容') $data = $frame->data; foreach($server->connections as $fd){ $server->push($fd , $data);//循环广播 } }); $server->on('close', function ($ser, $fd) { echo "client {$fd} closed\n"; }); $server->start();
这里最核心的广播代码其实还用到了一个以前没有提到过的成员,也就是swoole_websocket_server对象的connections成员,这个成员中保存了全部已链接上该WebSocket服务端的fd,也就是客户端id。所以咱们只要在message事件中使用foreach遍历该成员,循环将全部服务端收到的弹幕消息都发送给其余已链接上该服务端的客户端便可。
后端讲完了再讲讲前端吧。
前端代码也不是不少https://github.com/cw1997/danmu-demo/blob/master/index.html
var ws = new WebSocket("ws://192.168.1.107:1997"); ws.onopen = function(){ console.log("握手成功"); ws.send('hello world!!!'); }; ws.onmessage = function(e){ console.log("message:" + e.data); var time = jQuery('#danmu').data("nowtime") + 1; var text_obj = '{ "text":"' + e.data + '" , "color":"green" ,"size":"1","position":"0","time":"' + time + '" ,"isnew":" "}'; //构造加上了innew属性的字符串danmu对象 console.log(text_obj); var new_obj = eval_r('(' + text_obj + ')'); //转化为js对象 jQuery('#danmu').danmu("add_danmu", new_obj); //向插件中添加该danmu对象 }; ws.onerror = function(){ console.log("error"); };
核心代码都在这里,使用new WebSocket("ws://192.168.1.107:1997")建立一个WebSocket客户端链接对象,经过该对象的各类事件进行对应的操做,和服务端是否是很像?更多代码解释能够参考源代码中的注释,这里不作更多介绍。
看到这里相信做为一名PHPer的你也能够开发出属于本身的弹幕系统了。这里展现的只是一个最基础最原始的弹幕平台。咱们也了解到了使用PHP开发一个弹幕平台须要涉及到的技术有WebSocket,Swoole扩展,甚至碰到了不少初级开发者平时不怎么接触的工具,好比说PECL,好比说Linux。
其实PHP结合Swoole扩展还能够作不少事情,好比说对接各类家电,对接各类硬件接口实如今Web端实时控制家电,又好比说结合树莓派作智能小车,经过web端进行遥控等等,各类新奇的玩法等你发现。谁说PHP只能作Web开发?PHP拥有了Swoole扩展其实能作的事情还有不少,Swoole就像他的宣传标题同样:从新定义PHP。
本文章由 @昌维 原创,在知乎专栏-代码之美 https://zhuanlan.zhihu.com/codes 首发,转载请注明出处,谢谢。