listen
void Worker::listen(void)
Used to perform listening after instantiating Worker.
This method is mainly used to dynamically create new Worker instances after the Worker process is started. It allows the same process to listen on multiple ports and supports various protocols. It should be noted that this method only adds listening in the current process, and will not dynamically create new processes nor trigger the onWorkerStart method.
For example, after an http Worker is started, if a websocket Worker is instantiated, this process can be accessed both via the http protocol and the websocket protocol. Since the websocket Worker and the http Worker are in the same process, they can access shared memory variables and share all socket connections. This enables the ability to receive http requests and then operate on websocket clients to achieve similar effects of pushing data to clients.
Note:
If the PHP version is <= 7.0, it does not support instantiating a Worker on the same port in multiple child processes. For example, if process A creates a Worker that listens on port 2016, then process B cannot create another Worker listening on port 2016, otherwise, it will report an Address already in use error. For example, the following code is not able to run.
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker();
// 4 processes
$worker->count = 4;
// Create a new Worker to listen in the current process after each process starts
$worker->onWorkerStart = function($worker)
{
/**
* When all 4 processes start, they all create a Worker listening on port 2016
* When executing worker->listen(), it will report Address already in use error
* If worker->count=1, it will not report an error
*/
$inner_worker = new Worker('http://0.0.0.0:2016');
$inner_worker->onMessage = 'on_message';
// Execute listen. This will report the Address already in use error
$inner_worker->listen();
};
$worker->onMessage = 'on_message';
function on_message(TcpConnection $connection, $data)
{
$connection->send("hello\n");
}
// Run worker
Worker::runAll();
If your PHP version is >= 7.0, you can set Worker->reusePort=true, which allows multiple child processes to create Workers on the same port. See the example below:
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('text://0.0.0.0:2015');
// 4 processes
$worker->count = 4;
// Create a new Worker to listen in the current process after each process starts
$worker->onWorkerStart = function($worker)
{
$inner_worker = new Worker('http://0.0.0.0:2016');
// Set port reuse, allowing the creation of Workers listening on the same port (requires PHP >= 7.0)
$inner_worker->reusePort = true;
$inner_worker->onMessage = 'on_message';
// Execute listen. This will listen normally and won't report an error
$inner_worker->listen();
};
$worker->onMessage = 'on_message';
function on_message(TcpConnection $connection, $data)
{
$connection->send("hello\n");
}
// Run worker
Worker::runAll();
Example of PHP backend pushing messages to the client in real time
Principle:
- Establish a websocket Worker to maintain long connections with clients.
- Inside the websocket Worker, create a text Worker.
- The websocket Worker and the text Worker are in the same process, making it easy to share client connections.
- A separate PHP backend system communicates with the text Worker via the text protocol.
- The text Worker operates the websocket connections to push data.
Code and Steps
push.php
<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
// Initialize a worker container, listening on port 1234
$worker = new Worker('websocket://0.0.0.0:1234');
/*
* Note that the number of processes must be set to 1 here
*/
$worker->count = 1;
// Create a text Worker after the worker process starts to open an internal communication port
$worker->onWorkerStart = function($worker)
{
// Open an internal port for the internal system to push data, formatted for Text protocol
$inner_text_worker = new Worker('text://0.0.0.0:5678');
$inner_text_worker->onMessage = function(TcpConnection $connection, $buffer)
{
// Format of the $data array, which contains uid, indicating which uid's page to push data to
$data = json_decode($buffer, true);
$uid = $data['uid'];
// Push data to the uid's page via workerman
$ret = sendMessageByUid($uid, $buffer);
// Return the push result
$connection->send($ret ? 'ok' : 'fail');
};
// ## Execute listen ##
$inner_text_worker->listen();
};
// New property to save the mapping of uid to connection
$worker->uidConnections = array();
// Callback function executed when a client sends a message
$worker->onMessage = function(TcpConnection $connection, $data)
{
global $worker;
// Check if the current client has already been verified, i.e., whether the uid has been set
if(!isset($connection->uid))
{
// If not verified, take the first package as the uid (here for demonstration convenience, no real verification done)
$connection->uid = $data;
/* Save the uid to connection mapping, allowing easy lookup of connection by uid,
* To achieve targeted data push to a specific uid
*/
$worker->uidConnections[$connection->uid] = $connection;
return;
}
};
// When a client connection is closed
$worker->onClose = function(TcpConnection $connection)
{
global $worker;
if(isset($connection->uid))
{
// Remove mapping on connection closure
unset($worker->uidConnections[$connection->uid]);
}
};
// Broadcast messages to all verified users
function broadcast($message)
{
global $worker;
foreach($worker->uidConnections as $connection)
{
$connection->send($message);
}
}
// Push data to a specific uid
function sendMessageByUid($uid, $message)
{
global $worker;
if(isset($worker->uidConnections[$uid]))
{
$connection = $worker->uidConnections[$uid];
$connection->send($message);
return true;
}
return false;
}
// Run all workers
Worker::runAll();
Start the backend service
php push.php start -d
Front end JavaScript code to receive push messages
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);
};
Backend code to push messages
// Establish a socket connection to the internal push port
$client = stream_socket_client('tcp://127.0.0.1:5678', $errno, $errmsg, 1);
// Data to be pushed, includes uid field, indicating which uid to push to
$data = array('uid'=>'uid1', 'percent'=>'88%');
// Send data, note that port 5678 is for the Text protocol, which requires a newline at the end of the data
fwrite($client, json_encode($data)."\n");
// Read the push result
echo fread($client, 8192);