wss 서비스 생성하기
질문:
Workerman으로 클라이언트가 wss 프로토콜을 통해 통신할 수 있도록 wss 서비스를 어떻게 생성하나요? 예를 들어, WeChat 미니 프로그램에서 서버에 연결하는 경우입니다.
답변:
wss 프로토콜은 실제로 websocket + SSL입니다. 즉, websocket 프로토콜에 SSL 레이어를 추가한 것이며 https와 유사합니다 (http + SSL).
따라서 websocket 프로토콜의 기반 위에서 SSL을 활성화하면 wss 프로토콜을 지원할 수 있습니다.
방법 1, nginx/apache로 SSL 프록시 사용하기 (권장)
추천하는 이유
- 443 포트를 재사용할 수 있어 클라이언트가 포트를 지정할 필요가 없습니다.
- SSL 인증서를 중앙에서 관리할 수 있으며, 웹사이트 구성도 재사용할 수 있습니다.
- 부하 분산이 가능합니다.
- 로그 모니터링이 포함되어 있습니다.
- 더 나은 호환성을 제공합니다.
통신 원리 및 흐름
- 클라이언트가 wss 연결을 위해 nginx/apache에 요청을 보냅니다.
- nginx/apache는 wss 프로토콜 데이터를 ws 프로토콜 데이터로 변환하여 Workerman의 websocket 프로토콜 포트로 전달합니다.
- Workerman은 데이터를 수신한 후 비즈니스 논리를 처리합니다.
- Workerman이 클라이언트에게 메시지를 보낼 경우, 반대 과정이 발생하여 데이터는 nginx/apache를 통해 wss 프로토콜로 변환되어 클라이언트로 전송됩니다.
nginx 구성 참고
전제 조건 및 준비 작업:
- nginx가 설치되어 있어야 하며, 버전은 1.3 이상이어야 합니다.
- Workerman이 8282 포트를 리슨하고 있다고 가정합니다 (websocket 프로토콜).
- 인증서를 신청했으며 (pem/crt 파일 및 key 파일), 가정상 /etc/nginx/conf.d/ssl에 저장되어 있습니다.
- nginx를 통해 443 포트를 열어 wss 프록시 서비스를 제공할 계획입니다 (포트는 필요에 따라 수정할 수 있습니다).
- 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 활성화하기 (비추천)
주의
nginx/apache로 SSL을 프록시하는 것과 Workerman에서 SSL을 설정하는 것 중 하나만 선택할 수 있으며, 동시에 활성화할 수는 없습니다.
준비 작업:
- 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, // self-signed 인증서인 경우 이 옵션을 활성화해야 합니다.
)
);
// 여기서는 websocket 프로토콜을 설정합니다 (포트는 임의로 선택할 수 있지만 다른 프로그램이 사용하지 않아야 합니다).
$worker = new Worker('websocket://0.0.0.0:8282', $context);
// transport에서 ssl을 활성화하여, websocket + ssl 즉 wss가 됩니다.
$worker->transport = 'ssl';
$worker->onMessage = function(TcpConnection $con, $msg) {
$con->send('ok');
};
Worker::runAll();
위의 코드를 통해 Workerman은 wss 프로토콜을 리슨하게 되며, 클라이언트는 wss 프로토콜을 통해 Workerman에 안전하게 실시간 통신을 할 수 있습니다.
테스트
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 프로토콜로만 접근할 수 있으며, ws로는 접근할 수 없습니다.
- 인증서는 일반적으로 도메인에 바인딩되므로 테스트할 때 클라이언트는 도메인으로 연결해야 하며, IP로 연결하지 마십시오.
- 접근할 수 없는 경우 서버 방화벽을 점검하십시오.
- 이 방법은 PHP 버전 >= 5.6을 요구합니다. WeChat 미니 프로그램은 tls1.2를 요구하는데, PHP 5.6 이하 버전은 tls1.2를 지원하지 않기 때문입니다.