在 Laravel 中集成 Swoole 实现 WebSocket 服务器
创建 WebSocketService 类
关于 LaravelS 扩展包我们之前在介绍基于 Swoole 实现 HTTP 服务器的时候已经提到过了,这里不再赘述,若未安装可以参考LaravelS基于Swoole实现高性能 HTTP 服务器教程进行安装和配置,要基于该扩展包实现 WebSocket 服务器,首先首先需要创建一个实现了 Hhxsv5\LaravelS\Swoole\WebSocketHandlerInterface 接口的 WebSocketService 类:
<?php
namespace App\Services;
use Hhxsv5\LaravelS\Swoole\WebSocketHandlerInterface;
use Illuminate\Support\Facades\Log;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;
class WebSocketService implements WebSocketHandlerInterface
{
public function __construct()
{
}
// 连接建立时触发
public function onOpen(Server $server, Request $request)
{
// 在触发 WebSocket 连接建立事件之前,Laravel 应用初始化的生命周期已经结束,你可以在这里获取 Laravel 请求和会话数据
// 调用 push 方法向客户端推送数据,fd 是客户端连接标识字段
Log::info('WebSocket 连接建立');
$server->push($request->fd, 'Welcome to WebSocket Server built on LaravelS');
}
// 收到消息时触发
public function onMessage(Server $server, Frame $frame)
{
// 调用 push 方法向客户端推送数据
//$frame->fd fd绑定客户端传过来的标识uid $frame->data data 发送的内容
$server->push($frame->fd, 'This is a message sent from WebSocket Server at ' . date('Y-m-d H:i:s'));
}
// 关闭连接时触发
public function onClose(Server $server, $fd, $reactorId)
{
Log::info('WebSocket 连接关闭');
}
}
在这个 WebSocket 服务器类中,需要实现接口中声明的方法,其实就是 WebSocket 通信事件的回调函数,和上篇教程(基于 Swoole 实现简单的 WebSocket 服务器及客户端
)介绍的原生实现基本一致,只是通过类进行了封装而已。
修改配置文件 接下来,打开配置文件 config/laravels.php,启用 WebSocket 通信并将刚刚创建的服务器类配置到对应的配置项:
'websocket' => [
'enable' => true,
'handler' => \App\Services\WebSocketService::class,
],
我们还可以在 swoole 配置项中配置 WebSocket 长连接的强制关闭逻辑:
'swoole' => [
...
// 每隔 60s 检测一次所有连接,如果某个连接在 600s 内都没有发送任何数据,则关闭该连接
'heartbeat_idle_time' => 600,
'heartbeat_check_interval' => 60,
...
],
此外,记得在对应 Laravel 项目根目录下 .env 环境配置文件中设置如下配置项:
LARAVELS_LISTEN_IP=workspace // 这里的 IP 需要和 nginx upstream 中配置的监听 IP 保持一致
LARAVELS_DAEMONIZE=true
演示基于 Laravel 的 WebSocket 通信 在 Laravel 项目根目下启动 Swoole 服务器:
php bin/laravels start
启动成功后,就可以看到基于 Swoole 的 HTTP 服务器和 WebSocket 服务器信息了:
然后,我们基于上一篇教程(
基于 Swoole 实现简单的 WebSocket 服务器及客户端
)创建的 WebSocket 客户端,将其中的 WebSocket Server IP 和端口修改如下:
// 初始化 WebSocket 客户端套接字并建立与服务器的连接
var socket = new WebSocket("ws://todo-s.test/ws");
点击「确定」,即可开始建立与 Laravel WebSocket 服务器的通信:
在开发者工具栏 Network->WS 标签页同样可以看到基于 HTTP 协议的 Websocket 通信握手和建立过程:
如果我们在输入框中发送消息,即可触发 WebSocket 服务器推送消息给客户端:
同时,在 storage/logs 目录下也可以看到通信连接建立与断开的日志信息:
[2019-05-22 13:55:02] local.INFO: WebSocket 连接建立
[2019-05-22 13:55:10] local.INFO: WebSocket 连接关闭
[2019-05-22 13:56:25] local.INFO: WebSocket 连接建立
[2019-05-22 13:57:25] local.INFO: WebSocket 连接关闭
[2019-05-22 14:01:18] local.INFO: WebSocket 连接建立
[2019-05-22 14:02:20] local.INFO: WebSocket 连接关闭
简易聊天室:http://admin.chencong.fun 打开两个窗口 进行演示:
上代码
<?php
namespace App\Services;
use Hhxsv5\LaravelS\Swoole\WebSocketHandlerInterface;
use Illuminate\Support\Facades\Log;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
use Swoole\WebSocket\Server;
class WebSocketService implements WebSocketHandlerInterface
{
public function __construct()
{
Log::info('进入初始放法');
}
// 连接建立时触发
public function onOpen(Server $server, Request $request)
{
// 在触发 WebSocket 连接建立事件之前,Laravel 应用初始化的生命周期已经结束,你可以在这里获取 Laravel 请求和会话数据
// 调用 push 方法向客户端推送数据,fd 是客户端连接标识字段
Log::info("标示id:".$request->fd.'WebSocket 连接建立');
$server->push($request->fd, 'WebSocket 连接建立'.$request->fd);
}
// 收到消息时触发
public function onMessage(Server $server, Frame $frame)
{
/*$frame->data 传输时可以传json字符串 */
$data=json_decode($frame->data,true);
Log::info("标示id:".$frame->fd." 用户:".$data['username']." 发送消息:".$data['msg']);
/* $server->connections所有的用户id都存放在里面 */
foreach ($server->connections as $fd ){
if($fd==$frame->fd){
// 调用 push 方法向客户端推送数据
$server->push($fd, date('Y-m-d H:i:s').'我('.$data['username'].' 标示id:'.$frame->fd.')发送的消息:'.$data['msg'] );
}else{
$server->push($fd, date('Y-m-d H:i:s').'对方('.$data['username'].' 标示id:'.$frame->fd.')发送的消息:'.$data['msg']);
}
}
}
// 关闭连接时触发
public function onClose(Server $server, $fd, $reactorId)
{
Log::info("标示id:".$fd.'WebSocket 连接关闭'.$reactorId);
// $server->push($fd, "我(标示id:".$sfd.")WebSocket 连接关闭 reactorId:".$reactorId);
}
}
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Chat Client</title>
</head>
<body>
<script>
window.onload = function () {
var nick = prompt("输入用户名");
var input = document.getElementById("input");
input.focus();
// 初始化客户端套接字并建立连接
// var socket = new WebSocket("ws://admin.chencong.fun//:5200");
var socket = new WebSocket("ws://admin.chencong.fun/ws");
// var socket = new WebSocket("ws://admin.chencong.fun/Swoole");
// 连接建立时触发
socket.onopen = function (event) {
console.log("Connection open ...");
}
// 接收到服务端推送时执行
socket.onmessage = function (event) {
var msg = event.data;
var node = document.createTextNode(msg);
var div = document.createElement("div");
div.appendChild(node);
document.body.insertBefore(div, input);
input.scrollIntoView();
};
// 连接关闭时触发
socket.onclose = function (event) {
console.log("Connection closed ...");
}
input.onchange = function () {
var data={
'username':nick,
'msg':input.value
// 'pid':pid
};
// var msg = nick + ": " + input.value;
var msg=JSON.stringify(data);
// 将输入框变更信息通过 send 方法发送到服务器
socket.send(msg);
input.value = "";
};
}
</script>
<input id="input" style="width: 100%;">
</body>
</html>
猜你喜欢
黑客、后门
阅读 1572留下的网站后门,可以作什么?
swoole 极简聊天室
阅读 1265五分钟教你写超简单的swoole聊天室
Laravel验证码
阅读 712Composer生成Laravel验证码
Resultful API规范
阅读 811什么是resultful
Layui富文本视频等多功能
阅读 702Layui富文本多功能添加
基于 Swoole 实现简单的 WebSocket 服务器及客户端
阅读 1319基于 Swoole 实现简单的 WebSocket 服务器及客户端
微擎常用记录
阅读 752微擎常用记录
PHP定时任务
阅读 1729PHP框架Laravel定时任务的实现