Cómo personalizar un protocolo
De hecho, crear tu propio protocolo es una tarea bastante sencilla. Un protocolo simple generalmente consta de dos partes:
- Un identificador que distingue los límites de los datos
- Definición del formato de los datos
Un ejemplo
Definición del protocolo
Aquí asumimos que el identificador que distingue los límites de los datos es el salto de línea "\n" (nota: los datos de la solicitud en sí no pueden contener saltos de línea), y el formato de los datos es Json. Por ejemplo, el siguiente es un paquete de solicitud que cumple con esta regla.
{"type":"message","content":"hello"}
Nota que al final de los datos de la solicitud hay un carácter de salto de línea (representado en PHP por la cadena de comillas dobles "\n"), que indica el final de una solicitud.
Pasos de implementación
Para implementar el protocolo anterior en Workerman, suponiendo que el nombre del protocolo es JsonNL y el proyecto se llama MyApp, se deben seguir estos pasos:
-
Coloca el archivo del protocolo en la carpeta Protocols del proyecto, por ejemplo, el archivo MyApp/Protocols/JsonNL.php.
-
Implementa la clase JsonNL, utilizando
namespace Protocols;como espacio de nombres, y debe implementar tres métodos estáticos: input, encode, decode.
Nota: Workerman llamará automáticamente a estos tres métodos estáticos para manejar la fragmentación, desempaquetado y empaquetado. Consulta la siguiente descripción del flujo de ejecución.
Flujo de interacción entre Workerman y la clase del protocolo
- Supongamos que un cliente envía un paquete de datos al servidor. Una vez que el servidor recibe los datos (puede que sean datos parciales), llamará inmediatamente al método
inputdel protocolo para comprobar la longitud del paquete. El métodoinputdevuelve el valor de longitud$lengthal marco de Workerman. - El marco de Workerman, al recibir este valor
$length, determina si en el búfer de datos actualmente se han recibido datos de longitud$length. Si no es así, seguirá esperando datos hasta que la longitud de los datos en el búfer sea mayor o igual a$length. - Una vez que la longitud de los datos en el búfer es suficiente, Workerman extraerá del búfer los datos de longitud
$length(es decir, fragmentación) y llamará al métododecodedel protocolo para desempaquetar, devolviendo los datos como$data. - Después de desempaquetar, Workerman pasará los datos
$dataa la funcionalidad de callbackonMessage($connection, $data), permitiendo que la lógica de negocio use la variable$datapara obtener los datos completos y desempaquetados enviados por el cliente. - Cuando la lógica de negocio dentro de
onMessagenecesita enviar datos al cliente llamando al método$connection->send($buffer), Workerman utilizará automáticamente el métodoencodedel protocolo para empaquetar$bufferantes de enviarlo al cliente.
Implementación específica
Implementación de MyApp/Protocols/JsonNL.php
namespace Protocols;
class JsonNL
{
/**
* Verifica la integridad del paquete
* Si se puede obtener la longitud del paquete, devuelve la longitud en el búfer; de lo contrario, devuelve 0 para continuar esperando datos
* Si hay un problema con el protocolo, puede devolver -1, lo que resultará en el cierre de la conexión del cliente actual
* @param string $buffer
* @return int
*/
public static function input($buffer)
{
// Encuentra la posición del carácter de salto de línea "\n"
$pos = strpos($buffer, "\n");
// Si no hay salto de línea, no se puede conocer la longitud del paquete, retorna 0 para continuar esperando datos
if($pos === false)
{
return 0;
}
// Hay un salto de línea; devuelve la longitud del paquete actual (incluyendo el salto de línea)
return $pos + 1;
}
/**
* Empaqueta; se llamará automáticamente al enviar datos al cliente
* @param string $buffer
* @return string
*/
public static function encode($buffer)
{
// Serializa a JSON y añade un salto de línea como marca del final de la solicitud
return json_encode($buffer) . "\n";
}
/**
* Desempaqueta; se llama automáticamente cuando la cantidad de bytes de datos recibidos es igual al valor devuelto por input (un valor mayor que 0)
* Y se pasa como parámetro $data a la función de callback onMessage
* @param string $buffer
* @return string
*/
public static function decode($buffer)
{
// Elimina el salto de línea y lo convierte de nuevo en un arreglo
return json_decode(trim($buffer), true);
}
}
Hasta este punto, el protocolo JsonNL está implementado y puede ser utilizado en el proyecto MyApp, como se muestra a continuación.
Archivo: 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 son los datos enviados por el cliente, ya han sido procesados por JsonNL::decode
echo $data;
// Los datos de $connection->send se empaquetarán automáticamente llamando al método JsonNL::encode antes de enviarlos al cliente
$connection->send(array('code'=>0, 'msg'=>'ok'));
};
Worker::runAll();
...
Nota
Workerman intentará cargar los protocolos en el espacio de nombresProtocols, por ejemplo,new Worker('JsonNL://0.0.0.0:1234')intentará cargar el protocoloProtocols\JsonNL.
Si muestra el errorClass 'Protocols\JsonNL' not found, consulta Carga automática para implementar la carga automática.
Descripción de la interfaz del protocolo
Las clases de protocolo desarrolladas en Workerman deben implementar tres métodos estáticos: input, encode, decode. La descripción de la interfaz del protocolo se puede encontrar en Workerman/Protocols/ProtocolInterface.php, definida como sigue:
namespace Workerman\Protocols;
use \Workerman\Connection\ConnectionInterface;
/**
* Interfaz de Protocolo
* @author walkor <walkor@workerman.net>
*/
interface ProtocolInterface
{
/**
* Utilizado para fragmentar en el recv_buffer recibido
*
* Si se puede obtener la longitud del paquete de solicitud en $recv_buffer, devuelve la longitud total del paquete
* De lo contrario, devuelve 0, indicando que se necesitan más datos para obtener la longitud del paquete de solicitud actual
* Si devuelve -1, eso indica que hay un error en la solicitud, lo que provocará el cierre de la conexión
*
* @param ConnectionInterface $connection
* @param string $recv_buffer
* @return int
*/
public static function input($recv_buffer, ConnectionInterface $connection);
/**
* Utilizado para desempaquetar la solicitud
*
* Si el valor devuelto por input es mayor que 0 y Workerman ha recibido suficientes datos, se llama automáticamente a decode
* Luego se activa el callback onMessage, y los datos decodificados de decode se pasan como segundo parámetro al callback onMessage
* Es decir, cuando se recibe una solicitud completa del cliente, se llamará automáticamente a decode para descomprimir, sin necesidad de invocarlo manualmente en el código de negocio
* @param ConnectionInterface $connection
* @param string $recv_buffer
*/
public static function decode($recv_buffer, ConnectionInterface $connection);
/**
* Utilizado para empaquetar la solicitud
*
* Cuando se necesita enviar datos al cliente, es decir, al invocar $connection->send($data);
* Se llamará automáticamente a encode para empaquetar $data, convirtiéndolo en un formato de datos conforme al protocolo antes de enviarlo al cliente
* Es decir, los datos enviados al cliente se empaquetarán automáticamente con encode, sin necesidad de llamarlo manualmente en el código de negocio
* @param ConnectionInterface $connection
* @param mixed $data
*/
public static function encode($data, ConnectionInterface $connection);
}
Nota:
No se exige estrictamente que las clases del protocolo en Workerman implementen ProtocolInterface, de hecho, cualquier clase de protocolo que contenga los tres métodos estáticos input, encode y decode será suficiente.