المشكلة: وقت استجابة منخفض لعمليات الاتصال بين العميل والخادم والخادم والعميل
تم إنشاء الويب بشكل كبير حول ما يُعرف بنموذج الطلب/الاستجابة في HTTP. يُحمِّل العميل صفحة ويب، ولا يحدث أيّ شيء إلى أن ينقر المستخدم على الصفحة التالية. في عام 2005 تقريبًا، بدأت تقنية AJAX في جعل الويب أكثر ديناميكية. ومع ذلك، كان العميل يدير جميع عمليات تبادل البيانات عبر بروتوكول HTTP، ما كان يتطلّب تفاعل المستخدم أو إجراء عمليات استطلاع دورية لتحميل بيانات جديدة من الخادم.
لقد كانت التقنيات التي تتيح للخادم إرسال البيانات إلى العميل في اللحظة التي يعلم فيها أنّ هناك بيانات جديدة متاحة متوفّرة منذ فترة طويلة. وتُعرف هذه الرسائل باسم "Push" أو "Comet". إنّ إحدى الطرق الأكثر شيوعًا لخداع المستخدمين وإيهامهم بأنّ الاتصال قد بدأ من الخادم تُعرف باسم "الاستطلاع الطويل". باستخدام ميزة "الاستطلاع الطويل"، يفتح العميل اتصالاً عبر بروتوكول HTTP مع الخادم ويبقيه مفتوحًا إلى أن يتم إرسال الاستجابة. عندما يتوفّر لدى الخادم بيانات جديدة، يُرسِل الاستجابة (تتضمن التقنيات الأخرى طلبات Flash وXHR المتعدّدة الأجزاء وما يُعرف باسم htmlfiles). تعمل تقنية "الاستطلاع الطويل" والأساليب الأخرى بشكل جيد. ويمكنك استخدامها يوميًا في تطبيقات مثل خدمة الدردشة في Gmail.
ومع ذلك، تشترك جميع الحلول البديلة هذه في مشكلة واحدة: فهي تتحمل النفقات العامة لبروتوكول HTTP، ما يجعلها غير مناسبة للتطبيقات التي تتطلب وقت استجابة منخفضًا. تشمل هذه الألعاب ألعاب الرماية من منظور البطل المتعدّدة اللاعبين في المتصفّح أو أي لعبة أخرى على الإنترنت تتضمّن مكوّنًا في الوقت الفعلي.
تقديم WebSocket: توفير مآخذ على الويب
تحدِّد مواصفات WebSocket واجهة برمجة تطبيقات لإنشاء اتصالات "مقبس" بين متصفّح ويب وخادم. بعبارة أخرى، هناك اتصال دائم بين العميل والخادم ويمكن لكلا الطرفَين بدء إرسال البيانات في أي وقت.
البدء
يمكنك فتح اتصال WebSocket ببساطة عن طريق استدعاء عنصر الإنشاء WebSocket:
var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);
لاحِظ الرمز ws:
. هذا هو مخطّط عنوان URL الجديد لاتصالات WebSocket. هناك أيضًا wss:
لإنشاء اتصال آمن عبر WebSocket بالطريقة نفسها التي يتم بها استخدام https:
لإنشاء اتصالات آمنة عبر HTTP.
من خلال إرفاق بعض معالجات الأحداث على الفور بالاتصال، يمكنك معرفة وقت فتح الاتصال أو تلقّي الرسائل الواردة أو حدوث خطأ.
تقبل الوسيطة الثانية بروتوكولات فرعية اختيارية. ويمكن أن يكون سلسلة أو صفيفًا من السلاسل. يجب أن تمثّل كل سلسلة اسم بروتوكول فرعي، ولا يقبل الخادم سوى أحد البروتوكولات الفرعية التي تم تمريرها في الصفيف. يمكن تحديد البروتوكول الفرعي المقبول من خلال الوصول إلى سمة protocol
لكائن WebSocket.
يجب أن تكون أسماء البروتوكولات الفرعية واحدة من أسماء البروتوكولات الفرعية المسجّلة في سجلّ IANA. لا يتوفّر حاليًا سوى اسم بروتوكول فرعي واحد (soap) مسجَّل اعتبارًا من شباط (فبراير) 2012.
// When the connection is open, send some data to the server
connection.onopen = function () {
connection.send('Ping'); // Send the message 'Ping' to the server
};
// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};
التواصل مع الخادم
بعد أن نتمكّن من الاتصال بالخادم (عند بدء الحدث open
)، يمكننا بدء إرسال البيانات إلى الخادم باستخدام الطريقة send('your message')
في عنصر الاتصال. كان هذا البروتوكول يتيح إرسال سلاسل فقط، ولكن في أحدث المواصفات، يمكنه الآن إرسال رسائل ثنائية أيضًا. لإرسال البيانات الثنائية، يمكنك استخدام عنصر Blob
أو ArrayBuffer
.
// Sending String
connection.send('your message');
// Sending canvas ImageData as ArrayBuffer
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
binary[i] = img.data[i];
}
connection.send(binary.buffer);
// Sending file as Blob
var file = document.querySelector('input[type="file"]').files[0];
connection.send(file);
وبالمثل، قد يرسل الخادم إلينا رسائل في أي وقت. وكلما حدث ذلك، يتمّ تشغيل onmessage
. يتلقّى الإجراء المُعاد الاتصال به عنصر حدث ويمكن الوصول إلى الرسالة الفعلية من خلال السمة data
.
يمكن أن يتلقّى بروتوكول WebSocket أيضًا رسائل ثنائية في أحدث المواصفات. ويمكن تلقّي الإطارات الثنائية بتنسيق Blob
أو ArrayBuffer
. لتحديد تنسيق الملف الثنائي المستلَم، اضبط سمة binaryType لكائن WebSocket على "blob" أو "arraybuffer". التنسيق التلقائي هو "blob". (ليس عليك ضبط مَعلمة binaryType عند الإرسال).
// Setting binaryType to accept received binary as either 'blob' or 'arraybuffer'
connection.binaryType = 'arraybuffer';
connection.onmessage = function(e) {
console.log(e.data.byteLength); // ArrayBuffer object if binary
};
من الميزات الأخرى التي تمت إضافتها مؤخرًا إلى WebSocket هي الإضافات. باستخدام الإضافات، سيكون من الممكن إرسال إطارات مضغوطة ومتعدّدة القنوات وما إلى ذلك. يمكنك العثور على الإضافات المقبولة من الخادم من خلال فحص سمة الإضافات لواجهة WebSocket بعد الحدث المفتوح. لم يتم نشر مواصفات رسمية للإضافة حتى الآن اعتبارًا من شباط (فبراير) 2012.
// Determining accepted extensions
console.log(connection.extensions);
التواصل بين المواقع الإلكترونية
بما أنّ WebSocket هو بروتوكول حديث، تم تضمين ميزة التواصل بين مصادر مختلفة فيه. على الرغم من أنّه يجب التأكّد من التواصل مع العملاء والخوادم التي تثق بها فقط، يتيح WebSocket التواصل بين الأطراف في أي نطاق. يقرّر الخادم ما إذا كان سيتيح خدمته لجميع العملاء أو فقط لأولئك الذين يقيمون في مجموعة من النطاقات المحدّدة بوضوح.
خوادم الوكيل
تواجه كل تكنولوجيا جديدة مجموعة جديدة من المشاكل. في ما يتعلّق بـ WebSocket، فإنّه يتوافق مع الخوادم الوكيلة التي تتوسّط اتصالات HTTP في معظم شبكات الشركات. يستخدم بروتوكول WebSocket نظام ترقية HTTP (الذي يُستخدَم عادةً مع HTTP/SSL) من أجل "ترقية" اتصال HTTP إلى اتصال WebSocket. لا تتوافق بعض خوادم الوكيل مع هذا الإجراء، ما يؤدي إلى قطع الاتصال. وبالتالي، حتى إذا كان أحد العملاء يستخدم بروتوكول WebSocket، قد لا يكون من الممكن إنشاء اتصال. وهذا يجعل القسم التالي أكثر أهمية.
استخدام WebSockets اليوم
لا تزال تقنية WebSocket جديدة ولم يتم تنفيذها بالكامل في جميع المتصفّحات. ومع ذلك، يمكنك استخدام WebSocket اليوم مع المكتبات التي تستخدم أحد الحلول الاحتياطية المذكورة أعلاه في حال عدم توفّر WebSocket. من المكتبات التي أصبحت رائجة جدًا في هذا المجال هي socket.io التي تأتي مع تنفيذ بروتوكول العميل والخادم وتشمل عمليات الاستبدال (لا تتوافق socket.io مع المراسلة الثنائية حتى الآن اعتبارًا من شباط (فبراير) 2012). هناك أيضًا حلول تجارية، مثل PusherApp، والتي يمكن دمجها بسهولة في أي بيئة ويب من خلال توفير واجهة برمجة تطبيقات HTTP لإرسال رسائل WebSocket إلى العملاء. بسبب طلب HTTP الإضافي، سيكون هناك دائمًا عبء إضافي مقارنةً بـ WebSocket.
جهة الخادم
يؤدي استخدام WebSocket إلى إنشاء نمط استخدام جديد تمامًا للتطبيقات من جهة الخادم. على الرغم من أنّ مجموعات الخوادم التقليدية، مثل LAMP، مصمّمة استنادًا إلى دورة طلب/استجابة HTTP، إلا أنّها غالبًا ما لا تتعامل بشكل جيد مع عدد كبير من اتصالات WebSocket المفتوحة. يتطلّب إبقاء عدد كبير من الاتصالات مفتوحة في الوقت نفسه بنية معمارية تتلقّى عددًا كبيرًا من عمليات المعالجة المتزامنة بتكلفة أداء منخفضة. وعادةً ما يتم تصميم هذه التصاميم حول خيوط المعالجة أو ما يُعرف باسم "إدخال/إخراج غير حظر".
عمليات التنفيذ من جهة الخادم
- Node.js
- Java
- روبي
- Python
- وحدة قياس زمن الانتظار (Erlang)
- C++
- .NET
إصدارات البروتوكول
البروتوكول المُستخدَم لنقل البيانات (تأكيد الاتصال ونقل البيانات بين العميل والخادم) في WebSocket هو الآن RFC6455. إنّ أحدث إصدارَي Chrome وChrome لأجهزة Android متوافقان تمامًا مع RFC6455، بما في ذلك المراسلة الثنائية. سيكون Firefox متوافقًا أيضًا مع الإصدار 11، وInternet Explorer مع الإصدار 10. سيظل بإمكانك استخدام إصدارات البروتوكول القديمة، ولكن لا يُنصح بذلك لأنّها معروفة بأنّها معرّضة للاختراق. إذا كانت لديك عمليات تنفيذ خادم لإصدارات قديمة من بروتوكول WebSocket، ننصحك بترقيتها إلى أحدث إصدار.
حالات الاستخدام
استخدِم WebSocket عندما تحتاج إلى اتصال سريع جدًا بين العميل والخادم، أي اتصال قريب من الوقت الفعلي. يُرجى العِلم أنّ هذا قد يتطلّب إعادة التفكير في كيفية إنشاء تطبيقاتك من جهة الخادم مع التركيز بشكل جديد على تقنيات مثل قوائم انتظار الأحداث. في ما يلي بعض أمثلة حالات الاستخدام:
- الألعاب المتعدّدة اللاعبين على الإنترنت
- تطبيقات المحادثات
- شريط الأخبار الرياضية المباشر
- أحداث البث على وسائل التواصل الاجتماعي التي يتم تعديلها في الوقت الفعلي
عروض توضيحية
- Plink
- Paint With Me
- Pixelatr
- خط متقطّع
- لعبة كلمات متقاطعة متعددة اللاعبين على الإنترنت
- خادم "إرسال طلب فحص الاتصال" (المستخدَم في الأمثلة أعلاه)
- نموذج HTML5demos