إنشاء خدمات خلفية لتطبيقات WebRTC

تاريخ النشر: 4 تشرين الثاني (نوفمبر) 2013

يتيح WebRTC الاتصال من نظير إلى نظير. ويتطلب ذلك خوادم لكي يتمكّن العملاء من تبادل البيانات الوصفية وتنسيق الاتصال من خلال عملية تُسمى الإشارة. تساعد الخوادم أيضًا العملاء في التعامل مع مترجمي عناوين الشبكة (NAT) وجدران الحماية.

ستتعرّف هنا على كيفية إنشاء خدمة إشارات والتعامل مع المشاكل الغريبة في إمكانية الاتصال في العالم الحقيقي باستخدام خوادم STUN وTURN. ستتعرّف أيضًا على كيفية تعامل تطبيقات WebRTC مع المكالمات المتعددة الأطراف وتفاعلها مع خدمات مثل VoIP وPSTN (المعروفة أيضًا باسم الهواتف).

ما هي الإشارات؟

الإشارة هي عملية تنسيق الاتصال. لكي يتمكّن تطبيق WebRTC من إعداد مكالمة، يجب أن تتبادل برامج العميل المعلومات التالية:

  • رسائل التحكّم في الجلسة المستخدَمة لفتح الاتصال أو إغلاقه
  • رسائل الخطأ
  • البيانات الوصفية للوسائط، مثل برامج الترميز وإعداداتها والنطاق الترددي وأنواع الوسائط
  • البيانات الرئيسية المستخدَمة لإنشاء اتصالات آمنة
  • بيانات الشبكة، مثل عنوان IP الخاص بالمضيف والمنفذ كما يظهر للعالم الخارجي

تحتاج عملية الإشارة هذه إلى طريقة تتيح للعملاء تبادل الرسائل. ولا يتم تنفيذ هذه الآلية من خلال واجهات برمجة تطبيقات WebRTC. بدلاً من ذلك، تحتاج إلى إنشائه بنفسك.

لماذا لا يتم تحديد الإشارات بواسطة WebRTC؟

لتجنب التكرار ولتحقيق أقصى قدر من التوافق مع التقنيات المعمول بها، لا يتم تحديد طرق وبروتوكولات الإشارات بواسطة معايير WebRTC. يتم توضيح هذا النهج من خلال بروتوكول إنشاء جلسة JavaScript‏ (JSEP):

تتجنب بنية JSEP أيضًا أن يضطر المتصفح إلى حفظ الحالة، أي أن يعمل كآلة حالة إشارة. قد يؤدي ذلك إلى حدوث مشكلة، على سبيل المثال، في حال فقدان بيانات الإشارات في كل مرة تتم فيها إعادة تحميل الصفحة. بدلاً من ذلك، يمكن حفظ حالة الإشارة على خادم.

مخطّط بياني لبنية JSEP

يتطلّب بروتوكول JSEP تبادل العرض والردّ بين الأطراف، وهما البيانات الوصفية للوسائط. يتم تبادل العروض والإجابات بتنسيق "بروتوكول وصف الجلسة" (SDP)، والذي يبدو على النحو التالي:

v=0
o=- 7614219274584779017 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS
m=audio 1 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:W2TGCZw2NZHuwlnf
a=ice-pwd:xdQEccP40E+P0L5qTyzDgfmW
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=mid:audio
a=rtcp-mux
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:9c1AHz27dZ9xPI91YNfSlI67/EMkjHHIHORiClQe
a=rtpmap:111 opus/48000/2
...

هل تريد معرفة معنى كل هذه المصطلحات المعقّدة في بروتوكول SDP؟ يمكنك الاطّلاع على أمثلة مجموعة مهندسي شبكة الإنترنت (IETF).

Bear in mind that WebRTC is designed so that the offer or answer can be tweaked before being set as the local or remote description by editing the values in the SDP text. على سبيل المثال، يمكن استخدام الدالة preferAudioCodec() في appr.tc لضبط برنامج الترميز ومعدل نقل البيانات التلقائيَين. يصعب إلى حد ما التعامل مع بروتوكول SDP باستخدام JavaScript، وهناك نقاش حول ما إذا كان يجب أن تستخدم الإصدارات المستقبلية من WebRTC تنسيق JSON بدلاً من ذلك، ولكن هناك بعض المزايا للالتزام ببروتوكول SDP.

واجهة برمجة التطبيقات RTCPeerConnection والإشارات: العرض والرد والمرشح

RTCPeerConnection هي واجهة برمجة التطبيقات التي تستخدمها تطبيقات WebRTC لإنشاء اتصال بين النظراء، ونقل الصوت والفيديو.

لبدء هذه العملية، لدى RTCPeerConnection مهمتان:

  • تحديد حالات الوسائط المحلية، مثل الدقة وإمكانيات برامج الترميز هذه هي البيانات الوصفية المستخدَمة لآلية العرض والردّ.
  • الحصول على عناوين الشبكة المحتملة لمضيف التطبيق، والمعروفة باسم المرشحين

بعد التأكّد من هذه البيانات المحلية، يجب تبادلها من خلال آلية إرسال الإشارات مع الطرف البعيد.

لنفترض أنّ "أمل" تحاول الاتصال بـ "إيمان". في ما يلي آلية الردّ الكاملة بكل تفاصيلها:

  1. تنشئ "أمل" كائن RTCPeerConnection.
  2. تنشئ "أمل" عرضًا (وصف جلسة SDP) باستخدام طريقة RTCPeerConnection createOffer().
  3. تتصل "أمل" بـ "setLocalDescription()" لتقديم عرضها.
  4. تحوّل "أليس" العرض إلى سلسلة وتستخدم آلية إرسال الإشارات لإرساله إلى "إيف".
  5. تتصل "إيف" بـ "setRemoteDescription()" باستخدام عرض "أليس"، لكي يعرف RTCPeerConnection الخاص بها إعدادات "أليس".
  6. تتصل "حواء" بـ createAnswer() ويتم تمرير رد الاتصال الناجح لهذا إلى وصف الجلسة المحلية - إجابة "حواء".
  7. تضبط "حنان" إجابتها على أنّها الوصف المحلي من خلال طلب setLocalDescription().
  8. بعد ذلك، تستخدم "إيمان" آلية الإشارة لإرسال إجابتها النصية إلى "أمل".
  9. تضبط "أليس" إجابة "إيف" كـ "وصف الجلسة عن بُعد" باستخدام setRemoteDescription().

تحتاج "أليس" و"إيف" أيضًا إلى تبادل معلومات الشبكة. يشير التعبير "العثور على المرشحين" إلى عملية العثور على واجهات الشبكة والمنافذ باستخدام إطار عمل ICE.

  1. تنشئ "أمل" عنصر RTCPeerConnection باستخدام معالج onicecandidate.
  2. يتم استدعاء المعالج عندما تصبح الشبكات المرشحة متاحة.
  3. في المعالج، ترسل "أمل" بيانات المرشحين على شكل سلسلة إلى "إيمان" من خلال قناة الإشارات.
  4. عندما تتلقّى "سارة" رسالة مرشّح من "أمل"، تطلب addIceCandidate() لإضافة المرشّح إلى وصف النظير البعيد.

يتيح بروتوكول JSEP استخدام ICE Candidate Trickling، ما يسمح للمتصل بتقديم المرشحين تدريجيًا إلى المتلقي بعد العرض الأولي، وللمتلقي ببدء العمل على المكالمة وإعداد اتصال بدون انتظار وصول جميع المرشحين.

رمز WebRTC للإشارات

مقتطف الرمز التالي هو مثال على رمز W3C يلخّص عملية الإشارة الكاملة. يفترض الرمز وجود آلية إرسال إشارات معيّنة، وهي SignalingChannel. تتم مناقشة الإشارات بمزيد من التفصيل لاحقًا.

// handles JSON.stringify/parse
const signaling = new SignalingChannel();
const constraints = {audio: true, video: true};
const configuration = {iceServers: [{urls: 'stun:stun.example.org'}]};
const pc = new RTCPeerConnection(configuration);

// Send any ice candidates to the other peer.
pc.onicecandidate = ({candidate}) => signaling.send({candidate});

// Let the "negotiationneeded" event trigger offer generation.
pc.onnegotiationneeded = async () => {
  try {
    await pc.setLocalDescription(await pc.createOffer());
    // send the offer to the other peer
    signaling.send({desc: pc.localDescription});
  } catch (err) {
    console.error(err);
  }
};

// After remote track media arrives, show it in remote video element.
pc.ontrack = (event) => {
  // Don't set srcObject again if it is already set.
  if (remoteView.srcObject) return;
  remoteView.srcObject = event.streams[0];
};

// Call start() to initiate.
async function start() {
  try {
    // Get local stream, show it in self-view, and add it to be sent.
    const stream =
      await navigator.mediaDevices.getUserMedia(constraints);
    stream.getTracks().forEach((track) =>
      pc.addTrack(track, stream));
    selfView.srcObject = stream;
  } catch (err) {
    console.error(err);
  }
}

signaling.onmessage = async ({desc, candidate}) => {
  try {
    if (desc) {
      // If you get an offer, you need to reply with an answer.
      if (desc.type === 'offer') {
        await pc.setRemoteDescription(desc);
        const stream =
          await navigator.mediaDevices.getUserMedia(constraints);
        stream.getTracks().forEach((track) =>
          pc.addTrack(track, stream));
        await pc.setLocalDescription(await pc.createAnswer());
        signaling.send({desc: pc.localDescription});
      } else if (desc.type === 'answer') {
        await pc.setRemoteDescription(desc);
      } else {
        console.log('Unsupported SDP type.');
      }
    } else if (candidate) {
      await pc.addIceCandidate(candidate);
    }
  } catch (err) {
    console.error(err);
  }
};

للاطّلاع على عمليات العرض والرد وتبادل المرشحين أثناء العمل، انتقِل إلى simpl.info RTCPeerConnection وألقِ نظرة على سجلّ وحدة التحكّم للحصول على مثال على محادثة فيديو من صفحة واحدة. يمكنك تنزيل نسخة كاملة من إشارات وإحصاءات WebRTC من صفحة about://webrtc-internals في Google Chrome أو صفحة opera://webrtc-internals في Opera.

اكتشاف الأجهزة المجاورة

هذه طريقة رائعة للسؤال، "كيف أجد شخصًا أتحدث إليه؟"

بالنسبة إلى المكالمات الهاتفية، لديك أرقام هواتف وأدلة. بالنسبة إلى محادثات الفيديو والمراسلة على الإنترنت، تحتاج إلى أنظمة إدارة الهوية والحضور ووسيلة للمستخدمين لبدء الجلسات. تحتاج تطبيقات WebRTC إلى طريقة تتيح للعملاء إرسال إشارات إلى بعضهم البعض بأنّهم يريدون بدء مكالمة أو الانضمام إليها.

لا يتم تحديد آليات اكتشاف النظراء بواسطة WebRTC ولا تدخل في الخيارات هنا. يمكن أن تكون العملية بسيطة مثل إرسال عنوان URL عبر البريد الإلكتروني أو المراسلة. بالنسبة إلى تطبيقات محادثات الفيديو، مثل Talky وtawk.to وBrowser Meeting، يمكنك دعوة المستخدمين إلى مكالمة من خلال مشاركة رابط مخصّص. أنشأ المطوّر "كريس بول" تجربة serverless-webrtc مثيرة للاهتمام تتيح للمشاركين في مكالمات WebRTC تبادل البيانات الوصفية من خلال أي خدمة مراسلة يفضّلونها، مثل المراسلة الفورية أو البريد الإلكتروني أو الحمام الزاجل.

كيف يمكنك إنشاء خدمة إرسال إشارات؟

للتكرار، لا يتم تحديد بروتوكولات وآليات الإشارات بواسطة معايير WebRTC. أيًا كان اختيارك، ستحتاج إلى خادم وسيط لتبادل رسائل الإشارات وبيانات التطبيق بين العملاء. للأسف، لا يمكن لتطبيق الويب أن يصرخ في الإنترنت، "أريد الاتصال بصديقي!"

لحسن الحظ، رسائل الإشارات صغيرة ويتم تبادلها في الغالب في بداية المكالمة. في الاختبار باستخدام appr.tc لجلسة محادثة فيديو، تم التعامل مع إجمالي 30-45 رسالة تقريبًا بواسطة خدمة الإشارات بإجمالي حجم لجميع الرسائل يبلغ حوالي 10 كيلوبايت.

بالإضافة إلى كونها غير متطلبة نسبيًا من حيث النطاق الترددي، فإن خدمات إشارات WebRTC لا تستهلك الكثير من المعالجة أو الذاكرة لأنها تحتاج فقط إلى ترحيل الرسائل والاحتفاظ بكمية صغيرة من بيانات حالة الجلسة، مثل العملاء المتصلين.

إرسال رسائل من الخادم إلى العميل

Browser Support

  • Chrome: 6.
  • Edge: 79.
  • Firefox: 6.
  • Safari: 5.

Source

يجب أن تكون خدمة الرسائل للإشارة ثنائية الاتجاه: من العميل إلى الخادم ومن الخادم إلى العميل. يتعارض الاتصال الثنائي الاتجاه مع نموذج الطلب/الاستجابة الخاص بعميل HTTP/الخادم، ولكن تم تطوير العديد من الحلول البديلة، مثل الاستقصاء الطويل، على مدار سنوات عديدة من أجل دفع البيانات من خدمة تعمل على خادم ويب إلى تطبيق ويب يعمل في متصفح.

تتيح واجهة برمجة التطبيقات EventSource الأحداث المُرسَلة من الخادم، وهي البيانات المُرسَلة من خادم ويب إلى برنامج متصفّح عبر بروتوكول HTTP. تم تصميم EventSource للمراسلة أحادية الاتجاه، ولكن يمكن استخدامه مع XHR لإنشاء خدمة لتبادل رسائل الإشارات. تمرّر خدمة الإشارات رسالة من المتصل، يتم تسليمها من خلال طلب XHR، عن طريق إرسالها من خلال EventSource إلى المتلقي.

Browser Support

  • Chrome: 5.
  • Edge: 12.
  • Firefox: 11.
  • Safari: 5.

Source

تم تصميم WebSocket للاتصال مزدوج الاتجاه بين العميل والخادم، ما يعني أنّ الرسائل يمكن أن تتدفق في كلا الاتجاهين في الوقت نفسه. من مزايا خدمة الإشارات التي تم إنشاؤها باستخدام WebSocket أو الأحداث التي يرسلها الخادم (EventSource) هي أنّه يمكن تنفيذ الخلفية لواجهات برمجة التطبيقات هذه على مجموعة متنوعة من أُطر عمل الويب الشائعة في معظم حزم استضافة الويب للغات مثل PHP وPython وRuby.

تتوافق جميع المتصفّحات التي تتيح استخدام WebRTC مع WebSocket أيضًا. يجب استخدام TLS لجميع الاتصالات لضمان عدم اعتراض الرسائل غير المشفّرة وللحدّ من المشاكل المتعلقة باجتياز الخادم الوكيل. لمزيد من المعلومات حول WebSocket واجتياز الخادم الوكيل، يُرجى الاطّلاع على فصل WebRTC في كتاب High Performance Browser Networking (شبكات المتصفّحات العالية الأداء) من تأليف "إيليا غريغوريك".

يمكنك التعامل مع الإشارات من خلال جعل عملاء WebRTC يستطلعون خادم المراسلة بشكل متكرر من خلال Ajax، ولكن هذا يؤدي إلى الكثير من طلبات الشبكة الزائدة عن الحاجة، وهو أمر يمثل مشكلة خاصة للأجهزة المحمولة. حتى بعد إنشاء جلسة، يحتاج النظراء إلى الاستقصاء عن رسائل الإشارة في حالة حدوث تغييرات أو إنهاء الجلسة من قبل نظراء آخرين. يستخدم مثال تطبيق WebRTC Book هذا الخيار مع بعض التحسينات لمعدّل تكرار الاستطلاع.

توسيع نطاق الإشارات

على الرغم من أنّ خدمة الإشارات تستهلك نطاقًا تردديًا ووحدة معالجة مركزية (CPU) قليلة نسبيًا لكل عميل، قد تضطر خوادم الإشارات لتطبيق رائج إلى معالجة الكثير من الرسائل من مواقع جغرافية مختلفة بمستويات عالية من التزامن. تحتاج تطبيقات WebRTC التي تحصل على الكثير من الزيارات إلى خوادم إشارات قادرة على التعامل مع حمل كبير. لن نتناول التفاصيل هنا، ولكن هناك عدد من الخيارات للمراسلة بكميات كبيرة وعالية الأداء، بما في ذلك ما يلي:

  • بروتوكول المراسلة والحضور القابل للتوسيع (XMPP) هو بروتوكول تم تطويره للمراسلة الفورية ويمكن استخدامه للإشارة. وتشمل عمليات تنفيذ الخادم ejabberd وOpenfire. تستخدم برامج JavaScript، مثل Strophe.js، بروتوكول BOSH لمحاكاة البث الثنائي الاتجاه، ولكن قد يكون بروتوكول BOSH غير فعّال ويتوسّع بشكل سيئ مقارنةً ببروتوكول WebSocket.

  • المكتبات المفتوحة المصدر، مثل ZeroMQ (كما تستخدمها TokBox لخدمة Rumour) وOpenMQ (NullMQ تطبّق مفاهيم ZeroMQ على منصات الويب باستخدام بروتوكول STOMP عبر WebSocket.)

  • منصات المراسلة عبر السحابة الإلكترونية التجارية التي تستخدم WebSocket (على الرغم من أنّها قد تعود إلى الاستقصاء الطويل)، مثل Pusher وKaazing

  • منصات WebRTC التجارية، مثل vLine

إنشاء خدمة إرسال إشارات باستخدام Socket.io على Node

في ما يلي رمز لتطبيق ويب يستخدم خدمة إرسال إشارات تم إنشاؤها باستخدام Socket.io على Node. يسهّل تصميم Socket.io إنشاء خدمة لتبادل الرسائل، وهو مناسب بشكل خاص لإرسال إشارات WebRTC بسبب مفهوم الغرف المضمّن فيه. تم تصميم هذا المثال لعدد صغير نسبيًا من المستخدمين، وليس للاستخدام في بيئة إنتاجية.

يستخدم Socket.io بروتوكول WebSocket مع خيارات احتياطية: استقصاء AJAX الطويل، وبث AJAX متعدد الأجزاء، وForever Iframe، واستقصاء JSONP. تم نقله إلى واجهات خلفية مختلفة، ولكن ربما يكون معروفًا بشكل أفضل بإصدار Node المستخدم في هذا المثال.

لا يتوفّر WebRTC في هذا المثال. تم تصميم هذا التطبيق لتوضيح كيفية إنشاء الإشارات في تطبيق ويب. يمكنك الاطّلاع على سجلّ وحدة التحكّم لمعرفة ما يحدث عندما ينضم العملاء إلى غرفة ويتبادلون الرسائل. يقدّم الدرس التطبيقي حول الترميز في WebRTC تعليمات مفصّلة حول كيفية دمج هذه الميزة في تطبيق محادثات فيديو كامل في WebRTC.

إليك العميل index.html:

<!DOCTYPE html>
<html>
  <head>
    <title>WebRTC client</title>
  </head>
  <body>
    <script src='/socket.io/socket.io.js'></script>
    <script src='js/main.js'></script>
  </body>
</html>

إليك ملف JavaScript‏ main.js المُشار إليه في العميل:

const isInitiator;

room = prompt('Enter room name:');

const socket = io.connect();

if (room !== '') {
  console.log('Joining room ' + room);
  socket.emit('create or join', room);
}

socket.on('full', (room) => {
  console.log('Room ' + room + ' is full');
});

socket.on('empty', (room) => {
  isInitiator = true;
  console.log('Room ' + room + ' is empty');
});

socket.on('join', (room) => {
  console.log('Making request to join room ' + room);
  console.log('You are the initiator!');
});

socket.on('log', (array) => {
  console.log.apply(console, array);
});

في ما يلي تطبيق الخادم الكامل:

const static = require('node-static');
const http = require('http');
const file = new(static.Server)();
const app = http.createServer(function (req, res) {
  file.serve(req, res);
}).listen(2013);

const io = require('socket.io').listen(app);

io.sockets.on('connection', (socket) => {

  // Convenience function to log server messages to the client
  function log(){
    const array = ['>>> Message from server: '];
    for (const i = 0; i < arguments.length; i++) {
      array.push(arguments[i]);
    }
      socket.emit('log', array);
  }

  socket.on('message', (message) => {
    log('Got message:', message);
    // For a real app, would be room only (not broadcast)
    socket.broadcast.emit('message', message);
  });

  socket.on('create or join', (room) => {
    const numClients = io.sockets.clients(room).length;

    log('Room ' + room + ' has ' + numClients + ' client(s)');
    log('Request to create or join room ' + room);

    if (numClients === 0){
      socket.join(room);
      socket.emit('created', room);
    } else if (numClients === 1) {
      io.sockets.in(room).emit('join', room);
      socket.join(room);
      socket.emit('joined', room);
    } else { // max two clients
      socket.emit('full', room);
    }
    socket.emit('emit(): client ' + socket.id +
      ' joined room ' + room);
    socket.broadcast.emit('broadcast(): client ' + socket.id +
      ' joined room ' + room);

  });

});

لتشغيل هذا التطبيق على المضيف المحلي، يجب أن يكون لديك Node.js وSocket.IO وnode-static. لتثبيت Socket.IO وnode-static، شغِّل npm من وحدة طرفية في دليل تطبيقك:

npm install socket.io
npm install node-static

لبدء الخادم، نفِّذ الأمر التالي من وحدة طرفية في دليل تطبيقك:

node server.js

من المتصفّح، افتح localhost:2013. افتح علامة تبويب أو نافذة جديدة في أي متصفّح وافتح localhost:2013 مرة أخرى. لمعرفة ما يحدث، يُرجى الاطّلاع على وحدة التحكّم. في Chrome وOpera، يمكنك الوصول إلى وحدة التحكّم من خلال أدوات مطوّري برامج Google Chrome باستخدام Ctrl+Shift+J (أو Command+Option+J على أجهزة Mac).

أيًا كان النهج الذي تختاره للإشارة، يجب أن توفر الواجهة الخلفية وتطبيق العميل خدمات مشابهة لهذا المثال.

مشاكل الإشارات

  • لن يبدأ RTCPeerConnection في جمع المرشحين حتى يتم استدعاء setLocalDescription(). وهذا مطلوب في مسودة JSEP IETF.
  • استفِد من Trickle ICE. اتصل بـ addIceCandidate() بمجرد وصول المرشحين.

خوادم إشارات جاهزة

إذا كنت لا تريد إنشاء خادمك الخاص، تتوفّر عدة خوادم إشارات WebRTC تستخدم Socket.IO مثل المثال السابق وتكون مدمجة مع مكتبات JavaScript لعملاء WebRTC:

  • webRTC.io هي إحدى أولى مكتبات التجريد لبروتوكول WebRTC.
  • Signalmaster هو خادم إشارات تم إنشاؤه للاستخدام مع مكتبة برامج JavaScript SimpleWebRTC.

إذا كنت لا تريد كتابة أي رمز برمجي على الإطلاق، يمكنك الحصول على منصات WebRTC التجارية الكاملة من شركات، مثل vLine وOpenTok وAsterisk.

للعلم، أنشأت شركة Ericsson خادم إشارات باستخدام PHP على Apache في الأيام الأولى من WebRTC. أصبح هذا الأسلوب قديمًا بعض الشيء، ولكن من المفيد الاطّلاع على الرمز البرمجي إذا كنت تفكّر في استخدام أسلوب مشابه.

أمان Signal

التشفير إلزامي لجميع مكوّنات WebRTC.

ومع ذلك، لا يتم تحديد آليات الإشارات من خلال معايير WebRTC، لذا فالأمر متروك لك لجعل الإشارات آمنة. إذا تمكّن المهاجم من الاستيلاء على الإشارات، يمكنه إيقاف الجلسات وإعادة توجيه الاتصالات وتسجيل المحتوى أو تغييره أو حقنه.

أهم عامل في تأمين الإشارات هو استخدام بروتوكولات آمنة: HTTPS وWSS، مثل TLS. يضمن ذلك عدم اعتراض الرسائل غير المشفّرة. يُرجى أيضًا الحرص على عدم بث رسائل الإشارات بطريقة تتيح للمتصلين الآخرين الوصول إليها باستخدام خادم الإشارات نفسه.

التعامل مع ترجمة عناوين الشبكة (NAT) وجدران الحماية

بالنسبة إلى إرسال البيانات الوصفية، تستخدم تطبيقات WebRTC خادمًا وسيطًا، ولكن بالنسبة إلى بث الوسائط والبيانات الفعلي بعد إنشاء جلسة، يحاول RTCPeerConnection ربط العملاء مباشرةً أو من نظير إلى نظير.

في عالم أبسط، سيكون لكل نقطة نهاية WebRTC عنوان فريد يمكنها تبادله مع أقران آخرين من أجل التواصل مباشرة.

اتصال بسيط بين جهازين
عالم بدون ترجمة عناوين الشبكة (NAT) وجدران حماية

في الواقع، تعمل معظم الأجهزة خلف طبقة واحدة أو أكثر من NAT، ويحتوي بعضها على برامج مكافحة الفيروسات التي تحظر منافذ وبروتوكولات معيّنة، والعديد منها يعمل خلف الخوادم الوكيلة وجدران الحماية الخاصة بالشركات. قد يتم تنفيذ جدار الحماية وNAT من خلال الجهاز نفسه، مثل جهاز توجيه Wi-Fi منزلي.

الأجهزة النظيرة خلف جدران الحماية و&quot;ترجمة عنوان الشبكة&quot; (NAT)
العالم الحقيقي

يمكن لتطبيقات WebRTC استخدام إطار عمل ICE للتغلّب على تعقيدات الشبكات في العالم الحقيقي. لتفعيل هذه الميزة، يجب أن يمرّر تطبيقك عناوين URL لخادم ICE إلى RTCPeerConnection.

يحاول ICE العثور على أفضل مسار لربط الأقران. ويجرّب جميع الاحتمالات بالتوازي ويختار الخيار الأكثر كفاءة الذي يعمل. يحاول بروتوكول ICE أولاً إنشاء اتصال باستخدام عنوان المضيف الذي تم الحصول عليه من نظام تشغيل الجهاز وبطاقة الشبكة. وفي حال فشل ذلك (وهو ما سيحدث للأجهزة التي تستخدم بروتوكول NAT)، يحصل بروتوكول ICE على عنوان خارجي باستخدام خادم STUN، وفي حال فشل ذلك، يتم توجيه عدد الزيارات من خلال خادم ترحيل TURN.

بعبارة أخرى، يُستخدَم خادم STUN للحصول على عنوان شبكة خارجي، وتُستخدَم خوادم TURN لإعادة توجيه حركة البيانات في حال تعذُّر الاتصال المباشر (بين النظراء).

يتيح كل خادم TURN استخدام STUN. خادم TURN هو خادم STUN يتضمّن وظيفة إضافية مضمّنة لإعادة الإرسال. يتعامل ICE أيضًا مع تعقيدات إعدادات NAT. في الواقع، قد يتطلب ثقب NAT أكثر من مجرد عنوان IP:port عام.

يتم تحديد عناوين URL لخوادم STUN وTURN (اختياريًا) من خلال تطبيق WebRTC في عنصر الإعداد iceServers الذي يمثّل الوسيطة الأولى لدالة الإنشاء RTCPeerConnection. بالنسبة إلى appr.tc، تبدو هذه القيمة على النحو التالي:

{
  'iceServers': [
    {
      'urls': 'stun:stun.l.google.com:19302'
    },
    {
      'urls': 'turn:192.158.29.39:3478?transport=udp',
      'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
      'username': '28224511:1379330808'
    },
    {
      'urls': 'turn:192.158.29.39:3478?transport=tcp',
      'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
      'username': '28224511:1379330808'
    }
  ]
}

بعد حصول RTCPeerConnection على هذه المعلومات، يحدث سحر ICE تلقائيًا. يستخدم RTCPeerConnection إطار عمل ICE لتحديد أفضل مسار بين الأجهزة النظيرة، ويعمل مع خوادم STUN وTURN حسب الحاجة.

STUN

توفّر أجهزة NAT عنوان IP للجهاز لاستخدامه ضمن شبكة محلية خاصة، ولكن لا يمكن استخدام هذا العنوان خارجيًا. بدون عنوان متاح للجميع، لن تتمكّن أجهزة WebRTC النظيرة من التواصل. لحلّ هذه المشكلة، يستخدم WebRTC STUN.

توجد خوادم STUN على الإنترنت العام ولها مهمة واحدة: التحقق من عنوان IP:port للطلب الوارد (من تطبيق يعمل خلف NAT) وإرسال هذا العنوان مرة أخرى كاستجابة. بعبارة أخرى، يستخدم التطبيق خادم STUN لاكتشاف عنوان IP:المنفذ من منظور عام. تتيح هذه العملية لأحد أطراف WebRTC الحصول على عنوان متاح للجميع، ثم تمريره إلى طرف آخر من خلال آلية إرسال إشارات من أجل إعداد رابط مباشر.

لا تحتاج خوادم STUN إلى تنفيذ الكثير من المهام أو تذكُّر الكثير من المعلومات، لذا يمكن لخوادم STUN ذات المواصفات المنخفضة نسبيًا معالجة عدد كبير من الطلبات.

تنجح معظم مكالمات WebRTC في إنشاء اتصال باستخدام STUN، ولكن قد يكون عددها أقل في المكالمات بين الأجهزة النظيرة خلف جدران الحماية وإعدادات NAT المعقدة.

اتصال من نظير إلى نظير باستخدام خادم STUN
استخدام خوادم STUN للحصول على عناوين IP:المنفذ العامة

TURN

يحاول RTCPeerConnection إعداد اتصال مباشر بين النظراء عبر بروتوكول UDP. وفي حال تعذّر ذلك، يلجأ RTCPeerConnection إلى بروتوكول TCP. وإذا تعذّر ذلك، يمكن استخدام خوادم TURN كخيار بديل، حيث تنقل البيانات بين نقاط النهاية.

للتذكير، يُستخدم خادم TURN لنقل الصوت والفيديو وبث البيانات بين الأقران، وليس لنقل بيانات الإشارات.

تحتوي خوادم TURN على عناوين عامة، لذا يمكن للأقران الاتصال بها حتى إذا كانت الأقران خلف جدران حماية أو خوادم وكيلة. تؤدي خوادم TURN مهمة واحدة: نقل البث وعلى عكس خوادم STUN، تستهلك هذه الخوادم بطبيعتها قدرًا كبيرًا من معدل نقل البيانات.

اتصال من نظير إلى نظير باستخدام خادم STUN
الخدمات الكاملة: STUN وTURN والإشارات

يوضّح هذا المخطّط البياني طريقة عمل TURN. لم ينجح بروتوكول STUN النقي، لذا يلجأ كل طرف إلى استخدام خادم TURN.

نشر خوادم STUN وTURN

لأغراض الاختبار، تشغّل Google خادم STUN عامًا، stun.l.google.com:19302، كما يستخدمه appr.tc. بالنسبة إلى خدمة STUN/TURN في بيئة الإنتاج، استخدِم rfc5766-turn-server. يتوفّر الرمز المصدر لخوادم STUN وTURN على GitHub، حيث يمكنك أيضًا العثور على روابط تؤدي إلى عدة مصادر للمعلومات حول تثبيت الخادم. تتوفّر أيضًا صورة جهاز افتراضي لخدمات Amazon Web Services.

يتوفّر خادم TURN بديل باسم restund، وهو متاح كـرمز مصدر وأيضًا لخدمات AWS. يمكنك إعداد ردّ الأموال على جهاز الكمبيوتر :

  1. افتح جدار الحماية حسب الضرورة لـ tcp=443 وudp/tcp=3478.
  2. أنشئ أربعة مثيلات، واحد لكل عنوان IP عام، صورة Ubuntu 12.06 العادية.
  3. اضبط إعدادات جدار الحماية المحلي (السماح لأي جهاز من أي شبكة).
  4. تثبيت الأدوات: shell sudo apt-get install make sudo apt-get install gcc
  5. ثبِّت libre من creytiv.com/re.html.
  6. يمكنك جلب restund من creytiv.com/restund.html وفك ضغطه.
  7. wget hancke.name/restund-auth.patch وتطبيقها باستخدام patch -p1 < restund-auth.patch.
  8. شغِّل make وsudo make install لـ libre وrestund.
  9. يمكنك تكييف restund.conf حسب احتياجاتك (استبدِل عناوين IP وتأكَّد من أنّها تحتوي على السر المشترك نفسه) وانسخها إلى /etc.
  10. انسخ restund/etc/restund إلى /etc/init.d/.
  11. اضبط restund:
    1. اضبط LD_LIBRARY_PATH.
    2. انسخ restund.conf إلى /etc/restund.conf.
    3. اضبط restund.conf لاستخدام الـ 10 أرقام الصحيحة. عنوان IP
  12. تشغيل restund
  13. اختبار استخدام عميل stund من جهاز بعيد: ./client IP:port

بروتوكول WebRTC المتعدّد الأطراف

قد ترغب أيضًا في إلقاء نظرة على معيار IETF المقترح من "جاستن أوبيرتي" بشأن REST API للوصول إلى خدمات TURN.

هناك العديد من حالات الاستخدام لبث الوسائط التي تتجاوز المكالمات الفردية. على سبيل المثال، عقد اجتماع فيديو بين مجموعة من الزملاء أو فعالية عامة مع متحدث واحد ومئات أو ملايين المشاهدين.

يمكن لتطبيق WebRTC استخدام عدة اتصالات RTCPeerConnections بحيث تتصل كل نقطة نهاية بكل نقطة نهاية أخرى في إعدادات الشبكة. هذا هو النهج الذي تتبعه تطبيقات مثل talky.io، وهو يعمل بشكل جيد للغاية مع عدد قليل من الأقران. بعد ذلك، يصبح استهلاك المعالجة والنطاق الترددي مفرطًا، خاصةً لعملاء تطبيقات الجوّال.

الشبكة المتداخلة: مكالمة صغيرة بين عدة أطراف
بنية الشبكة المتداخلة الكاملة: الجميع متصلون بالجميع

بدلاً من ذلك، يمكن لتطبيق WebRTC اختيار نقطة نهاية واحدة لتوزيع أحداث البث على جميع النقاط الأخرى في إعدادات النجمة. سيكون من الممكن أيضًا تشغيل نقطة نهاية WebRTC على خادم وإنشاء آلية إعادة التوزيع الخاصة بك (يتم توفير نموذج تطبيق عميل بواسطة webrtc.org).

يمكن استخدام MediaStream من RTCPeerConnection كإدخال لآخر. ويمكن أن يتيح ذلك بنية أكثر مرونة لأنّه يسمح لتطبيق ويب بمعالجة توجيه المكالمات من خلال اختيار النظير الآخر الذي سيتم الاتصال به. للاطّلاع على ذلك عمليًا، يمكنك مراجعة نماذج WebRTC لاتصال شبكة الند للند ونماذج WebRTC لاتصالات شبكة الند للند المتعددة.

وحدة التحكّم المتعددة النقاط

الخيار الأفضل لعدد كبير من نقاط النهاية هو استخدام وحدة تحكّم متعددة النقاط (MCU). هذا خادم يعمل كجسر لتوزيع الوسائط بين عدد كبير من المشاركين. يمكن لوحدات MCU التعامل مع درجات دقة وبرامج ترميز وعدد اللقطات في الثانية مختلفة في اجتماع فيديو، والتعامل مع تحويل الرموز، وإعادة توجيه البث الانتقائي، ومزج الصوت والفيديو أو تسجيلهما. بالنسبة إلى المكالمات المتعددة الأطراف، هناك عدد من المشاكل التي يجب مراعاتها، لا سيما كيفية عرض مدخلات فيديو متعددة ومزج الصوت من مصادر متعددة. تحاول أيضًا المنصات السحابية، مثل vLine، تحسين توجيه الزيارات.

يمكنك شراء حزمة أجهزة MCU كاملة أو إنشاء حزمة خاصة بك.

الرؤية الخلفية لجهاز Cisco MCU5300
الجهة الخلفية من وحدة التحكم المتعددة النقاط (MCU) من Cisco

تتوفّر عدة خيارات لبرامج MCU المفتوحة المصدر. على سبيل المثال، يُنتج Licode (المعروف سابقًا باسم Lynckia) وحدة MCU مفتوحة المصدر لـ WebRTC. يحتوي OpenTok على Mantis.

ما وراء المتصفّحات: VoIP والهواتف والمراسلة

تتيح الطبيعة الموحّدة لـ WebRTC إمكانية إنشاء اتصال بين تطبيق WebRTC يعمل في متصفح وجهاز أو نظام أساسي يعمل على نظام أساسي آخر للاتصال، مثل الهاتف أو نظام مؤتمرات الفيديو.

SIP هو بروتوكول إرسال إشارات تستخدمه أنظمة VoIP وأنظمة عقد اجتماعات الفيديو. لإتاحة الاتصال بين تطبيق ويب WebRTC وعميل SIP، مثل نظام عقد اجتماعات الفيديو، يحتاج WebRTC إلى خادم وكيل للتوسّط في الإشارات. يجب أن تتدفق الإشارات عبر البوابة، ولكن بمجرد إنشاء الاتصال، يمكن أن تتدفق حركة مرور SRTP (الفيديو والصوت) من نظير إلى نظير.

شبكة الهاتف العامة (PSTN) هي شبكة تبديل الدارات لجميع الهواتف التناظرية "القديمة العادية". بالنسبة إلى المكالمات بين تطبيقات الويب التي تستخدم WebRTC والهواتف، يجب أن تمرّ حركة البيانات عبر بوابة شبكة الهاتف العامة (PSTN). وبالمثل، تحتاج تطبيقات الويب التي تستخدم WebRTC إلى خادم XMPP وسيط للتواصل مع نقاط نهاية Jingle، مثل برامج المراسلة الفورية. طوّرت Google بروتوكول Jingle كإضافة إلى بروتوكول XMPP لإتاحة المكالمات الصوتية ومكالمات الفيديو لخدمات المراسلة. تستند عمليات تنفيذ WebRTC الحالية إلى مكتبة libjingle بلغة C++‎، وهي عملية تنفيذ لبروتوكول Jingle تم تطويرها في البداية لتطبيق Talk.

يستفيد عدد من التطبيقات والمكتبات والمنصات من قدرة WebRTC على التواصل مع العالم الخارجي. على سبيل المثال:

  • ‫jsSIP: مكتبة JavaScript SIP
  • Phono: واجهة برمجة تطبيقات هاتفية مفتوحة المصدر تستند إلى JavaScript ومصمَّمة كمكوّن إضافي
  • ‫Twilio: المكالمات الصوتية والمراسلة

مزيد من المعلومات

يقدّم الكتاب المؤلّف من 350 صفحة WebRTC: APIs and RTCWEB Protocols of the HTML5 Real-Time Web الكثير من التفاصيل حول مسارات البيانات والإشارات، ويتضمّن عددًا من المخططات البيانية المفصّلة لتخطيط الشبكة.