使用Swoole构建WebSocket服务端:先了解扩展依赖,Windows使用有技巧

这意味着我们能够利用它来搭建 WebSocket 的服务端。看到此处,我们是否就会迫不及待地拿官网的 WebSocket 服务端代码去进行测试呢?并非如此,Swoole 是一个 PHP 扩展,这就意味着我们还需要去对其进行安装。难道直接去下载 so 文件,然后在 php.ini 中添加 extension=swoole.so 就可以了吗?我们先去查看 Swoole 扩展的依赖,这是我们在使用任何语言的任何外部包、外部模块、外部扩展时最先需要了解的问题,不是其他情况。

使用Swoole构建WebSocket服务端:先了解扩展依赖,Windows使用有技巧

官网参考:wiki.swoole.com/wiki/page/7.html

环境依赖PHP版本依赖

意味着在 Windows 系统下无法使用这个扩展。实际上可以借助 cygwin 在 Windows 下使用 swoole,但鉴于使用 swoole 扩展是为了性能,也是为了熟悉以后的生产环节部署做准备,所以强烈推荐在 Linux 下进行开发。那么,我们把开发环境转移到 Linux 下吧。

要求 Linux 内核版本需在 2.3.32 及以上,PHP 版本要在 5.3.10 及以上,那么我们可以选用最新的 CentOS,因为该版本通过 yum 安装的 php 直接就是 PHP7 的最新版,无需考虑其他问题。当然,如果喜欢图形界面,也可以使用 Ubuntu。其他大多数最新的 Linux 发行版都能满足版本要求。

接着我们开始安装这个扩展,建议使用 PECL 进行安装,仅需一条命令即可。

pecl install swoole

非常方便。当然,如果你需要进行编译安装,具体的步骤可以参考 wiki.swoole.com/wiki/page/6.html 。

安装完扩展之后在命令行下输入

php -m

检查,如果有swoole那么说明安装成功了。

接下来就正式开始我们的编码旅程了。

开始编码旅程之前我们先看看最基础的效果原型是什么样子

就是这个样子。两个浏览器之前一直是各自独立地使用 Websocket 连接服务端。所以对于服务端而言,这两个浏览器就如同两个处在完全不同机器上的客户端。

效果看完了就开始来讲代码吧。

我们先看看官网的WebSocket服务端示例代码。

创建一个名为 $serv 的新对象,该对象是 Swoole 的 WebSocket 服务器,它监听在 127.0.0.1 的 9502 端口上。
$serv 监听 'Open' 事件,当事件触发时,会执行一个函数,该函数接收两个参数,分别是 $server 和 $req 。
输出“连接已打开:”与请求对象的文件描述符($req->fd)相连的内容。
});
当接收到“Message”事件时,$serv 会执行一个函数,该函数接受两个参数,分别是 $server 和 $frame 。
输出“message: ”与 $frame->data 进行拼接后的内容。
$server 向 $frame 的 fd 推送了 json 编码后的 ["hello", "world"] 数据。
});
当服务器的连接关闭时,会触发 Close 事件,此时会执行一个匿名函数,该函数接受服务器对象 $server 和文件描述符 $fd 作为参数。
输出“连接关闭:”以及变量 $fd 的值。
});
$serv->start();

我们看到这个代码的第一行创建了一个 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”更易理解,此事件为关闭事件。需要明确的是,被关闭的并非服务端,而是客户端,也就是客户端与服务端断开连接的事件。回调函数中的代码意思是输出与服务端断开连接的那个客户端的 id。

基本的API都清楚了,下面就直接看代码吧,短短二十行而已。

github.com 下 cw1997 的 danmu-demo 仓库中 master 分支下的 server.php 文件

创建了一个名为 $server 的新的 swoole_websocket_server 实例,其监听地址为 "0.0.0.0",端口号为 1997 。

服务器的 $server 监听了 'open' 事件,当事件触发时,会执行一个匿名函数,该函数接收两个参数,一个是 swoole_websocket_server 类型的 $server,另一个是 $request 。

输出“server: 与 fd{$request->fd} 成功握手”。
   //$request->fd 是客户端id
   
});

服务器的 on 方法监听了'message'事件,当事件触发时,会执行一个匿名函数。这个匿名函数接收两个参数,一个是 swoole_websocket_server 类型的服务器对象 $server,另一个是包含消息帧信息的 $frame。

接收自 {$frame->fd} 的内容为 {$frame->data},操作码为 {$frame->opcode}。
   fin:{$frame->finish}\n";
   
客户端 id 是 $frame->fd,客户端发送的数据是 $frame->data
   
服务端会通过$server->push( '客户端id', '内容')的方式向客户端发送数据。
   
   $data = $frame->data;
   
对于每个 $server 的连接 $fd 来说:
   
$server 执行 push 操作,将 $data 推送给 $fd ;这是一个循环广播的过程。
       
   }});

$server 触发了 'close' 事件,在这个事件的处理函数中,有两个参数,分别是 $ser 和 $fd 。

输出“client(fd)已关闭”。
   
});

$server->start();

这里最核心的广播代码实际上还运用了一个之前未曾提及的成员,此成员为 swoole_websocket_server 对象的 connections 成员。在这个成员中,保存着所有已连接到该 WebSocket 服务端的 fd,也就是客户端 id。因此,在 message 事件中,我们可以使用 foreach 来遍历该成员。通过循环,能够将所有服务端收到的弹幕消息发送给其他已连接上该服务端的客户端。

后端讲完了再讲讲前端吧。

前端代码的数量并不是很多。它位于 github.com/cw1997/danmu-demo/blob/master/index.html 这个路径下。

创建了一个 WebSocket 对象,其连接地址为 "ws://192.168.1.107:1997"。
ws.onopen = function(){
    console.log("握手成功");
    ws.send('hello world!!!');
};
ws.onmessage = function(e){
在控制台输出“message:”加上 e.data 的内容。
text_obj.isnew = "";  //构造加上了 innew 属性的字符串 danmu 对象
    console.log(text_obj);
通过 eval 函数将 text_obj 转化为一个 JavaScript 对象,并将其赋值给 new_obj 。
jQuery 选择了 id 为“danmu”的元素,然后通过“danmu”方法向该插件添加了新的对象 new_obj,即添加了该 danmu 对象
};
ws.onerror = function(){
    console.log("error");
};

核心代码位于此处,通过使用 new WebSocket("ws://192.168.1.107:1997") 来创建一个 WebSocket 客户端连接对象。利用该对象的各种事件,能够进行相应的操作。这与服务端的情况是不是很相似呢?若想了解更多代码的解释,可以参考源代码中的注释,在此处就不再做更多的介绍了。

看到这里,相信身为一名 PHP 开发者的你能够开发出属于自己的弹幕系统了。这里所展示的仅仅是一个最为基础、最为原始的弹幕平台。我们也知晓了使用 PHP 开发一个弹幕平台需要涉及的技术包含 WebSocket 和 Swoole 扩展,甚至还碰到了许多初级开发者平时较少接触的工具,像 PECL,像 Linux。

PHP 结合 Swoole 扩展能够做许多事情。例如可以对接各类家电,能够对接各种硬件接口,从而实现在 Web 端实时控制家电。还可以结合树莓派制作智能小车,通过 web 端进行遥控等。各种新奇的玩法等待着你去发现。谁说 PHP 只能用于 Web 开发呢?PHP拥有 Swoole 扩展后能做的事情有很多。Swoole 如同其宣传标题所表述的那样:能够重新定义 PHP。

请明确具体需要改写的横线下的句子呀,没有看到相关内容呢。

PHP 饭米粒(phpfamily)是由一群值得信赖的人创建的,希望能给 PHPer 带来一些值得慢慢品味的精神方面的食物!

饭米粒只发原创或授权发表的文章,不转载网上的文章

所发的文章,均可找到原作者进行沟通。

希望各位能够多多给予打赏,将其当作稿费给予文章作者。同时也希望大家能够多多投递稿件。

投稿请联系:

shenzhe163@gmail.com

你可能想看:
« 上一篇
下一篇 »

相关推荐

php实时推送,php实地培训

2026年10月05日

46阅读

php乱码出现问号的原因,php乱码出现问号的原因有哪些

2026年10月05日

39阅读

php一句话木马,一句话木马ctf

2026年10月05日

41阅读

php获取数组长度,php中如何获取数组的长度

2026年10月05日

43阅读

php面试题库及答案,php程序员面试真题库

2026年10月05日

41阅读

phpcontent-type修改,php源码二次修改需要多少钱

2026年10月05日

38阅读