Como personalizar um protocolo
Na verdade, é bastante simples criar seu próprio protocolo. Protocolos simples geralmente consistem em duas partes:
- Identificador para delimitar os dados
- Definição do formato dos dados
Um exemplo
Definição do protocolo
Vamos assumir que o identificador que delimita os dados é a quebra de linha "\n" (observe que os dados da solicitação em si não podem conter a quebra de linha), e o formato dos dados é Json. Abaixo está um exemplo de um pacote de solicitação que segue essas regras.
{"type":"message","content":"hello"}
Observe que no final dos dados da solicitação acima, há um caractere de quebra de linha (representado em PHP com uma string de aspas duplas "\n"), indicando o término da solicitação.
Etapas de implementação
No Workerman, se quisermos implementar o protocolo acima, considerando que o nome do protocolo é JsonNL e o projeto é chamado de MyApp, precisaremos seguir os passos abaixo:
-
Colocar o arquivo de protocolo na pasta Protocols do projeto, por exemplo, o arquivo ficará em MyApp/Protocols/JsonNL.php.
-
Implementar a classe JsonNL com o namespace
Protocols;
. Esta classe deve obrigatoriamente implementar três métodos estáticos: input, encode e decode.
Observação: O Workerman chamará automaticamente esses três métodos estáticos para realizar a fragmentação, empacotamento e desempacotamento. Consulte a explicação detalhada do fluxo de execução abaixo.
Fluxo de interação entre o Workerman e a classe de protocolo
- Suponha que o cliente envie um pacote de dados para o servidor. O servidor, ao receber os dados (que podem ser somente uma parte dos dados), imediatamente chamará o método
input
do protocolo para verificar o comprimento do pacote. O métodoinput
retornará o comprimento$length
para o framework Workerman. - Após obter o valor de
$length
, o Workerman verificará se o buffer de dados atual já recebeu dados de comprimento$length
. Se ainda não recebeu, o Workerman continuará aguardando dados até que o comprimento do buffer seja igual ou superior a$length
. - Quando o comprimento do buffer for suficiente, o Workerman extrairá (ou seja, fragmentação) os dados do buffer com o comprimento
$length
e chamará o métododecode
do protocolo para realizar o desempacotamento. Os dados desempacotados serão armazenados em$data
. - Após o desempacotamento, o Workerman passará os dados
$data
para a camada de negócios por meio da chamada de retornoonMessage($connection, $data)
. Na funçãoonMessage
, a aplicação pode usar a variável$data
para obter os dados completos e desempacotados enviados pelo cliente. - Quando a camada de negócios precisa enviar dados para o cliente através do método
$connection->send($buffer)
, o Workerman automaticamente utilizará o métodoencode
do protocolo para empacotar o$buffer
antes de enviá-lo para o cliente.
Implementação específica
Implementação de MyApp/Protocols/JsonNL.php
namespace Protocols;
class JsonNL
{
public static function input($buffer)
{
// Encontra a posição do caractere de quebra de linha "\n"
$pos = strpos($buffer, "\n");
// Se não houver caractere de quebra de linha, o comprimento do pacote não pode ser determinado, retorna 0 e aguarda mais dados
if($pos === false)
{
return 0;
}
// Se houver caractere de quebra de linha, retorna o comprimento atual do pacote (incluindo o caractere de quebra de linha)
return $pos+1;
}
public static function encode($buffer)
{
// Serializa em JSON e adiciona a quebra de linha como marcador de término da solicitação
return json_encode($buffer)."\n";
}
public static function decode($buffer)
{
// Remove a quebra de linha e decodifica para um array
return json_decode(trim($buffer), true);
}
}
Com isso, o protocolo JsonNL está implementado e pode ser utilizado no projeto MyApp. Um exemplo de uso pode ser visto abaixo.
Arquivo: MyApp\start.php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
$json_worker = new Worker('JsonNL://0.0.0.0:1234');
$json_worker->onMessage = function(TcpConnection $connection, $data) {
// $data contém os dados enviados pelo cliente, e esses dados já foram processados pelo JsonNL::decode
echo $data;
// Os dados enviados através do $connection->send serão automaticamente empacotados com o método JsonNL::encode e enviados para o cliente
$connection->send(array('code'=>0, 'msg'=>'ok'));
};
Worker::runAll();
Observação:
O Workerman tentará carregar o protocolo sob o namespaceProtocols
, portanto, ao utilizarnew Worker('JsonNL://0.0.0.0:1234')
, o Workerman tentará carregar o protocoloProtocols\JsonNL
. Se ocorrer um erro comoClass 'Protocols\JsonNL' not found
, consulte a implementação do carregamento automático para resolver esse problema.
Descrição da interface do protocolo
As classes de protocolo desenvolvidas no Workerman devem implementar obrigatoriamente três métodos estáticos: input, encode e decode. A descrição da interface do protocolo está disponível no arquivo Workerman/Protocols/ProtocolInterface.php, conforme mostrado abaixo:
namespace Workerman\Protocols;
use \Workerman\Connection\ConnectionInterface;
interface ProtocolInterface
{
public static function input($recv_buffer, ConnectionInterface $connection);
public static function decode($recv_buffer, ConnectionInterface $connection);
public static function encode($data, ConnectionInterface $connection);
}
Observação:
O Workerman não exige estritamente que a classe de protocolo seja baseada na ProtocolInterface. Na prática, a classe do protocolo só precisa ter os três métodos estáticos: input, encode e decode.