ساخت سرویس‌های بک‌اند اپلیکیشن WebRTC

منتشر شده در: ۴ نوامبر ۲۰۱۳

WebRTC امکان ارتباط نظیر به نظیر را فراهم می‌کند. این فناوری به سرورهایی نیاز دارد تا کلاینت‌ها بتوانند فراداده‌ها را تبادل کرده و ارتباطات را از طریق فرآیندی به نام سیگنالینگ هماهنگ کنند. سرورها همچنین به کلاینت‌ها کمک می‌کنند تا با مترجم‌های آدرس شبکه (NAT) و فایروال‌ها کنار بیایند.

در اینجا، شما یاد می‌گیرید که چگونه یک سرویس سیگنالینگ بسازید و با پیچیدگی‌های اتصال در دنیای واقعی با سرورهای STUN و TURN کنار بیایید. همچنین خواهید فهمید که چگونه برنامه‌های WebRTC می‌توانند تماس‌های چندطرفه را مدیریت کنند و با سرویس‌هایی مانند VoIP و PSTN (که به عنوان تلفن نیز شناخته می‌شوند) تعامل داشته باشند.

سیگنالینگ چیست؟

سیگنالینگ فرآیند هماهنگی ارتباطات است. برای اینکه یک برنامه WebRTC بتواند یک تماس برقرار کند، کلاینت‌های آن باید اطلاعات زیر را رد و بدل کنند:

  • پیام‌های کنترل جلسه که برای باز یا بسته کردن ارتباط استفاده می‌شوند
  • پیام‌های خطا
  • فراداده‌های رسانه‌ای، مانند کدک‌ها، تنظیمات کدک، پهنای باند و انواع رسانه
  • داده‌های کلیدی مورد استفاده برای ایجاد اتصالات امن
  • داده‌های شبکه، مانند آدرس IP و پورت میزبان که توسط دنیای خارج دیده می‌شود

این فرآیند سیگنال‌دهی به روشی نیاز دارد که کلاینت‌ها بتوانند پیام‌ها را بین خود رد و بدل کنند. این مکانیزم توسط APIهای WebRTC پیاده‌سازی نشده است. در عوض، شما باید خودتان آن را بسازید.

چرا سیگنالینگ توسط WebRTC تعریف نشده است؟

برای جلوگیری از افزونگی و به حداکثر رساندن سازگاری با فناوری‌های موجود، روش‌ها و پروتکل‌های سیگنالینگ توسط استانداردهای WebRTC مشخص نشده‌اند. این رویکرد توسط پروتکل ایجاد جلسه جاوا اسکریپت (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) بیندازید.

در نظر داشته باشید که WebRTC به گونه‌ای طراحی شده است که می‌توان پیشنهاد یا پاسخ را قبل از تنظیم به عنوان توضیحات محلی یا از راه دور، با ویرایش مقادیر موجود در متن SDP، تغییر داد. به عنوان مثال، می‌توان از تابع preferAudioCodec() در appr.tc برای تنظیم کدک و بیت‌ریت پیش‌فرض استفاده کرد. دستکاری SDP با جاوا اسکریپت تا حدودی دشوار است و بحث‌هایی در مورد اینکه آیا نسخه‌های آینده WebRTC باید به جای آن از JSON استفاده کنند یا خیر، وجود دارد، اما مزایایی برای پایبندی به SDP وجود دارد.

API و سیگنالینگ RTCPeerConnection : پیشنهاد، پاسخ و کاندید

RTCPeerConnection برنامه‌نویسی کاربردی (API) است که توسط برنامه‌های WebRTC برای ایجاد ارتباط بین همتاها و برقراری ارتباط صوتی و تصویری استفاده می‌شود.

برای مقداردهی اولیه این فرآیند، RTCPeerConnection دو وظیفه دارد:

  • شرایط رسانه‌های محلی، مانند وضوح تصویر و قابلیت‌های کدک را بررسی کنید. این همان فراداده‌ای است که برای مکانیسم پیشنهاد و پاسخ استفاده می‌شود.
  • آدرس‌های شبکه بالقوه برای میزبان برنامه، که به عنوان کاندید شناخته می‌شوند، را دریافت کنید.

پس از اینکه این داده‌های محلی مشخص شدند، باید از طریق یک مکانیسم سیگنالینگ با همتای راه دور تبادل شوند.

تصور کنید آلیس می‌خواهد با ایو تماس بگیرد . در اینجا مکانیسم کامل پاسخ با تمام جزئیات تکان‌دهنده‌اش آمده است:

  1. آلیس یک شیء RTCPeerConnection ایجاد می‌کند.
  2. آلیس با استفاده از متد createOffer() در RTCPeerConnection یک پیشنهاد (توضیح جلسه SDP) ایجاد می‌کند.
  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 در گوگل کروم یا صفحه‌ی opera://webrtc-internals در اپرا دانلود کنید.

کشف همتا

این یک روش شیک برای پرسیدن این سوال است که «چطور کسی را برای صحبت پیدا کنم؟»

برای تماس‌های تلفنی، شماره تلفن‌ها و دایرکتوری‌ها را دارید. برای چت تصویری و پیام‌رسانی آنلاین، به سیستم‌های مدیریت هویت و حضور و ابزاری برای شروع جلسات توسط کاربران نیاز دارید. برنامه‌های WebRTC به روشی نیاز دارند که کلاینت‌ها بتوانند به یکدیگر علامت دهند که می‌خواهند یک تماس را شروع کنند یا به آن بپیوندند.

مکانیسم‌های کشف همتا توسط WebRTC تعریف نشده‌اند و شما در اینجا به گزینه‌های آن نمی‌پردازید. این فرآیند می‌تواند به کوچکی ارسال ایمیل یا پیام‌رسانی یک URL باشد. برای برنامه‌های چت تصویری، مانند Talky ، tawk.to و Browser Meeting ، شما با به اشتراک گذاشتن یک لینک سفارشی، افراد را به یک تماس دعوت می‌کنید. توسعه‌دهنده‌ای به نام کریس بال، یک آزمایش جذاب بدون سرور-webrtc ایجاد کرد که به شرکت‌کنندگان در تماس WebRTC امکان می‌دهد تا فراداده‌ها را از طریق هر سرویس پیام‌رسانی که دوست دارند، مانند IM، ایمیل یا homing pigeon، تبادل کنند.

چگونه می‌توانید یک سرویس سیگنالینگ بسازید؟

مجدداً تأکید می‌کنم که پروتکل‌ها و مکانیسم‌های سیگنالینگ توسط استانداردهای WebRTC تعریف نشده‌اند. هر چه انتخاب کنید، به یک سرور واسطه برای تبادل پیام‌های سیگنالینگ و داده‌های برنامه بین کلاینت‌ها نیاز دارید. متأسفانه، یک برنامه وب نمی‌تواند در اینترنت فریاد بزند: «من را به دوستم وصل کن!»

خوشبختانه پیام‌های سیگنالینگ کوچک هستند و بیشتر در ابتدای تماس رد و بدل می‌شوند. در آزمایش با appr.tc برای یک جلسه چت تصویری، در مجموع حدود 30 تا 45 پیام توسط سرویس سیگنالینگ مدیریت شد که حجم کل همه پیام‌ها حدود 10 کیلوبایت بود.

سرویس‌های سیگنالینگ WebRTC علاوه بر اینکه از نظر پهنای باند نسبتاً کم‌مصرف هستند، پردازش یا حافظه زیادی مصرف نمی‌کنند، زیرا فقط نیاز به ارسال پیام‌ها و حفظ مقدار کمی از داده‌های وضعیت جلسه، مانند اینکه کدام کلاینت‌ها متصل هستند، دارند.

ارسال پیام از سرور به کلاینت

Browser Support

  • کروم: ۶.
  • لبه: ۷۹.
  • فایرفاکس: ۶.
  • سافاری: ۵.

Source

یک سرویس پیام برای سیگنال‌دهی باید دو طرفه باشد: کلاینت به سرور و سرور به کلاینت. ارتباط دو طرفه برخلاف مدل درخواست/پاسخ HTTP کلاینت/سرور است، اما هک‌های مختلفی مانند long polling در طول سال‌های متمادی توسعه داده شده‌اند تا داده‌ها را از یک سرویس در حال اجرا روی یک وب سرور به یک برنامه وب در حال اجرا در یک مرورگر منتقل کنند.

رابط برنامه‌نویسی کاربردی EventSource رویدادهای ارسال‌شده توسط سرور، یعنی داده‌هایی که از یک وب سرور به یک مرورگر کلاینت از طریق HTTP ارسال می‌شوند را فعال می‌کند. EventSource برای پیام‌رسانی یک‌طرفه طراحی شده است، اما می‌تواند در ترکیب با XHR برای ساخت سرویسی برای تبادل پیام‌های سیگنالینگ استفاده شود. یک سرویس سیگنالینگ، پیامی را از یک فراخواننده که توسط درخواست XHR تحویل داده شده است، با ارسال آن از طریق EventSource به فراخوان‌شونده منتقل می‌کند.

Browser Support

  • کروم: ۵.
  • لبه: ۱۲.
  • فایرفاکس: ۱۱.
  • سافاری: ۵.

Source

وب‌سوکت برای ارتباط کاملاً دوطرفه کلاینت-سرور طراحی شده است، به این معنی که پیام‌ها می‌توانند همزمان در هر دو جهت جریان داشته باشند. یکی از مزایای یک سرویس سیگنالینگ ساخته شده با وب‌سوکت خالص یا رویدادهای ارسال شده توسط سرور ( EventSource ) این است که می‌توان بک‌اند این APIها را روی انواع چارچوب‌های وب رایج در اکثر بسته‌های میزبانی وب برای زبان‌هایی مانند PHP، Python و Ruby پیاده‌سازی کرد.

همه مرورگرهایی که از WebRTC پشتیبانی می‌کنند، از WebSocket نیز پشتیبانی می‌کنند. TLS باید برای همه اتصالات استفاده شود تا اطمینان حاصل شود که پیام‌ها به صورت رمزگذاری نشده قابل رهگیری نیستند و همچنین مشکلات مربوط به پیمایش پروکسی کاهش یابد . برای اطلاعات بیشتر در مورد WebSocket و پیمایش پروکسی، فصل WebRTC را در کتاب «شبکه‌سازی مرورگر با کارایی بالا» نوشته ایلیا گریگوریک مطالعه کنید.

شما می‌توانید با وادار کردن کلاینت‌های WebRTC به نظرسنجی مکرر از یک سرور پیام‌رسان از طریق Ajax، سیگنال‌دهی را مدیریت کنید، اما این کار منجر به درخواست‌های شبکه‌ای اضافی زیادی می‌شود که به ویژه برای دستگاه‌های تلفن همراه مشکل‌ساز است. حتی پس از برقراری یک جلسه، در صورت تغییر یا خاتمه جلسه توسط سایر همسالان، همسالان باید برای پیام‌های سیگنال‌دهی نظرسنجی کنند. مثال برنامه WebRTC Book این گزینه را با برخی بهینه‌سازی‌ها برای فرکانس نظرسنجی در نظر می‌گیرد.

سیگنالینگ مقیاس

اگرچه یک سرویس سیگنالینگ پهنای باند و CPU نسبتاً کمی را به ازای هر کلاینت مصرف می‌کند، اما سرورهای سیگنالینگ برای یک برنامه محبوب ممکن است مجبور باشند پیام‌های زیادی را از مکان‌های مختلف با سطوح بالای همزمانی مدیریت کنند. برنامه‌های WebRTC که ترافیک زیادی دریافت می‌کنند، به سرورهای سیگنالینگی نیاز دارند که بتوانند بار قابل توجهی را مدیریت کنند. در اینجا به جزئیات نمی‌پردازیم، اما تعدادی گزینه برای پیام‌رسانی با حجم و عملکرد بالا وجود دارد، از جمله موارد زیر:

  • پروتکل پیام‌رسانی و حضور توسعه‌پذیر (XMPP) پروتکلی است که برای پیام‌رسانی فوری توسعه داده شده و می‌تواند برای سیگنال‌دهی نیز استفاده شود. پیاده‌سازی‌های سرور شامل ejabberd و Openfire هستند. کلاینت‌های جاوا اسکریپت، مانند Strophe.js ، از BOSH برای شبیه‌سازی جریان دوطرفه استفاده می‌کنند، اما BOSH در مقایسه با WebSocket ممکن است ناکارآمد و مقیاس‌پذیر نباشد.

  • کتابخانه‌های متن‌باز، مانند ZeroMQ (همانطور که توسط TokBox برای سرویس Rumour خود استفاده می‌شود) و OpenMQ ( NullMQ مفاهیم ZeroMQ را با استفاده از پروتکل STOMP از طریق WebSocket در پلتفرم‌های وب اعمال می‌کند.)

  • پلتفرم‌های پیام‌رسان ابری تجاری که از WebSocket استفاده می‌کنند (هرچند ممکن است به روش long polling روی بیاورند)، مانند Pusher و Kaazing .

  • پلتفرم‌های تجاری WebRTC، مانند vLine

ساخت یک سرویس سیگنالینگ با Socket.io روی Node.

کد زیر برای یک برنامه وب است که از یک سرویس سیگنالینگ ساخته شده با Socket.io در Node . استفاده می‌کند. طراحی Socket.io ساخت سرویسی برای تبادل پیام را آسان‌تر می‌کند و Socket.io به دلیل مفهوم داخلی اتاق‌ها (rooms) به طور خاص برای سیگنالینگ WebRTC مناسب است. این مثال برای تعداد نسبتاً کمی از کاربران طراحی شده است، نه برای استفاده در محیط عملیاتی.

Socket.io از WebSocket به همراه fallbackهایی مانند AJAX long polling، AJAX multipart streaming، Forever Iframe و JSONP polling استفاده می‌کند. این پلتفرم به بک‌اندهای مختلفی منتقل شده است، اما شاید بیشتر به خاطر نسخه 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>

فایل جاوا اسکریپت 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 را باز کنید. برای دیدن آنچه اتفاق می‌افتد، کنسول را بررسی کنید. در کروم و اپرا، می‌توانید از طریق ابزارهای توسعه‌دهنده گوگل کروم با Ctrl+Shift+J (یا Command+Option+J در مک) به کنسول دسترسی پیدا کنید.

هر رویکردی که برای سیگنال‌دهی انتخاب کنید، بک‌اند و برنامه کلاینت شما باید خدماتی مشابه این مثال ارائه دهند.

مشکلات سیگنال

  • RTCPeerConnection تا زمانی که setLocalDescription() فراخوانی نشود، شروع به جمع‌آوری کاندیداها نمی‌کند. این موضوع در پیش‌نویس JSEP IETF الزامی شده است.
  • از Trickle ICE بهره ببرید. به محض رسیدن کاندیداها، تابع addIceCandidate() را فراخوانی کنید.

سرورهای سیگنال آماده

اگر نمی‌خواهید خودتان سرور بسازید، چندین سرور سیگنالینگ WebRTC موجود است که مانند مثال قبلی از Socket.IO استفاده می‌کنند و با کتابخانه‌های جاوا اسکریپت کلاینت WebRTC یکپارچه شده‌اند:

  • webRTC.io یکی از اولین کتابخانه‌های انتزاعی برای WebRTC است.
  • Signalmaster یک سرور سیگنالینگ است که برای استفاده با کتابخانه کلاینت جاوا اسکریپت SimpleWebRTC ایجاد شده است.

اگر اصلاً نمی‌خواهید کدی بنویسید، پلتفرم‌های تجاری کامل WebRTC از شرکت‌هایی مانند vLine ، OpenTok و Asterisk در دسترس هستند.

برای اطلاع، اریکسون در روزهای اولیه WebRTC یک سرور سیگنالینگ با استفاده از PHP روی آپاچی ساخت. این روش اکنون تا حدودی منسوخ شده است، اما اگر به دنبال چیزی مشابه هستید، ارزش دارد که کد آن را بررسی کنید.

امنیت سیگنال

رمزگذاری برای همه اجزای WebRTC الزامی است.

با این حال، مکانیسم‌های سیگنالینگ توسط استانداردهای WebRTC تعریف نشده‌اند، بنابراین ایمن‌سازی سیگنالینگ به شما بستگی دارد. اگر یک مهاجم موفق به ربودن سیگنالینگ شود، می‌تواند جلسات را متوقف کند، اتصالات را تغییر مسیر دهد و محتوا را ضبط، تغییر یا تزریق کند.

مهم‌ترین عامل در ایمن‌سازی سیگنالینگ، استفاده از پروتکل‌های امن است: HTTPS و WSS، مانند TLS. این امر تضمین می‌کند که پیام‌های رمزگذاری نشده قابل رهگیری نیستند. همچنین، مراقب باشید که پیام‌های سیگنالینگ را به گونه‌ای پخش نکنید که توسط سایر تماس‌گیرندگانی که از همان سرور سیگنالینگ استفاده می‌کنند، قابل دسترسی باشند.

با NATها و فایروال‌ها کنار بیایید

برای سیگنال‌دهی فراداده، برنامه‌های WebRTC از یک سرور واسطه استفاده می‌کنند، اما برای پخش رسانه و داده‌های واقعی پس از برقراری یک جلسه، RTCPeerConnection تلاش می‌کند تا کلاینت‌ها را مستقیماً یا به صورت نظیر به نظیر متصل کند.

در یک دنیای ساده‌تر، هر نقطه پایانی WebRTC یک آدرس منحصر به فرد خواهد داشت که می‌تواند برای برقراری ارتباط مستقیم با سایر همتایان خود تبادل کند.

اتصال ساده نظیر به نظیر
جهانی بدون NAT و فایروال

در واقعیت، اکثر دستگاه‌ها پشت یک یا چند لایه NAT قرار دارند، برخی نرم‌افزار آنتی‌ویروس دارند که پورت‌ها و پروتکل‌های خاصی را مسدود می‌کند و بسیاری پشت پروکسی‌ها و فایروال‌های شرکتی هستند. در واقع، یک فایروال و NAT ممکن است توسط یک دستگاه واحد، مانند یک روتر WIFI خانگی، پیاده‌سازی شوند.

نظیرهای پشت 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 همکاری می‌کند.

بی حس کردن

NATها به دستگاهی یک آدرس IP برای استفاده در یک شبکه محلی خصوصی ارائه می‌دهند، اما این آدرس نمی‌تواند به صورت خارجی استفاده شود. بدون یک آدرس عمومی، هیچ راهی برای برقراری ارتباط بین دستگاه‌های WebRTC وجود ندارد. برای حل این مشکل، WebRTC از STUN استفاده می‌کند.

سرورهای STUN در اینترنت عمومی قرار دارند و یک وظیفه دارند: آدرس IP:port یک درخواست ورودی (از برنامه‌ای که پشت NAT اجرا می‌شود) را بررسی کرده و آن آدرس را به عنوان پاسخ ارسال کنند. به عبارت دیگر، برنامه از یک سرور STUN برای کشف IP:port خود از منظر عمومی استفاده می‌کند. این فرآیند به یک همتای WebRTC این امکان را می‌دهد که یک آدرس قابل دسترس عمومی برای خود دریافت کند و سپس آن را از طریق یک مکانیسم سیگنالینگ به همتای دیگری منتقل کند تا یک لینک مستقیم برقرار شود.

سرورهای STUN لازم نیست کار زیادی انجام دهند یا اطلاعات زیادی را به خاطر بسپارند، بنابراین سرورهای STUN با مشخصات نسبتاً پایین می‌توانند تعداد زیادی درخواست را مدیریت کنند.

بیشتر تماس‌های WebRTC با استفاده از STUN با موفقیت ارتباط برقرار می‌کنند، اگرچه ممکن است این ارتباط برای تماس‌های بین دستگاه‌های متصل پشت فایروال‌ها و پیکربندی‌های پیچیده NAT کمتر باشد.

اتصال نظیر به نظیر با استفاده از سرور STUN
استفاده از سرورهای STUN برای دریافت آدرس‌های IP:port عمومی

نوبت

RTCPeerConnection سعی می‌کند ارتباط مستقیم بین همتاها را از طریق UDP برقرار کند. اگر این کار با شکست مواجه شود، RTCPeerConnection به TCP متوسل می‌شود. در صورت عدم موفقیت، سرورهای TURN می‌توانند به عنوان پشتیبان استفاده شوند و داده‌ها را بین نقاط انتهایی منتقل کنند.

فقط برای تکرار، TURN برای انتقال جریان صدا، تصویر و داده بین همتاها استفاده می‌شود، نه برای سیگنال‌دهی داده!

سرورهای TURN آدرس‌های عمومی دارند، بنابراین حتی اگر همتاها پشت فایروال‌ها یا پروکسی‌ها باشند، می‌توانند با آنها تماس بگیرند. سرورهای TURN یک وظیفه دارند: رله کردن یک جریان. برخلاف سرورهای STUN، آنها ذاتاً پهنای باند زیادی مصرف می‌کنند.

اتصال نظیر به نظیر با استفاده از سرور STUN
مانتی کامل: بی‌حس کردن، چرخاندن و علامت دادن

این نمودار TURN را در عمل نشان می‌دهد. STUN خالص موفق نشد، بنابراین هر یک از طرفین به استفاده از یک سرور TURN متوسل می‌شوند.

استقرار سرورهای STUN و TURN

برای آزمایش، گوگل یک سرور عمومی STUN به stun.l.google.com:19302 را اجرا می‌کند، همانطور که توسط appr.tc استفاده می‌شود. برای یک سرویس STUN/TURN در مرحله تولید، از rfc5766-turn-server استفاده کنید. کد منبع سرورهای STUN و TURN در GitHub موجود است، جایی که می‌توانید لینک‌هایی به چندین منبع اطلاعات در مورد نصب سرور نیز پیدا کنید. یک تصویر ماشین مجازی برای سرویس‌های وب آمازون نیز موجود است.

یک سرور جایگزین TURN به صورت restund موجود است که به صورت کد منبع و همچنین برای AWS در دسترس است. می‌توانید یک restund را روی Compute تنظیم کنید:

  1. در صورت لزوم، فایروال را برای tcp=443 و udp/tcp=3478 باز کنید.
  2. چهار نمونه ایجاد کنید، یکی برای هر IP عمومی، تصویر استاندارد اوبونتو ۱۲.۰۶.
  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 wget و با patch -p1 < restund-auth.patch آن را اعمال کنید.
  8. برای نصب libre و restund، make و sudo make install اجرا کنید.
  9. restund.conf با نیازهای خود تطبیق دهید (آدرس‌های IP را جایگزین کنید و مطمئن شوید که حاوی همان رمز مشترک است) و آن را در /etc کپی کنید.
  10. restund/etc/restund را در /etc/init.d/ کپی کنید.
  11. پیکربندی حالت استراحت:
    1. مقدار LD_LIBRARY_PATH را تنظیم کنید.
    2. restund.conf را در /etc/restund.conf کپی کنید.
    3. restund.conf را طوری تنظیم کنید که از آدرس IP صحیح استفاده کند.
  12. دویدن را متوقف کنید
  13. تست با استفاده از کلاینت stud از دستگاه راه دور: ./client IP:port

WebRTC چند حزبی

همچنین می‌توانید نگاهی به استاندارد پیشنهادی IETF جاستین اوبرتی برای یک API REST جهت دسترسی به سرویس‌های TURN بیندازید.

موارد استفاده‌ی بی‌شماری برای پخش رسانه وجود دارد که فراتر از یک تماس یک به یک است. به عنوان مثال، کنفرانس ویدیویی بین گروهی از همکاران یا یک رویداد عمومی با یک سخنران و صدها یا میلیون‌ها بیننده.

یک برنامه WebRTC می‌تواند از چندین RTCPeerConnections استفاده کند تا هر نقطه پایانی در یک پیکربندی مش به هر نقطه پایانی دیگر متصل شود. این رویکردی است که توسط برنامه‌هایی مانند talky.io اتخاذ شده است و برای تعداد کمی از همتاها به طرز چشمگیری خوب کار می‌کند. فراتر از آن، مصرف پردازش و پهنای باند، به خصوص برای کلاینت‌های تلفن همراه، بیش از حد می‌شود.

مش: تماس کوچک N-way
توپولوژی مش کامل: همه به همه متصل هستند

از طرف دیگر، یک برنامه WebRTC می‌تواند یک نقطه پایانی را برای توزیع جریان‌ها به سایر نقاط در یک پیکربندی ستاره‌ای انتخاب کند. همچنین می‌توان یک نقطه پایانی WebRTC را روی یک سرور اجرا کرد و مکانیزم توزیع مجدد خود را ساخت (یک برنامه کلاینت نمونه توسط webrtc.org ارائه شده است).

یک MediaStream از یک RTCPeerConnection می‌تواند به عنوان ورودی برای دیگری استفاده شود. این می‌تواند معماری‌های انعطاف‌پذیرتری را فعال کند زیرا به یک برنامه وب امکان می‌دهد تا با انتخاب اینکه به کدام همتای دیگر متصل شود، مسیریابی تماس را مدیریت کند. برای مشاهده این موضوع در عمل، به نمونه‌های WebRTC Peer connection relay و نمونه‌های WebRTC Multiple peer connections مراجعه کنید.

واحد کنترل چند نقطه‌ای

یک گزینه بهتر برای تعداد زیادی از نقاط پایانی، استفاده از یک واحد کنترل چند نقطه‌ای (MCU) است. این سروری است که به عنوان پلی برای توزیع رسانه بین تعداد زیادی از شرکت‌کنندگان عمل می‌کند. MCUها می‌توانند در یک کنفرانس ویدیویی با وضوح‌ها، کدک‌ها و نرخ فریم‌های مختلف کار کنند؛ کدگذاری را انجام دهند؛ ارسال جریان انتخابی را انجام دهند؛ و صدا و تصویر را میکس یا ضبط کنند. برای تماس‌های چندطرفه، تعدادی مسئله وجود دارد که باید در نظر گرفته شود، به ویژه نحوه نمایش چندین ورودی ویدئو و میکس صدا از چندین منبع. پلتفرم‌های ابری، مانند vLine ، همچنین تلاش می‌کنند مسیریابی ترافیک را بهینه کنند.

می‌توان یک بسته سخت‌افزاری کامل MCU خریداری کرد یا خودتان آن را ساخت.

نمای پشت Cisco MCU5300
پشت یک MCU سیسکو

چندین گزینه نرم‌افزاری متن‌باز MCU در دسترس هستند. برای مثال، Licode (که قبلاً با نام Lynckia شناخته می‌شد) یک MCU متن‌باز برای WebRTC تولید می‌کند. OpenTok نرم‌افزار Mantis را دارد.

فراتر از مرورگرها: VoIP، تلفن‌ها و پیام‌رسانی

ماهیت استاندارد WebRTC امکان برقراری ارتباط بین یک برنامه WebRTC که در یک مرورگر اجرا می‌شود و یک دستگاه یا پلتفرم که روی یک پلتفرم ارتباطی دیگر مانند تلفن یا سیستم کنفرانس ویدیویی اجرا می‌شود را فراهم می‌کند.

SIP یک پروتکل سیگنالینگ است که توسط سیستم‌های VoIP و کنفرانس ویدیویی استفاده می‌شود. برای فعال کردن ارتباط بین یک برنامه وب WebRTC و یک کلاینت SIP، مانند یک سیستم کنفرانس ویدیویی، WebRTC به یک سرور پروکسی برای واسطه‌گری سیگنالینگ نیاز دارد. سیگنالینگ باید از طریق دروازه جریان یابد، اما پس از برقراری ارتباط، ترافیک SRTP (تصویر و صدا) می‌تواند به صورت نظیر به نظیر جریان یابد.

شبکه تلفن عمومی (PSTN) شبکه سوئیچینگ مداری همه تلفن‌های آنالوگ "قدیمی" است. برای تماس‌های بین برنامه‌های وب WebRTC و تلفن‌ها، ترافیک باید از طریق یک دروازه PSTN عبور کند. به همین ترتیب، برنامه‌های وب WebRTC برای برقراری ارتباط با نقاط انتهایی Jingle مانند کلاینت‌های IM به یک سرور واسطه XMPP نیاز دارند. Jingle توسط گوگل به عنوان افزونه‌ای برای XMPP توسعه داده شد تا صدا و تصویر را برای سرویس‌های پیام‌رسانی فعال کند. پیاده‌سازی‌های فعلی WebRTC بر اساس کتابخانه libjingle ++C است، پیاده‌سازی‌ای از Jingle که در ابتدا برای Talk توسعه داده شده بود.

تعدادی از برنامه‌ها، کتابخانه‌ها و پلتفرم‌ها از قابلیت WebRTC برای برقراری ارتباط با دنیای خارج استفاده می‌کنند. برای مثال:

  • jsSIP : کتابخانه جاوا اسکریپت SIP
  • فونو : رابط برنامه‌نویسی کاربردی تلفن جاوا اسکریپت متن‌باز که به عنوان افزونه ساخته شده است
  • Twilio : صدا و پیام رسانی

اطلاعات بیشتر

کتاب ۳۵۰ صفحه‌ای WebRTC: APIها و پروتکل‌های RTCWEB از وب بلادرنگ HTML5 جزئیات زیادی در مورد داده‌ها و مسیرهای سیگنال‌دهی ارائه می‌دهد و شامل تعدادی نمودار توپولوژی شبکه دقیق است.