Criar serviço wss
Pergunta:
Como criar um serviço wss com Workerman, para que o cliente possa se conectar e se comunicar através do protocolo wss, por exemplo, conectar o servidor em um mini programa WeChat.
Resposta:
O protocolo wss na verdade é websocket+SSL, que adiciona uma camada SSL ao protocolo websocket, semelhante ao https (http+SSL). Portanto, tudo o que você precisa fazer é ativar o SSL com base no protocolo websocket para suportar o protocolo wss.
Método 1: Usando nginx/apache para proxy SSL (recomendado)
Razões para recomendação
- Pode reutilizar a porta 443, o cliente não precisa especificar a porta ao conectar
- Gerenciamento centralizado de certificados SSL, pode reutilizar a configuração do site
- Pode realizar balanceamento de carga
- Possui monitoramento de logs integrado
- Melhor compatibilidade
Princípio e fluxo de comunicação
- O cliente inicia a conexão wss para o nginx/apache.
- O nginx/apache converte os dados do protocolo wss em dados do protocolo ws e os encaminha para a porta do protocolo websocket do Workerman.
- O Workerman processa os dados recebidos conforme a lógica de negócios.
- Quando o Workerman envia uma mensagem ao cliente, o processo é o inverso: os dados são convertidos pelo nginx/apache para o protocolo wss e enviados ao cliente.
Exemplo de configuração do nginx
Pré-requisitos e preparação:
- O nginx já está instalado, versão não inferior a 1.3.
- Suponha que o Workerman está ouvindo a porta 8282 (protocolo websocket).
- Um certificado já foi solicitado (arquivo pem/crt e arquivo key), e supõe-se que está localizado em /etc/nginx/conf.d/ssl.
- Pretende-se usar o nginx para ativar a porta 443 para fornecer um serviço de proxy wss (a porta pode ser modificada conforme necessário).
- O nginx geralmente funciona como servidor de sites com outros serviços, para não afetar o uso do site original, aqui é utilizada a URL
dominio.com/wsscomo ingresso para o proxy wss. Ou seja, o endereço de conexão do cliente é wss://dominio.com/wss.
A configuração do nginx é semelhante ao seguinte:
server {
listen 443;
# configuração do domínio 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 / {} outras configurações do site...
}
Teste
// O certificado irá verificar o domínio, por favor, use o domínio para se conectar. Note que aqui não se escreve a porta
ws = new WebSocket("wss://dominio.com/wss");
ws.onopen = function() {
alert("Conexão bem-sucedida");
ws.send('tom');
alert("Enviando uma string para o servidor: tom");
};
ws.onmessage = function(e) {
alert("Mensagem recebida do servidor: " + e.data);
};
Usando apache para proxy wss
Você também pode usar o apache como um proxy wss para encaminhar para o Workerman.
Preparação:
- GatewayWorker ouvindo a porta 8282 (protocolo websocket).
- Um certificado ssl já foi solicitado, supõe-se que esteja localizado em /server/httpd/cert/.
- O apache irá encaminhar a porta 443 para a porta 8282 especificada.
- httpd-ssl.conf já foi carregado.
- openssl já está instalado.
Habilitar o módulo proxy_wstunnel_module
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
Configuração SSL e proxy
#extra/httpd-ssl.conf
DocumentRoot "/diretório/do/site"
ServerName dominio
# Configuração do Proxy
SSLProxyEngine on
ProxyRequests Off
ProxyPass /wss ws://127.0.0.1:8282/wss
ProxyPassReverse /wss ws://127.0.0.1:8282/wss
# Adicione suporte para protocolos SSL, remova protocolos inseguros
SSLProtocol all -SSLv2 -SSLv3
# Modifique a suíte de criptografia como segue
SSLCipherSuite HIGH:!RC4:!MD5:!aNULL:!eNULL:!NULL:!DH:!EDH:!EXP:+MEDIUM
SSLHonorCipherOrder on
# Configuração da chave pública do certificado
SSLCertificateFile /server/httpd/cert/your.pem
# Configuração da chave privada do certificado
SSLCertificateKeyFile /server/httpd/cert/your.key
# Configuração da cadeia de certificados,
SSLCertificateChainFile /server/httpd/cert/chain.pem
Teste
// O certificado irá verificar o domínio, por favor, use o domínio para se conectar. Note que não há porta
ws = new WebSocket("wss://dominio.com/wss");
ws.onopen = function() {
alert("Conexão bem-sucedida");
ws.send('tom');
alert("Enviando uma string para o servidor: tom");
};
ws.onmessage = function(e) {
alert("Mensagem recebida do servidor: " + e.data);
};
Método 2: Usando diretamente o Workerman para ativar SSL (não recomendado)
Atenção
Use o proxy SSL nginx/apache ou a configuração SSL do Workerman, não ambos ao mesmo tempo.
Preparação:
- Versão do Workerman >= 3.3.7.
- O PHP tem a extensão openssl instalada.
- Um certificado já foi solicitado (arquivo pem/crt e arquivo key) e está em qualquer diretório no disco.
Código:
<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
// O certificado deve ser um certificado solicitado
$context = array(
// Mais opções ssl podem ser encontradas na documentação http://php.net/manual/zh/context.ssl.php
'ssl' => array(
// Por favor, use o caminho absoluto
'local_cert' => 'caminho/do/disco/server.pem', // também pode ser um arquivo crt
'local_pk' => 'caminho/do/disco/server.key',
'verify_peer' => false,
'allow_self_signed' => true, // se for um certificado autoassinado, esta opção precisa ser ativada
)
);
// Aqui é configurado o protocolo websocket (a porta pode ser qualquer um, mas deve garantir que não esteja ocupada por outros programas)
$worker = new Worker('websocket://0.0.0.0:8282', $context);
// Definindo o transport como ssl, websocket + ssl ou wss
$worker->transport = 'ssl';
$worker->onMessage = function(TcpConnection $con, $msg) {
$con->send('ok');
};
Worker::runAll();
Com o código acima, o Workerman estará ouvindo o protocolo wss, e o cliente poderá se conectar ao Workerman via wss para realizar comunicação segura e em tempo real.
Teste
Abra o navegador Chrome, pressione F12 para abrir a console de depuração e digite (ou coloque o código abaixo em uma página HTML para executá-lo com js)
// O certificado irá verificar o domínio, por favor, use o domínio para se conectar, note que aqui tem um número de porta
ws = new WebSocket("wss://dominio.com:8282");
ws.onopen = function() {
alert("Conexão bem-sucedida");
ws.send('tom');
alert("Enviando uma string para o servidor: tom");
};
ws.onmessage = function(e) {
alert("Mensagem recebida do servidor: " + e.data);
};
Erros comuns
SSL routines:SSL23_GET_CLIENT_HELLO:http request
A causa é que o cliente está tentando acessar ws://dominio.com, a URL correta deveria ser wss://dominio.com, ou seja, deve começar com wss. A maior parte dos casos em que isso ocorre é que a porta originalmente era ws, e repentinamente se tornou wss, certos clientes em algumas páginas não foram atualizados e ainda acessam via ws, resultando em erro.
Esse erro pode ser ignorado, não afeta a conexão normal wss.
Atenção:
- Se for necessário usar a porta 443, use o primeiro método de proxy com nginx/apache para implementar wss.
- A porta wss só pode ser acessada através do protocolo wss, ws não pode acessar a porta wss.
- O certificado geralmente é vinculado ao domínio, portanto, ao testar, o cliente deve usar o domínio, não deve usar o IP.
- Se houver problemas de acesso, verifique o firewall do servidor.
- Este método requer PHP versão >= 5.6, porque o mini programa WeChat requer tls1.2, e versões do PHP abaixo de 5.6 não suportam tls1.2.
Artigos relacionados:
Obter o IP real do cliente através do proxy
Referência das opções de contexto SSL do Workerman