Crear un servicio wss

Pregunta:

¿Cómo crear un servicio wss en Workerman para que los clientes puedan conectarse a través del protocolo wss para comunicarse, por ejemplo, en una mini aplicación de WeChat?

Respuesta:

El protocolo wss es en realidad websocket + SSL, que es el protocolo websocket con una capa de SSL añadida, similar a https (http + SSL).
Por lo tanto, solo necesitas habilitar SSL en la base del protocolo websocket para soportar el protocolo wss.

Método 1: Usar nginx/apache como proxy SSL (recomendado)

Razones para la recomendación

  • Se puede reutilizar el puerto 443, por lo que el cliente no necesita especificar un puerto al conectarse.
  • Manejo centralizado de certificados SSL, se puede reutilizar la configuración del sitio web.
  • Puede balancear la carga.
  • Incluye monitorización de registros.
  • Mejor compatibilidad.

Principios y flujo de comunicación

  1. El cliente inicia una conexión wss a nginx/apache.

  2. Nginx/apache convierte los datos del protocolo wss en datos del protocolo ws y los reenvía al puerto del protocolo websocket de Workerman.

  3. Workerman recibe los datos y realiza el procesamiento lógico de negocios.

  4. Cuando Workerman envía un mensaje al cliente, es el proceso inverso: los datos son convertidos a wss por nginx/apache y enviados al cliente.

Referencia de configuración de nginx

Condiciones y preparativos previos:

  1. Nginx ya está instalado, versión no inferior a 1.3.

  2. Suponga que Workerman escucha en el puerto 8282 (protocolo websocket).

  3. Se ha solicitado un certificado (archivos pem/crt y archivo key), supongamos que se encuentran en /etc/nginx/conf.d/ssl.

  4. Se planea usar nginx para abrir el puerto 443 y proporcionar el servicio de proxy wss (el puerto se puede modificar según sea necesario).

  5. Nginx suele funcionar como servidor web para otros servicios. Para no afectar al sitio original, aquí se usa la dirección dominio.com/wss como punto de entrada del proxy wss. Es decir, la dirección de conexión del cliente será wss://dominio.com/wss.

La configuración de nginx es similar a la siguiente:

server {
  listen 443;
  # Configuración del dominio omitida...

  ssl on;
  ssl_certificate /etc/ssl/server.pem;
  ssl_certificate_key /etc/ssl/server.key;
  ssl_session_timeout 5m;
  ssl_session_cache shared:SSL:50m;
  ssl_protocols SSLv3 SSLv2 TLSv1 TLSv1.1 TLSv1.2;
  ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;

  location /wss
  {
    proxy_pass http://127.0.0.1:8282;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header X-Real-IP $remote_addr;
  }

  # location / {} Otras configuraciones del sitio...
}

Prueba

// El certificado comprobará el dominio, por favor, use un dominio para conectarse. Nota: no escriba el puerto aquí
ws = new WebSocket("wss://dominio.com/wss");

ws.onopen = function() {
    alert("Conexión exitosa");
    ws.send('tom');
    alert("Enviando una cadena al servidor: tom");
};
ws.onmessage = function(e) {
    alert("Mensaje recibido del servidor: " + e.data);
};

Usar apache para proxy wss

También se puede utilizar apache como un proxy wss para reenvíos a Workerman.

Preparativos:

  1. GatewayWorker escucha en el puerto 8282 (protocolo websocket).

  2. Se ha solicitado un certificado SSL, supongamos que se encuentra en /server/httpd/cert/.

  3. Utilizar apache para reenviar el puerto 443 al puerto 8282 especificado.

  4. el archivo httpd-ssl.conf está cargado.

  5. openssl está instalado.

Habilitar el módulo proxy_wstunnel_module

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so

Configurar SSL y proxy

#extra/httpd-ssl.conf
DocumentRoot "/directorio/del/sitio"
ServerName dominio

# Configuración del Proxy
SSLProxyEngine on

ProxyRequests Off
ProxyPass /wss ws://127.0.0.1:8282/wss
ProxyPassReverse /wss ws://127.0.0.1:8282/wss

# Añadir soporte para protocolos SSL, eliminar protocolos inseguros
SSLProtocol all -SSLv2 -SSLv3
# Modificar el conjunto de cifrado como sigue
SSLCipherSuite HIGH:!RC4:!MD5:!aNULL:!eNULL:!NULL:!DH:!EDH:!EXP:+MEDIUM
SSLHonorCipherOrder on
# Configuración de la clave pública del certificado
SSLCertificateFile /server/httpd/cert/your.pem
# Configuración de la clave privada del certificado
SSLCertificateKeyFile /server/httpd/cert/your.key
# Configuración de la cadena del certificado,
SSLCertificateChainFile /server/httpd/cert/chain.pem

Prueba

// El certificado comprobará el dominio, por favor, use un dominio para conectarse. Nota: no hay puerto
ws = new WebSocket("wss://dominio.com/wss");

ws.onopen = function() {
    alert("Conexión exitosa");
    ws.send('tom');
    alert("Enviando una cadena al servidor: tom");
};
ws.onmessage = function(e) {
    alert("Mensaje recibido del servidor: " + e.data);
};

Método 2: Iniciar SSL directamente con Workerman (no recomendado)

Nota
Solo se debe elegir uno entre el proxy SSL nginx/apache y la configuración SSL de Workerman, no se puede habilitar ambos simultáneamente.

Preparativos:

  1. Workerman versión >= 3.3.7.

  2. PHP tiene instalada la extensión openssl.

  3. Se ha solicitado un certificado (archivos pem/crt y archivo key) en cualquier directorio del disco.

Código:

<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';

// El certificado debe ser uno solicitado
$context = array(
    // Más opciones ssl, consulte el manual http://php.net/manual/zh/context.ssl.php
    'ssl' => array(
        // Por favor use rutas absolutas
        'local_cert'        => 'ruta/del/disco/server.pem', // También puede ser un archivo crt
        'local_pk'          => 'ruta/del/disco/server.key',
        'verify_peer'       => false,
        'allow_self_signed' => true, // Necesario si el certificado es autofirmado
    )
);
// Aquí se establece el protocolo websocket (puerto arbitrario, pero debe estar libre de otros programas)
$worker = new Worker('websocket://0.0.0.0:8282', $context);
// Configurar el transporte para habilitar ssl, websocket+ssl es wss
$worker->transport = 'ssl';
$worker->onMessage = function(TcpConnection $con, $msg) {
    $con->send('ok');
};

Worker::runAll();

Con el código anterior, Workerman escucha el protocolo wss, y el cliente puede conectarse a Workerman a través del protocolo wss para lograr comunicación segura.

Prueba

Abre el navegador Chrome, presiona F12 para abrir la consola de depuración, escribe en la sección Console (o coloca el siguiente código en una página html para ejecutarlo con js)

// El certificado comprobará el dominio, por favor, use un dominio para conectarse, nota que hay número de puerto
ws = new WebSocket("wss://dominio.com:8282");
ws.onopen = function() {
    alert("Conexión exitosa");
    ws.send('tom');
    alert("Enviando una cadena al servidor: tom");
};
ws.onmessage = function(e) {
    alert("Mensaje recibido del servidor: " + e.data);
};

Errores comunes
SSL routines:SSL23_GET_CLIENT_HELLO:http request
La causa es que el cliente intenta acceder usando ws://dominio.com, el acceso correcto debería ser wss://dominio.com, es decir, debería acceder con wss.
Este problema a menudo ocurre cuando el puerto originalmente era ws, y de repente se cambia a wss y algunas páginas del cliente no se actualizan, continuando accediendo con ws, lo que resulta en un error.
Este error se puede ignorar, no afecta la conexión normal wss.

Nota:

  1. Si es necesario utilizar el puerto 443, utilice el primer método de proxy nginx/apache para implementar wss.

  2. El puerto wss solo puede ser accedido a través del protocolo wss; ws no puede acceder al puerto wss.

  3. Los certificados generalmente están vinculados al dominio, por lo que al realizar pruebas, asegúrese de que el cliente use un dominio para conectarse, no debe usar una dirección IP.

  4. Si enfrenta problemas de acceso, verifique el firewall del servidor.

  5. Este método requiere PHP versión >= 5.6, ya que la mini aplicación de WeChat requiere tls1.2, y las versiones anteriores a PHP 5.6 no son compatibles con tls1.2.

Artículos relacionados:
Obteniendo la IP real del cliente a través de un proxy
Referencia de las opciones de contexto SSL de Workerman