Создание wss сервиса
Вопрос:
Как создать wss сервис в Workerman, чтобы клиенты могли подключаться и общаться через wss, например, в приложении WeChat Mini Program?
Ответ:
Протокол wss на самом деле является websocket+SSL, то есть это добавление слоя SSL к протоколу websocket, аналогично https (http+SSL). Поэтому достаточно включить SSL на основе протокола websocket, чтобы поддерживать протокол wss.
Метод 1. Использование nginx/apache для проксирования SSL (рекомендуется)
Причины для рекомендации
- Можно использовать 443 порт, клиентам не нужно указывать порт при подключении
- Централизованное управление SSL сертификатами, возможность повторного использования конфигурации сайта
- Возможность балансировки нагрузки
- Встроенное логирование и мониторинг
- Лучшая совместимость
Принципы и процесс связи
-
Клиент инициирует wss соединение с nginx/apache
-
nginx/apache преобразует данные протокола wss в данные протокола ws и пересылает их на порт websocket-протокола Workerman
-
Workerman получает данные и обрабатывает бизнес-логику
-
Когда Workerman отправляет сообщения клиенту, происходит обратный процесс, данные преобразуются через nginx/apache в протокол wss и затем отправляются клиенту
Пример конфигурации nginx
Предварительные условия и подготовка:
-
Установлен nginx, версия не ниже 1.3
-
Предполагаем, что Workerman слушает на порту 8282 (протокол websocket)
-
SSL сертификат (файлы pem/crt и key) предположительно находятся в каталоге /etc/nginx/conf.d/ssl
-
Планируется использовать nginx для предоставления wss прокси на порту 443 (порт можно изменить по необходимости)
-
Обычно nginx работает как веб-сервер для других услуг, чтобы не повлиять на работу существующего сайта, здесь используется адрес
домен.com/wssв качестве прокси для wss. То есть адрес подключения клиента будет wss://домен.com/wss
Конфигурация nginx будет выглядеть примерно так:
server {
listen 443;
# Конфигурация домена опущена...
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 / {} Другие конфигурации сайта...
}
Тест
// Сертификат проверяет доменное имя, пожалуйста, используйте домен для подключения. Обратите внимание, что порт не указан
ws = new WebSocket("wss://домен.com/wss");
ws.onopen = function() {
alert("Подключение успешно");
ws.send('tom');
alert("Отправка строки 'tom' на сервер");
};
ws.onmessage = function(e) {
alert("Получено сообщение с сервера: " + e.data);
};
Использование apache для проксирования wss
Также можно использовать apache в качестве прокси для wss, передавая запросы на Workerman.
Подготовка:
-
GatewayWorker слушает порт 8282 (протокол websocket)
-
SSL сертификат уже получен, предположительно находится в /server/httpd/cert/
-
Использование apache для перенаправления порта 443 на указанный порт 8282
-
httpd-ssl.conf загружен
-
Установлен openssl
Включение модуля proxy_wstunnel_module
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
Настройка SSL и прокси
#extra/httpd-ssl.conf
DocumentRoot "/каталог/вашего/сайта"
ServerName домен
# Настройки прокси
SSLProxyEngine on
ProxyRequests Off
ProxyPass /wss ws://127.0.0.1:8282/wss
ProxyPassReverse /wss ws://127.0.0.1:8282/wss
# Добавить поддержку SSL протоколов, убрать небезопасные протоколы
SSLProtocol all -SSLv2 -SSLv3
# Изменить шифры как следует
SSLCipherSuite HIGH:!RC4:!MD5:!aNULL:!eNULL:!NULL:!DH:!EDH:!EXP:+MEDIUM
SSLHonorCipherOrder on
# Настройка открытого ключа сертификата
SSLCertificateFile /server/httpd/cert/your.pem
# Настройка закрытого ключа сертификата
SSLCertificateKeyFile /server/httpd/cert/your.key
# Настройка цепочки сертификатов
SSLCertificateChainFile /server/httpd/cert/chain.pem
Тест
// Сертификат проверяет доменное имя, пожалуйста, используйте домен для подключения. Обратите внимание, что порт не указан
ws = new WebSocket("wss://домен.com/wss");
ws.onopen = function() {
alert("Подключение успешно");
ws.send('tom');
alert("Отправка строки 'tom' на сервер");
};
ws.onmessage = function(e) {
alert("Получено сообщение с сервера: " + e.data);
};
Метод 2. Прямое использование Workerman для установки SSL (не рекомендуется)
Внимание
Выберите либо прокси SSL с помощью nginx/apache, либо настройку SSL в Workerman, не используйте их одновременно.
Подготовка:
-
Версия Workerman >= 3.3.7
-
PHP установлен с расширением openssl
-
Сертификат уже получен (файлы pem/crt и key) находятся в любом каталоге на диске
Код:
<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
// Лучше использовать полученный сертификат
$context = array(
// Для получения дополнительных параметров ssl обратитесь к документации http://php.net/manual/zh/context.ssl.php
'ssl' => array(
// Пожалуйста, используйте абсолютный путь
'local_cert' => 'путь_на_диске/server.pem', // Это также может быть crt файл
'local_pk' => 'путь_на_диске/server.key',
'verify_peer' => false,
'allow_self_signed' => true, // Если это самоподписанный сертификат, необходимо открыть эту опцию
)
);
// Здесь устанавливается websocket протокол (порт любой, но необходимо, чтобы он не был занят другими программами)
$worker = new Worker('websocket://0.0.0.0:8282', $context);
// Устанавливаем транспорт для включения ssl, websocket+ssl это wss
$worker->transport = 'ssl';
$worker->onMessage = function(TcpConnection $con, $msg) {
$con->send('ok');
};
Worker::runAll();
С помощью этого кода, Workerman будет слушать протокол wss, клиент сможет подключаться к Workerman через wss для безопасного мгновенного общения.
Тест
Откройте браузер Chrome, нажмите F12 для открытия консоли разработчиков, введите в разделе Console (или вставьте следующий код в HTML и выполните его с помощью js)
// Сертификат проверяет доменное имя, пожалуйста, используйте домен для подключения, обратите внимание, что здесь указан порт
ws = new WebSocket("wss://домен.com:8282");
ws.onopen = function() {
alert("Подключение успешно");
ws.send('tom');
alert("Отправка строки 'tom' на сервер");
};
ws.onmessage = function(e) {
alert("Получено сообщение с сервера: " + e.data);
};
Распространенные ошибки
SSL routines:SSL23_GET_CLIENT_HELLO:http request
Причина заключается в том, что клиент пытается получить доступ с использованием ws://домен.com, правильный адрес доступа должен быть wss://домен.com, т.е. доступ с префиксом wss. Эта проблема часто возникает, когда порт изначально был ws, а затем внезапно был изменен на wss, так как некоторые клиентские страницы не обновились и продолжают использовать ws, что приводит к ошибке.
Эта ошибка подлежит игнорированию и не влияет на нормальное соединение wss.
Внимание:
-
Если необходимо использовать порт 443, пожалуйста, используйте первый вариант с прокси nginx/apache для реализации wss.
-
Порт wss может быть доступен только через протокол wss, нельзя получить доступ к wss порту через ws.
-
Сертификаты обычно связаны с доменом, поэтому тестируя, пожалуйста, используйте домен для подключения, не используйте IP.
-
Если возникли проблемы с доступом, пожалуйста, проверьте настройки брандмауэра сервера.
-
Этот метод требует, чтобы версия PHP была >= 5.6, так как приложения WeChat Mini Program требуют tls1.2, а версии PHP ниже 5.6 не поддерживают tls1.2.
Связанные статьи:
Получение реального IP клиента через прокси
Справочные параметры SSL контекста Workerman