الاستمع
void Worker::listen(void)
تستخدم لتنفيذ الاستماع بعد إنشاء عامل Worker.
تستهدف هذه الطريقة بشكل رئيسي إنشاء مثيلات جديدة من Worker ديناميكياً بعد إطلاق عملية Worker، مما يمكن من الاستماع لعدة منافذ في نفس العملية، ودعم بروتوكولات متعددة. من المهم ملاحظة أن استخدام هذه الطريقة يضيف فقط استماعًا في العملية الحالية، ولن يتم إنشاء عمليات جديدة ديناميكيًا، كما لن يتم استدعاء دالة onWorkerStart.
على سبيل المثال، عند بدء عامل http ثم إنشاء عامل websocket، يمكن لهذه العملية الوصول عبر بروتوكول http، وأيضًا عبر بروتوكول websocket. نظرًا لأن عامل websocket وعامل http في نفس العملية، فبإمكانهما الوصول إلى متغيرات الذاكرة المشتركة، ومشاركة جميع اتصالات socket. يمكن القيام باستقبال طلبات http، ثم تشغيل عميل websocket لإكمال دفع البيانات إلى العميل بشكل مشابه.
تنبيه:
إذا كانت نسخة PHP <= 7.0، فلا تدعم إنشاء Worker بنفس المنفذ في عدة عمليات فرعية. على سبيل المثال، إذا أنشأت عملية A عامل يستمع على المنفذ 2016، فلا يمكن لعملية B إنشاء عامل آخر يستمع على نفس المنفذ 2016، وإلا ستظهر رسالة الخطأ Address already in use. على سبيل المثال، الشيفرة أدناه هي غير قادرة على العمل.
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker();
// 4 عمليات
$worker->count = 4;
// عند بدء كل عملية، يتم إضافة عامل Worker للاستماع في العملية الحالية
$worker->onWorkerStart = function($worker)
{
/**
* عند بدء 4 عمليات، سيتم إنشاء عامل يستمع على المنفذ 2016
* عند تنفيذ worker->listen() ستظهر رسالة خطأ Address already in use
* إذا كانت worker->count=1 فلن يحدث خطأ
*/
$inner_worker = new Worker('http://0.0.0.0:2016');
$inner_worker->onMessage = 'on_message';
// تنفيذ الاستماع. هنا ستظهر رسالة خطأ Address already in use
$inner_worker->listen();
};
$worker->onMessage = 'on_message';
function on_message(TcpConnection $connection, $data)
{
$connection->send("hello\n");
}
// تشغيل العامل
Worker::runAll();
إذا كانت نسخة PHP >= 7.0، يمكنك تعيين Worker->reusePort=true، بحيث يمكن للعمليات الفرعية المتعددة إنشاء عامل بنفس المنفذ. انظر المثال أدناه:
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('text://0.0.0.0:2015');
// 4 عمليات
$worker->count = 4;
// عند بدء كل عملية، يتم إضافة عامل Worker للاستماع في العملية الحالية
$worker->onWorkerStart = function($worker)
{
$inner_worker = new Worker('http://0.0.0.0:2016');
// تعيين إعادة استخدام المنفذ، مما يمكن من إنشاء عمال يستمعون على نفس المنفذ (يتطلب PHP >= 7.0)
$inner_worker->reusePort = true;
$inner_worker->onMessage = 'on_message';
// تنفيذ الاستماع. الاستماع العادي لن يظهر أي خطأ
$inner_worker->listen();
};
$worker->onMessage = 'on_message';
function on_message(TcpConnection $connection, $data)
{
$connection->send("hello\n");
}
// تشغيل العامل
Worker::runAll();
مثال على دفع رسائل فورية من خادم PHP إلى العميل
المبدأ:
-
إنشاء عامل websocket، للحفاظ على اتصال العميل لفترة طويلة.
-
إنشاء عامل text داخلي في عامل websocket.
-
عامل websocket وعامل text في نفس العملية، مما يسهل مشاركة اتصالات العملاء.
-
نظام خلفي مستقل يستخدم بروتوكول النص للتواصل مع عامل text.
-
يقوم عامل text بتشغيل اتصال websocket لإكمال دفع البيانات.
الشيفرة والخطوات
push.php
<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
// إنشاء حاوية عامل جديدة، والاستماع على المنفذ 1234
$worker = new Worker('websocket://0.0.0.0:1234');
/*
* يجب تعيين عدد العمليات هنا إلى 1
*/
$worker->count = 1;
// بعد بدء عملية العامل، يتم إنشاء عامل text لفتح منفذ اتصالات داخلي
$worker->onWorkerStart = function($worker)
{
// فتح منفذ داخلي، لتسهيل دفع البيانات من النظام الداخلي، بتنسيق بروتوكول نص+حرف نقل
$inner_text_worker = new Worker('text://0.0.0.0:5678');
$inner_text_worker->onMessage = function(TcpConnection $connection, $buffer)
{
// مصفوفة $data، تحتوي على uid، تشير إلى أي uid يتم دفع البيانات إليه
$data = json_decode($buffer, true);
$uid = $data['uid'];
// باستخدام workerman، دفع البيانات إلى صفحة uid
$ret = sendMessageByUid($uid, $buffer);
// إرجاع نتيجة الدفع
$connection->send($ret ? 'ok' : 'fail');
};
// ## تنفيذ الاستماع ##
$inner_text_worker->listen();
};
// إضافة خاصية جديدة لحفظ خريطة uid إلى الاتصال
$worker->uidConnections = array();
// دالة رد النداء عند تلقي رسالة من عميل
$worker->onMessage = function(TcpConnection $connection, $data)
{
global $worker;
// التحقق مما إذا كان العميل الحالي قد تم التحقق منه، أي إذا تم تعيين uid
if(!isset($connection->uid))
{
// إذا لم يتم التحقق، فإن أول حزمة تعتبر uid (هنا للتبسيط، لم نقم بالتحقق الحقيقي)
$connection->uid = $data;
/* حفظ خريطة uid إلى الاتصال، مما يسهل العثور على الاتصال عبر uid،
* وتنفيذ دفع البيانات إلى uid معين
*/
$worker->uidConnections[$connection->uid] = $connection;
return;
}
};
// عند قطع اتصال عميل
$worker->onClose = function(TcpConnection $connection)
{
global $worker;
if(isset($connection->uid))
{
// عند قطع الاتصال، يتم حذف الخريطة
unset($worker->uidConnections[$connection->uid]);
}
};
// دفع البيانات إلى جميع المستخدمين المصدقين
function broadcast($message)
{
global $worker;
foreach($worker->uidConnections as $connection)
{
$connection->send($message);
}
}
// دفع البيانات بناءً على uid
function sendMessageByUid($uid, $message)
{
global $worker;
if(isset($worker->uidConnections[$uid]))
{
$connection = $worker->uidConnections[$uid];
$connection->send($message);
return true;
}
return false;
}
// تشغيل جميع العمال
Worker::runAll();
تشغيل خدمة الخلفية
php push.php start -d
شيفرة JavaScript على الواجهة الأمامية لاستقبال الدفع
var ws = new WebSocket('ws://127.0.0.1:1234');
ws.onopen = function(){
var uid = 'uid1';
ws.send(uid);
};
ws.onmessage = function(e){
alert(e.data);
};
شيفرة الدفع من الخلفية
// إنشاء اتصال socket إلى منفذ الدفع الداخلي
$client = stream_socket_client('tcp://127.0.0.1:5678', $errno, $errmsg, 1);
// البيانات المدفوعة، تضمين حقل uid، تشير إلى أنها لدفع هذا uid
$data = array('uid'=>'uid1', 'percent'=>'88%');
// إرسال البيانات، لاحظ أن المنفذ 5678 هو منفذ النص، تحتاج بروتوكولات النص إلى إضافة حرف نقل في نهاية البيانات
fwrite($client, json_encode($data)."\n");
// قراءة نتيجة الدفع
echo fread($client, 8192);