إنشاء خدمة wss

سؤال:

كيف يمكن لـ Workerman إنشاء خدمة wss بحيث يمكن للعميل الاتصال والتواصل باستخدام بروتوكول wss، مثل الاتصال بالخادم في تطبيق WeChat الصغير؟

جواب:

بروتوكول wss هو في الواقع websocket + SSL، وهو عبارة عن إضافة طبقة SSL إلى بروتوكول websocket، مشابهاً لـ https (http + SSL). لذا، كل ما يلزم هو تفعيل SSL على أساس بروتوكول websocket لدعم بروتوكول wss.

الطريقة الأولى، استخدام nginx/apache كوكيل SSL (موصى به)

أسباب التوصية

  • يمكن إعادة استخدام منفذ 443، مما يعني أن العميل ليس مضطراً لتحديد المنفذ عند الاتصال
  • إدارة مركزية لشهادات SSL، مما يسمح بإعادة استخدام إعدادات الموقع
  • يمكن القيام بتحميل موازن
  • يأتي مع مراقبة السجلات
  • توافق أفضل

مبدأ الاتصال والعملية

  1. العميل يقوم بإرسال اتصال wss إلى nginx/apache

  2. يقوم nginx/apache بتحويل بيانات بروتوكول wss إلى بيانات بروتوكول ws وإعادة توجيهها إلى منفذ بروتوكول websocket الخاص بـ Workerman

  3. Workerman يقوم بمعالجة البيانات وفقاً للمنطق التجاري

  4. عندما يقوم Workerman بإرسال رسالة إلى العميل، تكون العملية معكوسة، حيث يتم تحويل البيانات من خلال nginx/apache إلى بروتوكول wss ثم يتم إرسالها إلى العميل

إعدادات nginx المرجعية

مسبقات الشروط والإعدادات:

  1. يجب أن يكون nginx مثبتاً، بإصدار لا يقل عن 1.3

  2. نفترض أن Workerman يستمع على المنفذ 8282 (بروتوكول websocket)

  3. قد تم الحصول على شهادة (ملفات pem/crt وملف المفتاح) ويفترض أنها موجودة في /etc/nginx/conf.d/ssl

  4. نعتزم استخدام nginx لفتح المنفذ 443 للعرض وتوفير خدمة وكيل wss (يمكن تعديل المنفذ حسب الحاجة)

  5. يعمل 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.

التحضيرات:

  1. GatewayWorker يستمع إلى المنفذ 8282 (بروتوكول websocket)

  2. تم الحصول على شهادة ssl، ويفترض أنها موجودة في /server/httpd/cert/

  3. استخدام apache لتحويل المنفذ 443 إلى المنفذ المحدد 8282

  4. تم تحميل httpd-ssl.conf

  5. تم تثبيت 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);
};

الطريقة الثانية، استخدام Workerman لتفعيل SSL مباشرة (غير موصى به)

ملاحظة
يجب اختيار إما nginx/apache كوكيل SSL أو إعداد SSL في Workerman، ولا يمكن تفعيل كلاهما في نفس الوقت.

التحضيرات:

  1. إصدار Workerman >= 3.3.7

  2. يجب تثبيت وحدة openssl في PHP

  3. تم الحصول على شهادة (ملفات pem/crt وملف المفتاح) في أي دليل على القرص

الكود:

<?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);
// تعيين transport لتفعيل 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.

ملاحظات:

  1. إذا كان من الضروري استخدام المنفذ 443، يرجى استخدام الطريقة الأولى الخاصة بوكيل nginx/apache لتنفيذ wss.

  2. يمكن الوصول فقط إلى منفذ wss عبر بروتوكول wss، ولا يمكن الوصل إلى منفذ wss باستخدام ws.

  3. عادة ما تكون الشهادات مرتبطة بالنطاق، لذا يرجى استخدام النطاق للاتصال أثناء الاختبار، وعدم استخدام ip للاتصال.

  4. إذا واجهت صعوبة في الوصول، يرجى التحقق من جدار الحماية على الخادم.

  5. هذه الطريقة تتطلب إصدار PHP >= 5.6، لأن تطبيق WeChat الصغير يتطلب tls1.2، والإصدارات تحت 5.6 لا تدعم tls1.2.

المقالات ذات الصلة:
الحصول على عنوان IP الحقيقي للعميل عبر الوكيل
مرجع خيارات سياق ssl لـ workerman