ouvir
void Worker::listen(void)
Usado para instanciar a escuta do Worker.
Este método é principalmente usado para criar dinamicamente novas instâncias de Worker após o início do processo do Worker, permitindo que o mesmo processo escute várias portas e suporte vários protocolos. É importante notar que, ao usar este método, apenas a escuta é aumentada no processo atual, não haverá criação dinâmica de novos processos, nem será acionado o método onWorkerStart.
Por exemplo, após iniciar um Worker http, instanciar um Worker websocket, este processo pode ser acessado tanto por meio do protocolo http quanto do protocolo websocket. Como o Worker websocket e o Worker http estão no mesmo processo, eles podem acessar variáveis de memória comuns e compartilhar todas as conexões de soquete. Isso pode gerar o efeito de receber uma solicitação http e, em seguida, operar com um cliente websocket para enviar dados semelhantes.
Nota:
Se a versão do PHP for <=7.0, não será possível instanciar o mesmo Worker na mesma porta em vários processos filhos. Por exemplo, se o Processo A criar um Worker para escutar a porta 2016, então o Processo B não poderá mais criar um Worker para escutar a porta 2016, caso contrário, será gerado um erro de "Address already in use". Por exemplo, o código a seguir não funcionará.
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker();
// 4 processos
$worker->count = 4;
// Após cada processo ser iniciado, um novo Worker de escuta será adicionado no processo atual
$worker->onWorkerStart = function($worker)
{
/**
* Os 4 processos iniciados criarão um Worker escutando na porta 2016
* Ao executar worker->listen(), será gerado um erro de "Address already in use"
* Se worker->count=1, não gerará erro
*/
$inner_worker = new Worker('http://0.0.0.0:2016');
$inner_worker->onMessage = 'on_message';
// Escutar. Neste ponto, será gerado um erro de "Address already in use"
$inner_worker->listen();
};
$worker->onMessage = 'on_message';
function on_message(TcpConnection $connection, $data)
{
$connection->send("hello\n");
}
// Executar worker
Worker::runAll();
Se a sua versão do PHP for >=7.0, você pode configurar Worker->reusePort=true, o que permite que vários processos filhos criem o mesmo Worker na mesma porta. Veja o exemplo abaixo:
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$worker = new Worker('text://0.0.0.0:2015');
// 4 processos
$worker->count = 4;
// Após cada processo ser iniciado, um novo Worker de escuta será adicionado no processo atual
$worker->onWorkerStart = function($worker)
{
$inner_worker = new Worker('http://0.0.0.0:2016');
// Setar porta de reuso, é possível criar um Worker escutando a mesma porta (requer PHP>=7.0)
$inner_worker->reusePort = true;
$inner_worker->onMessage = 'on_message';
// Escutar. A escuta normal não gerará erro.
$inner_worker->listen();
};
$worker->onMessage = 'on_message';
function on_message(TcpConnection $connection, $data)
{
$connection->send("hello\n");
}
// Executar worker
Worker::runAll();
Exemplo de envio de mensagens em tempo real do back-end PHP para o cliente
Princípio:
- Estabelecer um Worker websocket, para manter conexões longas com o cliente
- Internamente, o Worker websocket cria um Worker de texto
- O Worker websocket e o Worker de texto estão no mesmo processo, o que permite compartilhar facilmente conexões com o cliente
- Um sistema de back-end PHP independente se comunica com o Worker de texto usando o protocolo de texto
- O Worker de texto opera a conexão do websocket para enviar dados
Código e Passos:
push.php
<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
// Inicializar um contêiner de worker, ouvindo a porta 1234
$worker = new Worker('websocket://0.0.0.0:1234');
/*
* Observe que o número de processos deve ser configurado como 1
*/
$worker->count = 1;
// Após a inicialização do processo do worker, criar um Worker de texto para abrir uma porta de comunicação interna
$worker->onWorkerStart = function($worker)
{
// Abrir uma porta interna para facilitar o envio de dados pelo sistema interno. O protocolo de texto é texto+quebra de linha
$inner_text_worker = new Worker('text://0.0.0.0:5678');
$inner_text_worker->onMessage = function(TcpConnection $connection, $buffer)
{
// O array $data contém o uid, indicando para qual página enviar os dados
$data = json_decode($buffer, true);
$uid = $data['uid'];
// Por meio do workerman, enviar dados para a página com o uid
$ret = sendMessageByUid($uid, $buffer);
// Retornar o resultado do envio
$connection->send($ret ? 'ok' : 'fail');
};
// ## Escutar ##
$inner_text_worker->listen();
};
// Adicionar um novo atributo para armazenar a conexão uid
$worker->uidConnections = array();
// Quando um cliente envia uma mensagem
$worker->onMessage = function(TcpConnection $connection, $data)
{
global $worker;
// Verificar se o cliente atual já foi verificado, ou seja, se o uid foi configurado
if(!isset($connection->uid))
{
// Caso não tenha sido verificado, o primeiro pacote é considerado como uid (aqui, para fins de demonstração, nenhuma verificação real é realizada)
$connection->uid = $data;
/* Armazenar o uid e a conexão, permitindo encontrar a conexão pelo uid, e realizar o envio de dados para o uid específico */
$worker->uidConnections[$connection->uid] = $connection;
return;
}
};
// Quando um cliente se desconecta
$worker->onClose = function(TcpConnection $connection)
{
global $worker;
if(isset($connection->uid))
{
// Ao desconectar, remover o mapeamento
unset($worker->uidConnections[$connection->uid]);
}
};
// Enviar dados para todos os usuários verificados
function broadcast($message)
{
global $worker;
foreach($worker->uidConnections as $connection)
{
$connection->send($message);
}
}
// Enviar dados para um uid específico
function sendMessageByUid($uid, $message)
{
global $worker;
if(isset($worker->uidConnections[$uid]))
{
$connection = $worker->uidConnections[$uid];
$connection->send($message);
return true;
}
return false;
}
// Executar todos os workers
Worker::runAll();
Iniciar o serviço back-end
php push.php start -d
Código JavaScript para receber as notificações no frontend
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);
};
Código para enviar mensagens pelo back-end
// Estabelecer conexão de socket para a porta interna de notificações
$client = stream_socket_client('tcp://127.0.0.1:5678', $errno, $errmsg, 1);
// Dados a serem enviados, incluindo o campo uid, indicando para qual uid enviar
$data = array('uid'=>'uid1', 'percent'=>'88%');
// Enviar dados, observe que a porta 5678 é a porta do protocolo de texto, e o protocolo de texto requer uma quebra de linha ao final dos dados
fwrite($client, json_encode($data)."\n");
// Ler o resultado do envio de notificação
echo fread($client, 8192);