WebRTC - Hızlı Kılavuz

Web, gerçek zamanlı iletişime artık yabancı değil WebRTC (Web Real-Time Communication)devreye giriyor. Mayıs 2011'de piyasaya sürülmesine rağmen halen gelişmekte ve standartları değişmektedir. Protokoller kümesi tarafından standardize edilmiştir grubunu Çalışma WEB tarayıcılarda Gerçek Zamanlı İletişim dehttp://tools.ietf.org/wg/rtcweb/ of IETF (Internet Engineering Task Force)Yeni API setleri Web Gerçek Zamanlı İletişim Çalışma Grubu tarafından standartlaştırılırken ,http://www.w3.org/2011/04/webrtc/ of W3C (World Wide Web Consortium). WebRTC'nin ortaya çıkmasıyla, modern web uygulamaları ses ve video içeriğini milyonlarca kişiye kolayca aktarabilir.

Temel Şema

WebRTC, diğer web tarayıcılarıyla hızlı ve kolay bir şekilde eşler arası bağlantılar kurmanıza olanak tanır. Böyle bir uygulamayı sıfırdan oluşturmak için veri kaybı, bağlantının kesilmesi ve NAT geçişi gibi tipik sorunlarla ilgilenen çok sayıda çerçeveye ve kitaplığa ihtiyacınız olacaktır. WebRTC ile tüm bunlar, kutudan çıkar çıkmaz tarayıcıda yerleşik olarak gelir. Bu teknolojinin herhangi bir eklentiye veya üçüncü taraf yazılıma ihtiyacı yoktur. Açık kaynaklıdır ve kaynak kodu şu adreste ücretsiz olarak mevcuttur:http://www.webrtc.org/.

WebRTC API, medya yakalama, ses ve video kodlama ve kod çözme, taşıma katmanı ve oturum yönetimini içerir.

Medya Yakalama

İlk adım, kullanıcının cihazının kamera ve mikrofonuna erişim sağlamaktır. Mevcut cihaz türlerini tespit eder, bu cihazlara erişim için kullanıcı izni alır ve akışı yönetiriz.

Ses ve Video Kodlama ve Kod Çözme

İnternet üzerinden ses ve video veri akışı göndermek kolay bir iş değildir. Bu, kodlama ve kod çözmenin kullanıldığı yerdir. Bu, video karelerini ve ses dalgalarını daha küçük parçalara ayırma ve sıkıştırma işlemidir. Bu algoritmayacodec. Farklı iş hedefleri olan farklı şirketler tarafından sürdürülen muazzam miktarda farklı kodlayıcı vardır. WebRTC içinde H.264, iSAC, Opus ve VP8 gibi birçok kodek de vardır. İki tarayıcı birbirine bağlandığında, iki kullanıcı arasında desteklenen en uygun codec bileşenini seçerler. Neyse ki, WebRTC kodlamanın çoğunu perde arkasında yapıyor.

Taşıma Katmanı

Taşıma katmanı, paketlerin sırasını yönetir, paket kaybıyla ilgilenir ve diğer kullanıcılara bağlanır. Yine WebRTC API, bağlantıyla ilgili sorunlar olduğunda bize haber veren olaylara kolay erişim sağlar.

Oturum Yönetimi

Oturum yönetimi, bağlantıların yönetilmesi, açılması ve organize edilmesiyle ilgilenir. Bu genelliklesignaling. Ses ve video akışlarını kullanıcıya aktarırsanız, yardımcı verileri aktarmak da mantıklıdır. Bu,RTCDataChannel API.

Google, Mozilla, Opera ve diğerleri gibi şirketlerin mühendisleri, bu gerçek zamanlı deneyimi Web'e getirmek için harika bir iş çıkardılar.

Tarayıcı Uyumluluğu

WebRTC standartları, web'de en hızlı gelişen standartlardan biridir, bu nedenle her tarayıcının aynı anda tüm aynı özellikleri desteklediği anlamına gelmez. Tarayıcınızın WebRTC'yi destekleyip desteklemediğini kontrol etmek için adresini ziyaret edebilirsiniz.http://caniuse.com/#feat=rtcpeerconnection. Tüm eğitimler boyunca, tüm örnekler için Chrome'u kullanmanızı tavsiye ederim.

WebRTC'yi denemek

Şimdi WebRTC'yi kullanmaya başlayalım. Tarayıcınızı adresindeki demo sitesine gidinhttps://apprtc.appspot.com/

"KATIL" düğmesine tıklayın. Açılır bir bildirim görmelisiniz.

Videonuzu ve sesi web sayfasında yayınlamaya başlamak için "İzin Ver" düğmesini tıklayın. Kendinize ait bir video akışı görmelisiniz.

Şimdi şu anda bulunduğunuz URL'yi yeni bir tarayıcı sekmesinde açın ve "KATIL" düğmesine tıklayın. Biri ilk müşterinizden, diğeri ikinci müşterinizden olmak üzere iki video akışı görmelisiniz.

Artık WebRTC'nin neden güçlü bir araç olduğunu anlamalısınız.

Kullanım Durumları

Gerçek zamanlı web, metin tabanlı sohbet, ekran ve dosya paylaşımı, oyun, görüntülü sohbet ve daha fazlası dahil olmak üzere yepyeni bir uygulama yelpazesinin kapısını açar. İletişimin yanı sıra WebRTC'yi aşağıdaki gibi başka amaçlar için kullanabilirsiniz:

  • gerçek zamanlı pazarlama
  • gerçek zamanlı reklamcılık
  • arka ofis iletişimi (CRM, ERP, SCM, FFM)
  • İK yönetimi
  • sosyal ağ
  • flört hizmetleri
  • çevrimiçi tıbbi konsültasyonlar
  • finansal hizmetler
  • surveillance
  • çok oyunculu oyunlar
  • canlı yayın
  • e-learning

Özet

Artık WebRTC terimini net bir şekilde anlamış olmalısınız. Tarayıcınızda zaten denediğiniz gibi, WebRTC ile ne tür uygulamaların oluşturulabileceği konusunda da bir fikriniz olmalıdır. Özetlemek gerekirse, WebRTC oldukça kullanışlı bir teknolojidir.

Genel WebRTC mimarisi büyük bir karmaşıklık düzeyine sahiptir.

Burada üç farklı katman bulabilirsiniz -

  • API for web developers - bu katman, RTCPeerConnection, RTCDataChannel ve MediaStrean nesneleri dahil olmak üzere ihtiyaç duyulan tüm API web geliştiricilerini içerir.

  • Tarayıcı üreticileri için API

  • Tarayıcı üreticilerinin bağlayabileceği geçersiz kılınabilir API.

Aktarım bileşenleri, çeşitli ağ türleri arasında bağlantı kurulmasına izin verirken, ses ve video motorları ses ve video akışlarını bir ses kartı ve kameradan ağa aktarmaktan sorumlu çerçevelerdir. Web geliştiricileri için en önemli kısım WebRTC API'dir.

WebRTC mimarisine istemci-sunucu tarafından bakarsak, en sık kullanılan modellerden birinin SIP (Session Initiation Protocol) Trapezoidinden esinlendiğini görebiliriz.

Bu modelde, her iki cihaz da farklı sunuculardan bir web uygulaması çalıştırmaktadır. RTCPeerConnection nesnesi, akışları birbirine eşler arası bağlanabilmeleri için yapılandırır. Bu sinyalleme HTTP veya WebSockets aracılığıyla yapılır.

Ancak en yaygın kullanılan model Üçgendir -

Bu modelde her iki cihaz da aynı web uygulamasını kullanır. Kullanıcı bağlantılarını yönetirken web geliştiricisine daha fazla esneklik sağlar.

WebRTC API

Birkaç ana javascript nesnesinden oluşur -

  • RTCPeerConnection
  • MediaStream
  • RTCDataChannel

RTCPeerConnection nesnesi

Bu nesne, WebRTC API'sinin ana giriş noktasıdır. Eşlere bağlanmamıza, bağlantıları başlatmamıza ve medya akışları eklememize yardımcı olur. Ayrıca başka bir kullanıcıyla bir UDP bağlantısını yönetir.

RTCPeerConnection nesnesinin ana görevi, bir eş bağlantı kurmak ve oluşturmaktır. Bağlantının anahtar noktalarını kolayca bağlayabiliriz çünkü bu nesne göründüklerinde bir dizi olayı tetikler. Bu olaylar, bağlantımızın yapılandırmasına erişmenizi sağlar -

RTCPeerConnection, bu şekilde kolayca oluşturabileceğiniz basit bir javascript nesnesidir -

[code] 
var conn = new RTCPeerConnection(conf); 

conn.onaddstream = function(stream) { 
   // use stream here 
}; 

[/code]

RTCPeerConnection nesnesi , bu öğreticilerde daha sonra ele alacağımız bir conf parametresini kabul eder . Onaddstream uzak kullanıcı kendi akran bağlantısına bir video veya ses akışı eklediğinde olayı harekete geçirilir.

MediaStream API

Modern tarayıcılar, geliştiriciye MediaStream API olarak da bilinen getUserMedia API'sine erişim sağlar . İşlevselliğin üç temel noktası vardır -

  • Bir geliştiriciye video ve ses akışlarını temsil eden bir akış nesnesine erişim sağlar

  • Bir kullanıcının cihazında birden fazla kamera veya mikrofon olması durumunda giriş kullanıcı cihazlarının seçimini yönetir

  • Kullanıcıya, s akışını her zaman almak istediği zaman soran bir güvenlik seviyesi sağlar

Bu API'yi test etmek için basit bir HTML sayfası oluşturalım. Tek bir <video> öğesi gösterecek, kullanıcıdan kamerayı kullanma iznini isteyecek ve sayfada kameradan canlı bir akış gösterecektir. Bir index.html dosyası oluşturun ve şunu ekleyin -

[code] 
<html>
 
   <head> 
      <meta charset = "utf-8"> 
   </head>
	
   <body> 
      <video autoplay></video> 
      <script src = "client.js"></script> 
   </body> 
	 
</html> 
[/code]

Ardından bir client.js dosyası ekleyin -

[code] 
//checks if the browser supports WebRTC 

function hasUserMedia() { 
   navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia 
      || navigator.mozGetUserMedia || navigator.msGetUserMedia; 
   return !!navigator.getUserMedia; 
}
 
if (hasUserMedia()) { 
   navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia
      || navigator.mozGetUserMedia || navigator.msGetUserMedia;
		
   //get both video and audio streams from user's camera 
   navigator.getUserMedia({ video: true, audio: true }, function (stream) { 
      var video = document.querySelector('video'); 
		
      //insert stream into the video tag 
      video.src = window.URL.createObjectURL(stream); 
   }, function (err) {}); 
	
}else {
   alert("Error. WebRTC is not supported!"); 
}
[/code]

Şimdi index.html'yi açın ve yüzünüzü gösteren video akışını görmelisiniz.

Ancak dikkatli olun, çünkü WebRTC yalnızca sunucu tarafında çalışır. Bu sayfayı tarayıcı ile açarsanız çalışmaz. Bu dosyaları Apache veya Node sunucularında veya hangisini tercih ettiğinizde barındırmanız gerekir.

RTCDataChannel nesnesi

Eşler arasında medya akışları göndermenin yanı sıra, DataChannel API kullanarak ek veriler de gönderebilirsiniz . Bu API, MediaStream API kadar basittir. Ana iş, mevcut bir RTCPeerConnection nesnesinden gelen bir kanal oluşturmaktır -

[code] 
var peerConn = new RTCPeerConnection(); 

//establishing peer connection 
//... 
//end of establishing peer connection 
var dataChannel = peerConnection.createDataChannel("myChannel", dataChannelOptions); 

// here we can start sending direct messages to another peer 
[/code]

Tek ihtiyacınız olan bu, sadece iki satır kod. Diğer her şey tarayıcının dahili katmanında yapılır. RTCPeerConnection nesnesi kapanana kadar herhangi bir eş bağlantıda bir kanal oluşturabilirsiniz .

Özet

Artık WebRTC mimarisini sağlam bir şekilde kavramış olmalısınız. MediaStream, RTCPeerConnection ve RTCDataChannel API'lerini de ele aldık. WebRTC API hareketli bir hedeftir, bu nedenle her zaman en son spesifikasyonları takip edin.

WebRTC uygulamalarımızı oluşturmaya başlamadan önce, kodlama ortamımızı ayarlamalıyız. Öncelikle, HTML ve Javascript'i düzenleyebileceğiniz bir metin düzenleyiciniz veya IDE'ye sahip olmalısınız. Bu öğreticiyi okurken tercih edileni zaten seçmiş olma ihtimaliniz var. Bana gelince, WebStorm IDE kullanıyorum. Deneme sürümünü şu adresten indirebilirsiniz:https://www.jetbrains.com/webstorm/. Ayrıca işletim sistemim olarak Linux Mint kullanıyorum.

Yaygın WebRTC uygulamaları için diğer gereksinim, HTML ve Javascript dosyalarını barındıracak bir sunucuya sahip olmaktır. Dosyalar gerçek bir sunucu tarafından sunulmadıkça tarayıcının kameralara ve mikrofonlara bağlanmasına izin verilmediğinden, kod yalnızca dosyalara çift tıklayarak çalışmayacaktır. Bu açıkça güvenlik sorunları nedeniyle yapılır.

Tonlarca farklı web sunucusu var, ancak bu eğiticide, Node.js'yi node-static ile kullanacağız -

  • Ziyaret etmek https://nodejs.org/en/ ve en son Node.js sürümünü indirin.

  • / Usr / local / nodejs dizinine açın.

  • /Home/YOUR_USERNAME/.profile dosyasını açın ve aşağıdaki satırı sonuna ekleyin - export PATH = $ PATH: / usr / local / nodejs / bin

  • Bilgisayarınızı yeniden başlatabilir veya kaynak /home/YOUR_USERNAME/.profile dosyasını çalıştırabilirsiniz.

  • Şimdi düğüm komutu komut satırından erişilebilir olmalıdır. Npm komutu da mevcuttur. NMP, Node.js için paket yöneticisidir. Daha fazla bilgi edinebilirsinizhttps://www.npmjs.com/.

  • Bir terminal açın ve sudo npm install -g node-static çalıştırın . Bu, Node.js için statik web sunucusunu kuracaktır.

  • Şimdi HTML dosyalarını içeren herhangi bir dizine gidin ve web sunucunuzu başlatmak için dizinin içindeki statik komutu çalıştırın .

  • Sen gidebilirsin http://localhost:8080 dosyalarınızı görmek için.

Nodej'leri kurmanın başka bir yolu var. Terminal penceresinde sudo apt-get install nodejs komutunu çalıştırın .

Node.js kurulumunuzu test etmek için terminalinizi açın ve node komutunu çalıştırın . Nasıl çalıştığını kontrol etmek için birkaç komut yazın -

Node.js, terminalde yazılan komutların yanı sıra Javascript dosyalarını çalıştırır. Aşağıdaki içeriğe sahip bir index.js dosyası oluşturun -

console.log(“Testing Node.js”);

Ardından düğüm dizini komutunu çalıştırın . Aşağıdakileri göreceksiniz -

Sinyalleme sunucumuzu oluştururken Node.js için bir WebSockets kitaplığı kullanacağız. Çalıştırmak için npm'yi terminale yükleyin .

Sinyalleme sunucumuzu test etmek için wscat yardımcı programını kullanacağız. Kurmak için terminal pencerenizde npm install -g wscat komutunu çalıştırın .

S.No Protokoller ve Açıklama
1 WebRTC Protokolleri

WebRTC uygulamaları, aktarım protokolü olarak UDP'yi (Kullanıcı Datagram Protokolü) kullanır. Günümüzde çoğu web uygulaması TCP (İletim Kontrol Protokolü) kullanılarak oluşturulmuştur.

2 Oturum Açıklama Protokolü

SDP, WebRTC'nin önemli bir parçasıdır. Medya iletişim oturumlarını tanımlamayı amaçlayan bir protokoldür.

3 Rota Bulmak

Başka bir kullanıcıya bağlanmak için, kendi ağınız ve diğer kullanıcının ağı etrafında net bir yol bulmalısınız. Ancak, kullandığınız ağın güvenlik sorunlarını önlemek için çeşitli düzeylerde erişim kontrolüne sahip olma ihtimali vardır.

4 Akış Kontrolü İletim Protokolü

Eş bağlantı ile video ve ses verilerini hızlı bir şekilde gönderme yeteneğine sahibiz. SCTP protokolü, RTCDataChannel nesnesini kullanırken şu anda kurulu olan eş bağlantımızın üzerine blob verilerini göndermek için bugün kullanılmaktadır.

Özet

Bu bölümde, UDP, TCP, STUN, TURN, ICE ve SCTP gibi eş bağlantılarını etkinleştiren birkaç teknolojiyi ele aldık. Artık SDP'nin nasıl çalıştığı ve kullanım durumları hakkında yüzey düzeyinde bir anlayışa sahip olmalısınız.

MediaStream API, yerel kameralardan ve mikrofonlardan ortam akışlarına kolay erişim sağlamak için tasarlanmıştır. GetUserMedia () metodu, yerel giriş cihazları erişmek için birincil bir yöntemdir.

API'nin birkaç önemli noktası vardır -

  • Gerçek zamanlı bir medya akışı, video veya ses biçimindeki bir akış nesnesiyle temsil edilir

  • Kullanıcı izinleri aracılığıyla bir web uygulaması bir akışı getirmeye başlamadan önce kullanıcıya soran bir güvenlik seviyesi sağlar

  • Giriş cihazlarının seçimi MediaStream API tarafından yapılır (örneğin, cihaza bağlı iki kamera veya mikrofon olduğunda)

Her MediaStream nesnesi birkaç MediaStreamTrack nesnesi içerir. Farklı giriş cihazlarından gelen video ve sesi temsil ederler.

Her MediaStreamTrack nesnesi birkaç kanal (sağ ve sol ses kanalları) içerebilir. Bunlar, MediaStream API tarafından tanımlanan en küçük parçalardır.

MediaStream nesnelerinin çıktısını almanın iki yolu vardır. İlk olarak, çıktıyı bir video veya ses öğesine dönüştürebiliriz. İkinci olarak, çıktıyı RTCPeerConnection nesnesine gönderebiliriz, ardından onu uzaktaki bir eşe göndeririz.

MediaStream API'yi kullanma

Basit bir WebRTC uygulaması oluşturalım. Ekranda bir video öğesi gösterecek, kullanıcıdan kamerayı kullanma izni isteyecek ve tarayıcıda canlı bir video akışı gösterecektir. İndex.html dosyası oluşturun -

<!DOCTYPE html> 
<html lang = "en">
 
   <head> 
      <meta charset = "utf-8" /> 
   </head> 
	
   <body> 
      <video autoplay></video> 
      <script src = "client.js"></script> 
   </body>
	
</html>

Ardından client.js dosyasını oluşturun ve aşağıdakileri ekleyin;

function hasUserMedia() { 
   //check if the browser supports the WebRTC 
   return !!(navigator.getUserMedia || navigator.webkitGetUserMedia || 
      navigator.mozGetUserMedia); 
} 

if (hasUserMedia()) { 
   navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia
      || navigator.mozGetUserMedia; 
		
   //enabling video and audio channels 
   navigator.getUserMedia({ video: true, audio: true }, function (stream) { 
      var video = document.querySelector('video'); 
		
      //inserting our stream to the video tag     
      video.src = window.URL.createObjectURL(stream); 
   }, function (err) {}); 
} else { 
   alert("WebRTC is not supported"); 
}

Burada, WebRTC'nin desteklenip desteklenmediğini kontrol eden hasUserMedia () işlevini oluşturuyoruz. Ardından , ikinci parametrenin kullanıcının cihazından gelen akışı kabul eden bir geri arama olduğu getUserMedia işlevine erişiriz . Daha sonra , parametrede verilen nesneyi temsil eden bir URL oluşturan window.URL.createObjectURL kullanarak akışımızı video öğesine yükleriz .

Şimdi sayfanızı yenileyin, İzin Ver'e tıklayın ve ekranda yüzünüzü görmelisiniz.

Tüm betiklerinizi web sunucusunu kullanarak çalıştırmayı unutmayın. WebRTC Ortam Eğitiminde zaten bir tane kurduk.

MediaStream API

Özellikleri

  • MediaStream.active (read only) - MediaStream etkinse true, aksi takdirde false döndürür.

  • MediaStream.ended (read only, deprecated)- Nesne üzerinde sona eren olay tetiklendiyse true , yani akışın tamamen okunduğu anlamına gelir veya akışın sonuna ulaşılmadıysa false döndürür.

  • MediaStream.id (read only) - Nesne için benzersiz bir tanımlayıcı.

  • MediaStream.label (read only, deprecated) - Kullanıcı aracısı tarafından atanan benzersiz bir tanımlayıcı.

Yukarıdaki özelliklerin tarayıcımda nasıl göründüğünü görebilirsiniz -

Etkinlik sahipleri

  • MediaStream.onactive- Bir MediaStream nesnesi etkinleştirildiğinde tetiklenen bir etkin olay için bir işleyici .

  • MediaStream.onaddtrack- Yeni bir MediaStreamTrack nesnesi eklendiğinde tetiklenen bir addtrack olayı için bir işleyici .

  • MediaStream.onended (deprecated)- Akış sona erdiğinde tetiklenen, sona eren bir olay için bir işleyici .

  • MediaStream.oninactive- Bir MediaStream nesnesi devre dışı kaldığında tetiklenen, etkin olmayan bir olay için işleyici .

  • MediaStream.onremovetrack- MediaStreamTrack nesnesi ondan kaldırıldığında tetiklenen bir removeetrack olayı için bir işleyici .

Yöntemler

  • MediaStream.addTrack()- MediaStream'e bağımsız değişken olarak verilen MediaStreamTrack nesnesini ekler . Parça zaten eklenmişse hiçbir şey olmaz.

  • MediaStream.clone() - Yeni bir ID ile MediaStream nesnesinin bir klonunu döndürür.

  • MediaStream.getAudioTracks()- MediaStream nesnesinden ses MediaStreamTrack nesnelerinin bir listesini döndürür .

  • MediaStream.getTrackById()- Parçayı kimliğine göre döndürür. Bağımsız değişken boşsa veya kimlik bulunamazsa, boş döndürür. Birkaç parça aynı kimliğe sahipse, ilkini döndürür.

  • MediaStream.getTracks()- MediaStream nesnesindeki tüm MediaStreamTrack nesnelerinin bir listesini döndürür .

  • MediaStream.getVideoTracks()- MediaStream nesnesinden video MediaStreamTrack nesnelerinin bir listesini döndürür .

  • MediaStream.removeTrack()- MediaStream'den bağımsız değişken olarak verilen MediaStreamTrack nesnesini kaldırır . Parça zaten kaldırılmışsa hiçbir şey olmaz.

Yukarıdaki API'leri test etmek için, index.html'yi aşağıdaki şekilde değiştirin -

<!DOCTYPE html> 
<html lang = "en">
 
   <head> 
      <meta charset = "utf-8" /> 
   </head>
	
   <body> 
      <video autoplay></video> 
      <div><button id = "btnGetAudioTracks">getAudioTracks()
         </button></div> 
      <div><button id = "btnGetTrackById">getTrackById()
         </button></div> 
      <div><button id = "btnGetTracks">getTracks()</button></div> 
      <div><button id = "btnGetVideoTracks">getVideoTracks()
         </button></div> 
      <div><button id = "btnRemoveAudioTrack">removeTrack() - audio
         </button></div> 
      <div><button id = "btnRemoveVideoTrack">removeTrack() - video
         </button></div> 
      <script src = "client.js"></script> 
   </body> 
	
</html>

Birkaç MediaStream API'sini denemek için birkaç düğme ekledik. Ardından yeni oluşturduğumuz buton için olay işleyicileri eklemeliyiz. Değiştir client.js bu şekilde dosya -

var stream;
  
function hasUserMedia() { 
   //check if the browser supports the WebRTC 
   return !!(navigator.getUserMedia || navigator.webkitGetUserMedia || 
      navigator.mozGetUserMedia); 
} 
 
if (hasUserMedia()) {
   navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia
      || navigator.mozGetUserMedia; 
		
   //enabling video and audio channels 
   navigator.getUserMedia({ video: true, audio: true }, function (s) { 
      stream = s; 
      var video = document.querySelector('video'); 
		
      //inserting our stream to the video tag     
      video.src = window.URL.createObjectURL(stream); 
   }, function (err) {}); 
} else { 
   alert("WebRTC is not supported"); 
}
  
btnGetAudioTracks.addEventListener("click", function(){ 
   console.log("getAudioTracks"); 
   console.log(stream.getAudioTracks()); 
});
  
btnGetTrackById.addEventListener("click", function(){ 
   console.log("getTrackById"); 
   console.log(stream.getTrackById(stream.getAudioTracks()[0].id)); 
});
  
btnGetTracks.addEventListener("click", function(){ 
   console.log("getTracks()"); 
   console.log(stream.getTracks()); 
});
 
btnGetVideoTracks.addEventListener("click", function(){ 
   console.log("getVideoTracks()"); 
   console.log(stream.getVideoTracks()); 
});

btnRemoveAudioTrack.addEventListener("click", function(){ 
   console.log("removeAudioTrack()"); 
   stream.removeTrack(stream.getAudioTracks()[0]); 
});
  
btnRemoveVideoTrack.addEventListener("click", function(){ 
   console.log("removeVideoTrack()"); 
   stream.removeTrack(stream.getVideoTracks()[0]); 
});

Şimdi sayfanızı yenileyin. Tıklayın getAudioTracks () düğmesine, ardından tıklayarak ses - removeTrack () düğmesi. Ses parçası şimdi kaldırılmalıdır. Sonra aynısını video parçası için yapın.

Tıklarsanız getTracks () düğmesi tüm görmelisiniz MediaStreamTracks (bağlı tüm video ve ses girişlerini). Ardından ses MediaStreamTrack'i almak için getTrackById () öğesine tıklayın .

Özet

Bu bölümde, MediaStream API kullanarak basit bir WebRTC uygulaması oluşturduk. Artık WebRTC'nin çalışmasını sağlayan çeşitli MediaStream API'leri hakkında net bir genel bakışa sahip olmalısınız.

RTCPeerConnection API, tarayıcıların her biri arasındaki eşler arası bağlantının çekirdeğidir. RTCPeerConnection nesnelerini oluşturmak için şunu yazın:

var pc = RTCPeerConnection(config);

nerede yapılandırma argümanı en azından anahtarı iceServers olarak barındırmaktadır. ICE adaylarının bulunması sırasında kullanılan, STUN ve TURN sunucuları hakkında bilgi içeren bir URL nesneleri dizisidir. Kullanılabilir genel STUN sunucularının bir listesini code.google.com adresinde bulabilirsiniz.

Arayan veya aranan uç olmanıza bağlı olarak, RTCPeerConnection nesnesi bağlantının her iki tarafında biraz farklı bir şekilde kullanılır.

İşte kullanıcının akışına bir örnek -

  • Kayıt onicecandidate işleyicisi. ICE adaylarını alındıkları gibi diğer akrana gönderir.

  • Kayıt onaddstream işleyicisi. Uzaktaki eşten alındığında video akışının görüntülenmesini yönetir.

  • Kayıt mesajı işleyicisi. Sinyal verme sunucunuz ayrıca diğer eşten alınan mesajlar için bir işleyiciye sahip olmalıdır. Mesaj RTCSessionDescription nesnesini içeriyorsa , setRemoteDescription () yöntemi kullanılarak RTCPeerConnection nesnesine eklenmelidir . Mesaj RTCIceCandidate nesnesini içeriyorsa , addIceCandidate () yöntemi kullanılarak RTCPeerConnection nesnesine eklenmelidir .

  • Yararlanın getUserMedia () yerel medya akışını ayarlamak ve eklemek için RTCPeerConnection kullanarak nesne addStream () yöntemini.

  • Teklif / cevap görüşme sürecini başlatın. Bu, arayan ucun akışının aranan uçtan farklı olduğu tek adımdır. Arayan, createOffer () yöntemini kullanarak görüşmeyi başlatır ve RTCSessionDescription nesnesini alan bir geri aramayı kaydeder . Daha sonra bu geri çağırma, setLocalDescription () kullanarak bu RTCSessionDescription nesnesini RTCPeerConnection nesnenize eklemelidir . Ve son olarak, arayan kişi bu RTCSessionDescription'ı sinyal verme sunucusunu kullanarak uzaktaki eşe göndermelidir . Öte yandan, aranan aynı geri aramayı kaydeder, ancak createAnswer () yönteminde. Aranan uç akışının yalnızca arayan kişiden teklif alındıktan sonra başlatıldığına dikkat edin.

RTCPeerConnection API

Özellikleri

  • RTCPeerConnection.iceConnectionState (read only)- Bağlantının durumunu açıklayan bir RTCIceConnectionState numaralandırması döndürür. Bu değer değiştiğinde bir iceconnectionstatechange olayı tetiklenir. Olası değerler -

    • new - ICE temsilcisi uzak adayları bekliyor veya adres topluyor

    • checking - ICE temsilcisinin uzak adayları var, ancak henüz bir bağlantı bulamadı

    • connected - ICE temsilcisi kullanılabilir bir bağlantı buldu, ancak daha iyi bağlantı için hala daha uzak adayı kontrol ediyor.

    • completed - ICE temsilcisi kullanılabilir bir bağlantı buldu ve uzak adayları test etmeyi durdurdu.

    • failed - ICE temsilcisi tüm uzak adayları kontrol etti ancak en az bir bileşen için bir eşleşme bulamadı.

    • disconnected - en az bir bileşen artık hayatta değil.

    • closed - ICE ajanı kapalıdır.

  • RTCPeerConnection.iceGatheringState (read only) - Bağlantı için ICE toplama durumunu tanımlayan bir RTCIceGatheringState numaralandırması döndürür -

    • new - nesne yeni oluşturuldu.

    • gathering - ICE acentesi adayları toplama sürecinde

    • complete ICE ajanı toplamayı tamamladı.

  • RTCPeerConnection.localDescription (read only)- Yerel oturumu açıklayan bir RTCSessionDescription döndürür. Henüz ayarlanmadıysa boş olabilir.

  • RTCPeerConnection.peerIdentity (read only)- Bir RTCIdentityAssertion döndürür. Bir idp (etki alanı adı) ve uzak eşin kimliğini temsil eden bir addan oluşur.

  • RTCPeerConnection.remoteDescription (read only)- Uzak oturumu açıklayan bir RTCSessionDescription döndürür. Henüz ayarlanmadıysa boş olabilir.

  • RTCPeerConnection.signalingState (read only)- Yerel bağlantının sinyal durumunu açıklayan bir RTCSignalingState numaralandırması döndürür. Bu durum, SDP teklifini açıklar. Bu değer değiştiğinde bir sinyalleşme durumu değişim olayı tetiklenir. Olası değerler -

    • stable- Başlangıç ​​durumu. Devam eden SDP teklif / cevap alışverişi yok.

    • have-local-offer - bağlantının yerel tarafı yerel olarak bir SDP teklifi uyguladı.

    • have-remote-offer - bağlantının uzak tarafı yerel olarak bir SDP teklifi uyguladı.

    • have-local-pranswer - uzak bir SDP teklifi uygulandı ve yerel olarak bir SDP pranswer uygulandı.

    • have-remote-pranswer - yerel bir SDP uygulandı ve bir SDP pranswer uzaktan uygulandı.

    • closed - bağlantı kapalıdır.

Etkinlik sahipleri

S.No. Olay İşleyicileri ve Açıklama
1

RTCPeerConnection.onaddstream

Bu işleyici, addstream olayı tetiklendiğinde çağrılır. Bu olay, uzak eş tarafından bu bağlantıya bir MediaStream eklendiğinde gönderilir.

2

RTCPeerConnection.ondatachannel

Bu işleyici, datachannel olayı tetiklendiğinde çağrılır. Bu olay, bu bağlantıya bir RTCDataChannel eklendiğinde gönderilir.

3

RTCPeerConnection.onicecandidate

Bu işleyici, icecandidate olayı tetiklendiğinde çağrılır. Bu olay, komut dosyasına bir RTCIceCandidate nesnesi eklendiğinde gönderilir.

4

RTCPeerConnection.oniceconnectionstatechange

Bu işleyici, iceconnectionstatechange olayı tetiklendiğinde çağrılır. Bu olay, iceConnectionState değeri değiştiğinde gönderilir.

5

RTCPeerConnection.onidentityresult

Bu işleyici, Identityresult olayı tetiklendiğinde çağrılır. Bu olay, bir teklif veya getIdentityAssertion () yoluyla bir yanıtın oluşturulması sırasında bir kimlik iddiası üretildiğinde gönderilir.

6

RTCPeerConnection.onidpassertionerror

Bu işleyici, idpassertionerror olayı tetiklendiğinde çağrılır. Bu olay, IdP (Kimlik Sağlayıcı) bir kimlik doğrulaması oluştururken bir hata bulduğunda gönderilir.

7

RTCPeerConnection.onidpvalidation

Bu işleyici, idpvalidationerror olayı tetiklendiğinde çağrılır. Bu olay, IdP (Kimlik Sağlayıcı) bir kimlik onayını doğrularken bir hata bulduğunda gönderilir.

8

RTCPeerConnection.onnegotiationneeded

Bu işleyici, pazarlık gerektiren olay tetiklendiğinde çağrılır. Bu olay, gelecekte bir noktada görüşmenin gerekli olacağını bildirmek için tarayıcı tarafından gönderilir.

9

RTCPeerConnection.onpeeridentity

Bu işleyici, peeridentity olayı tetiklendiğinde çağrılır. Bu olay, bu bağlantıda bir eş kimliği ayarlandığında ve doğrulandığında gönderilir.

10

RTCPeerConnection.onremovestream

Bu işleyici, signalingstatechange olayı tetiklendiğinde çağrılır. Bu olay, signalingState değeri değiştiğinde gönderilir.

11

RTCPeerConnection.onsignalingstatechange

Bu işleyici, removeestream olayı tetiklendiğinde çağrılır. Bu olay, bu bağlantıdan bir MediaStream kaldırıldığında gönderilir.

Yöntemler

S.No. Yöntemler ve Açıklama
1

RTCPeerConnection()

Yeni bir RTCPeerConnection nesnesi döndürür.

2

RTCPeerConnection.createOffer()

Uzak bir eş bulmak için bir teklif (istek) oluşturur. Bu yöntemin ilk iki parametresi, başarı ve hata geri aramalarıdır. İsteğe bağlı üçüncü parametre, ses veya video akışlarını etkinleştirmek gibi seçeneklerdir.

3

RTCPeerConnection.createAnswer()

Teklif / cevap pazarlığı sürecinde uzak tarafın aldığı teklife bir cevap oluşturur. Bu yöntemin ilk iki parametresi, başarı ve hata geri aramalarıdır. İsteğe bağlı üçüncü parametre, cevabın oluşturulmasına yönelik seçeneklerdir.

4

RTCPeerConnection.setLocalDescription()

Yerel bağlantı açıklamasını değiştirir. Açıklama, bağlantının özelliklerini tanımlar. Bağlantı hem eski hem de yeni açıklamaları destekleyebilmelidir. Yöntem üç parametre alır: RTCSessionDescription nesnesi, açıklama değişikliği başarılı olursa geri arama, açıklama değişikliği başarısız olursa geri arama.

5

RTCPeerConnection.setRemoteDescription()

Uzak bağlantı açıklamasını değiştirir. Açıklama, bağlantının özelliklerini tanımlar. Bağlantı hem eski hem de yeni açıklamaları destekleyebilmelidir. Yöntem üç parametre alır: RTCSessionDescription nesnesi, açıklama değişikliği başarılı olursa geri arama, açıklama değişikliği başarısız olursa geri arama.

6

RTCPeerConnection.updateIce()

Uzak adaylara ping atma ve yerel adayları toplama ICE aracısı sürecini günceller.

7

RTCPeerConnection.addIceCandidate()

ICE temsilcisine uzak bir aday sağlar.

8

RTCPeerConnection.getConfiguration()

Bir RTCConfiguration nesnesi döndürür. RTCPeerConnection nesnesinin yapılandırmasını temsil eder.

9

RTCPeerConnection.getLocalStreams()

Bir dizi yerel MediaStream bağlantısı döndürür.

10

RTCPeerConnection.getRemoteStreams()

Bir dizi uzak MediaStream bağlantısı döndürür.

11

RTCPeerConnection.getStreamById()

Verilen kimliğe göre yerel veya uzak MediaStream'i döndürür.

12

RTCPeerConnection.addStream()

Yerel bir video veya ses kaynağı olarak bir MediaStream ekler.

13

RTCPeerConnection.removeStream()

Bir MediaStream'i yerel video veya ses kaynağı olarak kaldırır.

14

RTCPeerConnection.close()

Bir bağlantıyı kapatır.

15

RTCPeerConnection.createDataChannel()

Yeni bir RTCDataChannel oluşturur.

16

RTCPeerConnection.createDTMFSender()

Belirli bir MediaStreamTrack ile ilişkilendirilmiş yeni bir RTCDTMFSender oluşturur. Bağlantı üzerinden DTMF (Çift tonlu çok frekanslı) telefon sinyali göndermeye izin verir.

17

RTCPeerConnection.getStats()

Bağlantıyla ilgili istatistikleri içeren yeni bir RTCStatsReport oluşturur.

18

RTCPeerConnection.setIdentityProvider()

IdP'yi ayarlar. Üç parametre alır - ad, iletişim için kullanılan protokol ve isteğe bağlı bir kullanıcı adı.

19

RTCPeerConnection.getIdentityAssertion()

Bir kimlik iddiası toplar. Uygulamada bu yöntemin ele alınması beklenmemektedir. Bu nedenle, açıkça sadece ihtiyacı önceden tahmin etmek için çağırabilirsiniz.

Bir Bağlantı Kurmak

Şimdi bir örnek uygulama oluşturalım. İlk olarak, "sinyalleşme sunucusu" eğitiminde oluşturduğumuz sinyalleme sunucusunu "düğüm sunucusu" üzerinden çalıştırın.

Sayfada biri giriş, diğeri bağlanmak istediğimiz kullanıcı adı olmak üzere iki metin girişi olacaktır. Bir index.html dosyası oluşturun ve aşağıdaki kodu ekleyin -

<html lang = "en"> 
   <head> 
      <meta charset = "utf-8" /> 
   </head>
	
   <body> 
	
      <div> 
         <input type = "text" id = "loginInput" /> 
         <button id = "loginBtn">Login</button> 
      </div> 
	
      <div> 
         <input type = "text" id = "otherUsernameInput" />
         <button id = "connectToOtherUsernameBtn">Establish connection</button> 
      </div> 
		
      <script src = "client2.js"></script>
		
   </body>
	
</html>

Bir giriş için metin girişini, giriş düğmesini, diğer eş kullanıcı adı için metin girişini ve ona bağlan düğmesini eklediğimizi görebilirsiniz. Şimdi bir client.js dosyası oluşturun ve aşağıdaki kodu ekleyin -

var connection = new WebSocket('ws://localhost:9090'); 
var name = ""; 
 
var loginInput = document.querySelector('#loginInput'); 
var loginBtn = document.querySelector('#loginBtn'); 
var otherUsernameInput = document.querySelector('#otherUsernameInput'); 
var connectToOtherUsernameBtn = document.querySelector('#connectToOtherUsernameBtn'); 
var connectedUser, myConnection;
  
//when a user clicks the login button 
loginBtn.addEventListener("click", function(event){ 
   name = loginInput.value; 
	
   if(name.length > 0){ 
      send({ 
         type: "login", 
         name: name 
      }); 
   } 
	
});
  
//handle messages from the server 
connection.onmessage = function (message) { 
   console.log("Got message", message.data);
   var data = JSON.parse(message.data); 
	
   switch(data.type) { 
      case "login": 
         onLogin(data.success); 
         break; 
      case "offer": 
         onOffer(data.offer, data.name); 
         break; 
      case "answer": 
         onAnswer(data.answer); 
         break; 
      case "candidate": 
         onCandidate(data.candidate); 
         break; 
      default: 
         break; 
   } 
};
  
//when a user logs in 
function onLogin(success) { 

   if (success === false) { 
      alert("oops...try a different username"); 
   } else { 
      //creating our RTCPeerConnection object 
		
      var configuration = { 
         "iceServers": [{ "url": "stun:stun.1.google.com:19302" }] 
      }; 
		
      myConnection = new webkitRTCPeerConnection(configuration); 
      console.log("RTCPeerConnection object was created"); 
      console.log(myConnection); 
  
      //setup ice handling
      //when the browser finds an ice candidate we send it to another peer 
      myConnection.onicecandidate = function (event) { 
		
         if (event.candidate) { 
            send({ 
               type: "candidate", 
               candidate: event.candidate 
            }); 
         } 
      }; 
   } 
};
  
connection.onopen = function () { 
   console.log("Connected"); 
};
  
connection.onerror = function (err) { 
   console.log("Got error", err); 
};
  
// Alias for sending messages in JSON format 
function send(message) { 

   if (connectedUser) { 
      message.name = connectedUser; 
   } 
	
   connection.send(JSON.stringify(message)); 
};

Sinyal sunucumuza bir soket bağlantısı kurduğumuzu görebilirsiniz. Bir kullanıcı oturum açma düğmesine tıkladığında, uygulama kullanıcı adını sunucuya gönderir. Oturum açma başarılı olursa, uygulama RTCPeerConnection nesnesini oluşturur ve bulunan tüm icecandidates'i diğer eşe gönderen onicecandidate işleyicisini ayarlar. Şimdi sayfayı açın ve giriş yapmayı deneyin. Aşağıdaki konsol çıktısını görmelisiniz -

Bir sonraki adım, diğer akran için bir teklif oluşturmaktır. Aşağıdaki kodu client.js dosyanıza ekleyin -

//setup a peer connection with another user 
connectToOtherUsernameBtn.addEventListener("click", function () { 
 
   var otherUsername = otherUsernameInput.value; 
   connectedUser = otherUsername;
	
   if (otherUsername.length > 0) { 
      //make an offer 
      myConnection.createOffer(function (offer) { 
         console.log(); 
         send({ 
            type: "offer", 
            offer: offer 
         });
			
         myConnection.setLocalDescription(offer); 
      }, function (error) { 
         alert("An error has occurred."); 
      }); 
   } 
}); 
 
//when somebody wants to call us 
function onOffer(offer, name) { 
   connectedUser = name; 
   myConnection.setRemoteDescription(new RTCSessionDescription(offer)); 
	
   myConnection.createAnswer(function (answer) { 
      myConnection.setLocalDescription(answer); 
      send({ 
         type: "answer", 
         answer: answer 
      }); 
		
   }, function (error) { 
      alert("oops...error"); 
   }); 
}
  
//when another user answers to our offer 
function onAnswer(answer) { 
   myConnection.setRemoteDescription(new RTCSessionDescription(answer)); 
} 
 
//when we got ice candidate from another user 
function onCandidate(candidate) { 
   myConnection.addIceCandidate(new RTCIceCandidate(candidate)); 
}

Bir kullanıcı "Bağlantı kur" düğmesini tıkladığında uygulamanın diğer eşe bir SDP teklifinde bulunduğunu görebilirsiniz. Ayrıca onAnswer ve onCandidate işleyicilerini de ayarladık . Sayfanızı yeniden yükleyin, iki sekmede açın, iki kullanıcıyla oturum açın ve aralarında bağlantı kurmaya çalışın. Aşağıdaki konsol çıktısını görmelisiniz -

Şimdi eşler arası bağlantı kuruldu. Sonraki eğitimlerde, metin sohbet desteğinin yanı sıra video ve ses akışları da ekleyeceğiz.

WebRTC yalnızca ses ve video akışlarını aktarmada değil, sahip olabileceğimiz herhangi bir keyfi veride de iyidir. RTCDataChannel nesnesi burada devreye girer.

RTCDataChannel API

Özellikleri

  • RTCDataChannel.label (read only) - Veri kanalı adını içeren bir dize döndürür.

  • RTCDataChannel.ordered (read only) - İletilerin teslim sırası garantiliyse doğru, garanti edilmezse yanlış döndürür.

  • RTCDataChannel.protocol (read only) - Bu kanal için kullanılan alt protokol adını içeren bir dize döndürür.

  • RTCDataChannel.id (read only) - RTCDataChannel nesnesinin oluşturulması sırasında ayarlanan kanal için benzersiz bir kimlik döndürür.

  • RTCDataChannel.readyState (read only)- Bağlantının durumunu temsil eden RTCDataChannelState numaralandırmasını döndürür. Olası değerler -

    • connecting- Bağlantının henüz aktif olmadığını gösterir. Bu başlangıç ​​durumudur.

    • open - Bağlantının çalıştığını gösterir.

    • closing- Bağlantının kapanma sürecinde olduğunu gösterir. Önbelleğe alınan mesajlar gönderilme veya alınma sürecindedir, ancak yeni oluşturulan hiçbir görev kabul edilmiyor.

    • closed - Bağlantının kurulamadığını veya kapatıldığını gösterir.

  • RTCDataChannel.bufferedAmount (read only)- Gönderilmek üzere sıraya alınmış bayt miktarını döndürür. Bu, RTCDataChannel.send () aracılığıyla henüz gönderilmemiş veri miktarıdır.

  • RTCDataChannel.bufferedAmountLowThreshold- RTCDataChannel.bufferedAmount değerinin düşük tutulduğu bayt sayısını döndürür. RTCDataChannel.bufferedAmount bu eşiğin altına düştüğünde, bufferedamountlow olayı tetiklenir.

  • RTCDataChannel.binaryType- Bağlantı tarafından iletilen ikili verinin türünü döndürür. "Blob" veya "arraybuffer" olabilir.

  • RTCDataChannel.maxPacketLifeType (read only) - Mesajlaşma güvenilmez moda girdiğinde, pencerenin milisaniye cinsinden uzunluğunu gösteren işaretsiz bir kısa döndürür.

  • RTCDataChannel.maxRetransmits (read only) - Bir kanalın teslim edilmediği takdirde veriyi yeniden ileteceği maksimum sayıyı gösteren işaretsiz bir kısa mesaj döndürür.

  • RTCDataChannel.negotiated (read only) - Kanalın kullanıcı aracısı veya uygulama tarafından müzakere edilip edilmediğini gösteren bir boole döndürür.

  • RTCDataChannel.reliable (read only) - Bağlantının güvenilmez modda mesajlar gönderebileceğini gösteren bir boole döndürür.

  • RTCDataChannel.stream (read only) - RTCDataChannel.id ile eşanlamlı

Etkinlik sahipleri

  • RTCDataChannel.onopen- Bu olay işleyicisi, açık olay tetiklendiğinde çağrılır. Bu olay, veri bağlantısı kurulduğunda gönderilir.

  • RTCDataChannel.onmessage- Bu olay işleyicisi, mesaj olayı tetiklendiğinde çağrılır. Olay, veri kanalında bir mesaj olduğunda gönderilir.

  • RTCDataChannel.onbufferedamountlow- Bu olay işleyici, bufferedamoutlow olayı tetiklendiğinde çağrılır. Bu olay, RTCDataChannel.bufferedAmount, RTCDataChannel.bufferedAmountLowThreshold özelliğinin altına düştüğünde gönderilir.

  • RTCDataChannel.onclose- Bu olay işleyici, close olayı tetiklendiğinde çağrılır. Bu olay, veri bağlantısı kapatıldığında gönderilir.

  • RTCDataChannel.onerror- Bu olay işleyici, hata olayı tetiklendiğinde çağrılır. Bu olay, bir hatayla karşılaşıldığında gönderilir.

Yöntemler

  • RTCDataChannel.close() - Veri kanalını kapatır.

  • RTCDataChannel.send()- Parametredeki verileri kanal üzerinden gönderir. Veriler bir blob, bir dize, bir ArrayBuffer veya bir ArrayBufferView olabilir.

Şimdi basit bir örnek oluşturalım. İlk olarak, "sinyalleşme sunucusu" eğitiminde oluşturduğumuz sinyalleme sunucusunu "düğüm sunucusu" üzerinden çalıştırın.

Sayfada biri giriş, biri kullanıcı adı ve diğeri diğer akrana göndermek istediğimiz mesaj için olmak üzere üç metin girişi olacaktır. Bir index.html dosyası oluşturun ve aşağıdaki kodu ekleyin -

<html lang = "en"> 
   <head> 
      <meta charset = "utf-8" /> 
   </head>
	
   <body> 
      <div> 
         <input type = "text" id = "loginInput" /> 
         <button id = "loginBtn">Login</button> 
      </div> 
		
      <div> 
         <input type = "text" id = "otherUsernameInput" /> 
         <button id = "connectToOtherUsernameBtn">Establish connection</button> 
      </div> 
		
      <div> 
         <input type = "text" id = "msgInput" /> 
         <button id = "sendMsgBtn">Send text message</button> 
      </div> 
		
      <script src = "client.js"></script>
   </body>
	
</html>

Ayrıca oturum açmak, bağlantı kurmak ve mesaj göndermek için üç düğme ekledik. Şimdi bir client.js dosyası oluşturun ve aşağıdaki kodu ekleyin -

var connection = new WebSocket('ws://localhost:9090'); 
var name = "";

var loginInput = document.querySelector('#loginInput'); 
var loginBtn = document.querySelector('#loginBtn'); 

var otherUsernameInput = document.querySelector('#otherUsernameInput'); 
var connectToOtherUsernameBtn = document.querySelector('#connectToOtherUsernameBtn'); 
var msgInput = document.querySelector('#msgInput'); 
var sendMsgBtn = document.querySelector('#sendMsgBtn'); 
var connectedUser, myConnection, dataChannel;
  
//when a user clicks the login button 
loginBtn.addEventListener("click", function(event) { 
   name = loginInput.value; 
	
   if(name.length > 0) { 
      send({ 
         type: "login", 
         name: name 
      }); 
   } 
}); 
 
//handle messages from the server 
connection.onmessage = function (message) { 
   console.log("Got message", message.data); 
   var data = JSON.parse(message.data); 
	
   switch(data.type) { 
      case "login": 
         onLogin(data.success); 
         break; 
      case "offer": 
         onOffer(data.offer, data.name); 
         break; 
      case "answer":
         onAnswer(data.answer); 
         break; 
      case "candidate": 
         onCandidate(data.candidate); 
         break; 
      default: 
         break; 
   } 
}; 
 
//when a user logs in 
function onLogin(success) { 

   if (success === false) { 
      alert("oops...try a different username"); 
   } else { 
      //creating our RTCPeerConnection object 
      var configuration = { 
         "iceServers": [{ "url": "stun:stun.1.google.com:19302" }] 
      }; 
		
      myConnection = new webkitRTCPeerConnection(configuration, { 
         optional: [{RtpDataChannels: true}] 
      }); 
		
      console.log("RTCPeerConnection object was created"); 
      console.log(myConnection); 
  
      //setup ice handling 
      //when the browser finds an ice candidate we send it to another peer 
      myConnection.onicecandidate = function (event) { 
		
         if (event.candidate) { 
            send({ 
               type: "candidate", 
               candidate: event.candidate 
            });
         } 
      }; 
		
      openDataChannel();
		
   } 
};
  
connection.onopen = function () { 
   console.log("Connected"); 
}; 
 
connection.onerror = function (err) { 
   console.log("Got error", err); 
};
  
// Alias for sending messages in JSON format 
function send(message) { 
   if (connectedUser) { 
      message.name = connectedUser; 
   }
	
   connection.send(JSON.stringify(message)); 
};

Sinyal sunucumuza bir soket bağlantısı kurduğumuzu görebilirsiniz. Bir kullanıcı oturum açma düğmesine tıkladığında, uygulama kullanıcı adını sunucuya gönderir. Oturum açma başarılı olursa, uygulama RTCPeerConnection nesnesini oluşturur ve bulunan tüm icecandidates'i diğer eşe gönderen onicecandidate işleyicisini kurar. Ayrıca bir dataChannel oluşturan openDataChannel () işlevini de çalıştırır. RTCPeerConnection nesnesini oluştururken, yapıcıdaki ikinci bağımsız değişkenin isteğe bağlı olduğuna dikkat edin: [{RtpDataChannels: true}], Chrome veya Opera kullanıyorsanız zorunludur. Bir sonraki adım, diğer akran için bir teklif oluşturmaktır. Sizin için aşağıdaki kodu ekleyin client.js file-

//setup a peer connection with another user 
connectToOtherUsernameBtn.addEventListener("click", function () {
  
   var otherUsername = otherUsernameInput.value;
   connectedUser = otherUsername;
	
   if (otherUsername.length > 0) { 
      //make an offer 
      myConnection.createOffer(function (offer) { 
         console.log(); 
         send({ 
            type: "offer", 
            offer: offer 
         }); 
			
         myConnection.setLocalDescription(offer); 
      }, function (error) { 
         alert("An error has occurred."); 
      }); 
   } 
});
  
//when somebody wants to call us 
function onOffer(offer, name) { 
   connectedUser = name; 
   myConnection.setRemoteDescription(new RTCSessionDescription(offer));
	
   myConnection.createAnswer(function (answer) { 
      myConnection.setLocalDescription(answer); 
		
      send({ 
         type: "answer", 
         answer: answer 
      }); 
		
   }, function (error) { 
      alert("oops...error"); 
   }); 
}

//when another user answers to our offer 
function onAnswer(answer) { 
   myConnection.setRemoteDescription(new RTCSessionDescription(answer)); 
}
  
//when we got ice candidate from another user 
function onCandidate(candidate) { 
   myConnection.addIceCandidate(new RTCIceCandidate(candidate)); 
}

Bir kullanıcı "Bağlantı kur" düğmesini tıkladığında uygulamanın diğer eşe bir SDP teklifinde bulunduğunu görebilirsiniz. Ayrıca onAnswer ve onCandidate işleyicilerini de ayarladık . Son olarak dataChannel'ımızı oluşturan openDataChannel () işlevini uygulayalım. Aşağıdaki kodu client.js dosyanıza ekleyin -

//creating data channel 
function openDataChannel() { 

   var dataChannelOptions = { 
      reliable:true 
   }; 
	
   dataChannel = myConnection.createDataChannel("myDataChannel", dataChannelOptions);
	
   dataChannel.onerror = function (error) { 
      console.log("Error:", error); 
   };
	
   dataChannel.onmessage = function (event) { 
      console.log("Got message:", event.data); 
   };  
}
  
//when a user clicks the send message button 
sendMsgBtn.addEventListener("click", function (event) { 
   console.log("send message");
   var val = msgInput.value; 
   dataChannel.send(val); 
});

Burada bağlantımız için dataChannel'ı oluşturuyoruz ve "mesaj gönder" düğmesi için olay işleyicisini ekliyoruz. Şimdi bu sayfayı iki sekmede açın, iki kullanıcıyla oturum açın, bağlantı kurun ve mesaj göndermeyi deneyin. Bunları konsol çıktısında görmelisiniz. Yukarıdaki örneğin Opera'da test edildiğine dikkat edin.

Artık RTCDataChannel'in WebRTC API'nin son derece güçlü bir parçası olduğunu görebilirsiniz. Bu nesne için eşler arası oyun veya torrent tabanlı dosya paylaşımı gibi birçok başka kullanım durumu vardır.

Çoğu WebRTC uygulaması yalnızca video ve ses yoluyla iletişim kuramıyor. Diğer birçok özelliğe ihtiyaçları var. Bu bölümde, temel bir sinyalleme sunucusu oluşturacağız.

Sinyalleşme ve Müzakere

Başka bir kullanıcıya bağlanmak için, Web'de nerede olduğunu bilmelisiniz. Cihazınızın IP adresi, İnternet özellikli cihazların birbirleri arasında doğrudan veri göndermesine izin verir. RTCPeerConnection nesne bu sorumludur. Cihazlar İnternet üzerinden birbirlerini nasıl bulacaklarını öğrenir öğrenmez, her bir cihazın hangi protokolleri ve kodekleri desteklediğiyle ilgili veri alışverişine başlarlar.

Başka bir kullanıcıyla iletişim kurmak için iletişim bilgilerini paylaşmanız yeterlidir ve gerisi WebRTC tarafından yapılacaktır. Diğer kullanıcıya bağlanma süreci, sinyal verme ve görüşme olarak da bilinir. Birkaç adımdan oluşur -

  • Eş bağlantı için potansiyel adayların bir listesini oluşturun.

  • Kullanıcı veya uygulama, bağlantı kurmak için bir kullanıcıyı seçer.

  • Sinyalleme katmanı, başka bir kullanıcıya birisinin kendisine bağlanmak istediğini bildirir. Kabul edebilir veya reddedebilir.

  • Teklifin kabulü ilk kullanıcıya bildirilir.

  • İlk kullanıcı , başka bir kullanıcıyla RTCPeerConnection'ı başlatır .

  • Her iki kullanıcı da sinyalleme sunucusu aracılığıyla yazılım ve donanım bilgisi alışverişinde bulunur.

  • Her iki kullanıcı da konum bilgisi alışverişinde bulunur.

  • Bağlantı başarılı veya başarısız.

WebRTC spesifikasyonu, bilgi alışverişi ile ilgili herhangi bir standart içermez. Bu nedenle, yukarıdakilerin sadece sinyal vermenin nasıl olabileceğinin bir örneği olduğunu unutmayın. İstediğiniz herhangi bir protokol veya teknolojiyi kullanabilirsiniz.

Sunucuyu Oluşturmak

Oluşturacağımız sunucu aynı bilgisayarda bulunmayan iki kullanıcıyı birbirine bağlayabilecektir. Kendi sinyalleşme mekanizmamızı yaratacağız. Sinyal sunucumuz bir kullanıcının diğerini aramasına izin verecektir. Bir kullanıcı diğerini aradığında, sunucu teklifi, yanıtı, ICE adaylarını aralarında geçirir ve bir WebRTC bağlantısı kurar.

Yukarıdaki şema, sinyal verme sunucusunu kullanırken kullanıcılar arasındaki mesajlaşma akışıdır. Her şeyden önce, her kullanıcı sunucuya kaydolur. Bizim durumumuzda, bu basit bir dize kullanıcı adı olacaktır. Kullanıcılar kayıt olduktan sonra birbirlerini arayabilirler. Kullanıcı 1, aramak istediği kullanıcı tanımlayıcı ile bir teklifte bulunur. Diğer kullanıcı cevap vermelidir. Son olarak, ICE adayları bir bağlantı kurana kadar kullanıcılar arasında gönderilir.

Bir WebRTC bağlantısı oluşturmak için, istemcilerin bir WebRTC eş bağlantısı kullanmadan mesajları aktarabilmesi gerekir. Burada, iki uç nokta arasında çift yönlü bir soket bağlantısı olan HTML5 WebSockets'i kullanacağımız yer - bir web sunucusu ve bir web tarayıcısı. Şimdi WebSocket kitaplığını kullanmaya başlayalım. Server.js dosyasını oluşturun ve aşağıdaki kodu ekleyin -

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 
 
//when a user connects to our sever 
wss.on('connection', function(connection) { 
   console.log("user connected");
	
   //when server gets a message from a connected user 
   connection.on('message', function(message){ 
      console.log("Got message from a user:", message); 
   }); 
	
   connection.send("Hello from server"); 
});

İlk satır, önceden kurmuş olduğumuz WebSocket kitaplığını gerektirir. Daha sonra 9090 portunda bir soket sunucusu oluşturuyoruz. Daha sonra bağlantı olayını dinliyoruz . Bu kod, bir kullanıcı sunucuya bir WebSocket bağlantısı yaptığında çalıştırılacaktır. Daha sonra kullanıcı tarafından gönderilen mesajları dinliyoruz. Son olarak bağlı kullanıcıya “Sunucudan Merhaba” diyen bir yanıt gönderiyoruz.

Şimdi düğüm sunucusunu çalıştırın ve sunucu soket bağlantılarını dinlemeye başlamalıdır.

Sunucumuzu test etmek için, zaten kurmuş olduğumuz wscat yardımcı programını kullanacağız . Bu araç, doğrudan WebSocket sunucusuna bağlanmaya ve komutları test etmeye yardımcı olur. Sunucumuzu bir terminal penceresinde çalıştırın, ardından diğerini açın ve wscat -c ws: // localhost: 9090 komutunu çalıştırın. İstemci tarafında aşağıdakileri görmelisiniz -

Sunucu ayrıca bağlı kullanıcıyı kaydetmelidir -

Kullanıcı Kaydı

Sinyal verme sunucumuzda, her bağlantı için dizeye dayalı bir kullanıcı adı kullanacağız, böylece mesajları nereye göndereceğimizi bileceğiz. En bizim değiştirelim bağlantı işleyicisi biraz -

connection.on('message', function(message) { 
   var data; 
	
   //accepting only JSON messages 
   try { 
      data = JSON.parse(message); 
   } catch (e) { 
      console.log("Invalid JSON"); 
      data = {}; 
   } 
	
});

Bu şekilde sadece JSON mesajlarını kabul ediyoruz. Sonra, tüm bağlı kullanıcıları bir yerde saklamamız gerekiyor. Bunun için basit bir Javascript nesnesi kullanacağız. Dosyamızın üstünü değiştirin -

//require our websocket library 
var WebSocketServer = require('ws').Server;
 
//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users
var users = {};

Müşteriden gelen her mesaj için bir tip alanı ekleyeceğiz . Örneğin, bir kullanıcı giriş yapmak isterse, giriş türü mesajını gönderir . Tanımlayalım -

connection.on('message', function(message){
   var data; 
	
   //accepting only JSON messages 
   try { 
      data = JSON.parse(message); 
   } catch (e) { 
      console.log("Invalid JSON"); 
      data = {}; 
   }
	
   //switching type of the user message 
   switch (data.type) { 
      //when a user tries to login 
      case "login": 
         console.log("User logged:", data.name); 
			
         //if anyone is logged in with this username then refuse 
         if(users[data.name]) { 
            sendTo(connection, { 
               type: "login", 
               success: false 
            }); 
         } else { 
            //save user connection on the server 
            users[data.name] = connection; 
            connection.name = data.name; 
				
            sendTo(connection, { 
               type: "login", 
               success: true 
            });
				
         } 
			
         break;
					 
      default: 
         sendTo(connection, { 
            type: "error", 
            message: "Command no found: " + data.type 
         }); 
			
         break; 
   } 
	
});

Kullanıcı, giriş türü ile bir mesaj gönderirse , biz -

  • Bu kullanıcı adıyla zaten giriş yapan biri olup olmadığını kontrol edin

  • Öyleyse, kullanıcıya başarılı bir şekilde oturum açmadığını söyleyin

  • Bu kullanıcı adını kimse kullanmıyorsa, kullanıcı adını bağlantı nesnesine anahtar olarak ekleriz.

  • Bir komut tanınmazsa, bir hata göndeririz.

Aşağıdaki kod, bir bağlantıya mesaj göndermek için yardımcı bir işlevdir. Bunu server.js dosyasına ekleyin -

function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}

Yukarıdaki işlev, tüm mesajlarımızın JSON biçiminde gönderilmesini sağlar.

Kullanıcı bağlantısı kesildiğinde bağlantısını temizlemeliyiz. Kapanış olayı tetiklendiğinde kullanıcıyı silebiliriz . Aşağıdaki kodu bağlantı işleyicisine ekleyin -

connection.on("close", function() { 
   if(connection.name) { 
      delete users[connection.name]; 
    } 
});

Şimdi sunucumuzu login komutu ile test edelim. Tüm mesajların JSON biçiminde kodlanması gerektiğini unutmayın. Sunucumuzu çalıştırın ve giriş yapmayı deneyin. Bunun gibi bir şey görmelisiniz -

Arama yapmak

Başarılı bir şekilde oturum açtıktan sonra, kullanıcı başka birini aramak ister. Bunu başarmak için başka bir kullanıcıya teklif vermelidir . Teklif işleyiciyi ekleyin -

case "offer": 
   //for ex. UserA wants to call UserB 
   console.log("Sending offer to: ", data.name); 
	
   //if UserB exists then send him offer details 
   var conn = users[data.name]; 
	
   if(conn != null){ 
      //setting that UserA connected with UserB 
      connection.otherName = data.name; 
		
      sendTo(conn, { 
         type: "offer", 
         offer: data.offer, 
         name: connection.name 
      }); 
   }
	
   break;

Öncelikle aramaya çalıştığımız kullanıcının bağlantısını alıyoruz. Varsa ona teklif ayrıntılarını göndeririz . Biz de eklemek otherName için bağlantı nesnesi. Bu, daha sonra bulmanın basitliği için yapılmıştır.

Cevaplanıyor

Yanıtı yanıtlamak, teklif işleyicide kullandığımız benzer bir modele sahiptir . Sunucumuz tüm mesajları başka bir kullanıcıya cevap olarak geçirir . Teklif işleyiciden sonra aşağıdaki kodu ekleyin -

case "answer": 
   console.log("Sending answer to: ", data.name); 
	
   //for ex. UserB answers UserA 
   var conn = users[data.name]; 
	
   if(conn != null) { 
      connection.otherName = data.name; 
      sendTo(conn, { 
         type: "answer", 
         answer: data.answer 
      }); 
   }
	
   break;

Bunun teklif işleyiciye nasıl benzediğini görebilirsiniz . Bu kodun RTCPeerConnection nesnesindeki createOffer ve createAnswer işlevlerini izlediğine dikkat edin .

Şimdi teklif / cevap mekanizmamızı test edebiliriz. Aynı anda iki müşteriyi birbirine bağlayın ve teklif vermeye ve cevaplamaya çalışın. Aşağıdakileri görmelisiniz -

Bu örnekte, offer ve answer basit dizelerdir, ancak gerçek bir uygulamada SDP verileriyle doldurulacaklardır.

ICE Adayları

Son kısım, kullanıcılar arasında ICE adayını ele almaktır. Kullanıcılar arasında mesaj gönderirken aynı tekniği kullanıyoruz. Temel fark, aday mesajlarının herhangi bir sırada kullanıcı başına birden çok kez olabilmesidir. Aday işleyiciyi ekleyin -

case "candidate": 
   console.log("Sending candidate to:",data.name); 
   var conn = users[data.name]; 
	
   if(conn != null) {
      sendTo(conn, { 
         type: "candidate", 
         candidate: data.candidate 
      }); 
   }
	
   break;

Teklif ve yanıt işleyicilerine benzer şekilde çalışmalıdır .

Bağlantıdan Ayrılma

Kullanıcılarımızın başka bir kullanıcıyla bağlantısını kesmelerine izin vermek için, kapatma işlevini uygulamalıyız. Ayrıca sunucuya tüm kullanıcı referanslarını silmesini söyleyecektir. Ekleleave işleyici -

case "leave": 
   console.log("Disconnecting from", data.name); 
   var conn = users[data.name]; 
   conn.otherName = null; 
	
   //notify the other user so he can disconnect his peer connection 
   if(conn != null) { 
      sendTo(conn, { 
         type: "leave" 
      }); 
   } 
	
   break;

Bu aynı zamanda diğer kullanıcıya ayrılma olayını da gönderecek, böylece eş bağlantısını uygun şekilde kesebilecektir. Ayrıca, bir kullanıcının sinyal verme sunucusundan bağlantısını kestiği durumu da ele almalıyız. Yakın işleyicimizi değiştirelim -

connection.on("close", function() { 

   if(connection.name) { 
      delete users[connection.name]; 
		
      if(connection.otherName) { 
         console.log("Disconnecting from ", connection.otherName); 
         var conn = users[connection.otherName]; 
         conn.otherName = null;
			
         if(conn != null) { 
            sendTo(conn, { 
               type: "leave" 
            }); 
         }  
      } 
   } 
});

Şimdi bağlantı kesilirse kullanıcılarımızın bağlantısı kesilecek. Yakın biz ise hala olayın bir kullanıcı tarayıcı penceresi kapandığında ateş açılacak teklif , cevap veya aday devlet.

Eksiksiz Sinyal Sunucusu

Sinyal sunucumuzun kodunun tamamı burada -

//require our websocket library 
var WebSocketServer = require('ws').Server;
 
//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users 
var users = {};
  
//when a user connects to our sever 
wss.on('connection', function(connection) {
  
   console.log("User connected");
	
   //when server gets a message from a connected user
   connection.on('message', function(message) { 
	
      var data; 
      //accepting only JSON messages 
      try {
         data = JSON.parse(message); 
      } catch (e) { 
         console.log("Invalid JSON"); 
         data = {}; 
      } 
		
      //switching type of the user message 
      switch (data.type) { 
         //when a user tries to login 
			
         case "login": 
            console.log("User logged", data.name); 
				
            //if anyone is logged in with this username then refuse 
            if(users[data.name]) { 
               sendTo(connection, { 
                  type: "login", 
                  success: false 
               }); 
            } else { 
               //save user connection on the server 
               users[data.name] = connection; 
               connection.name = data.name; 
               sendTo(connection, { 
                  type: "login", 
                  success: true 
               }); 
            } 
				
            break; 
				
         case "offer": 
            //for ex. UserA wants to call UserB 
            console.log("Sending offer to: ", data.name); 
				
            //if UserB exists then send him offer details 
            var conn = users[data.name];
				
            if(conn != null) { 
               //setting that UserA connected with UserB 
               connection.otherName = data.name; 
					
               sendTo(conn, { 
                  type: "offer", 
                  offer: data.offer, 
                  name: connection.name 
               }); 
            } 
				
            break;  
				
         case "answer": 
            console.log("Sending answer to: ", data.name); 
            //for ex. UserB answers UserA 
            var conn = users[data.name]; 
				
            if(conn != null) { 
               connection.otherName = data.name; 
               sendTo(conn, { 
                  type: "answer", 
                  answer: data.answer 
               }); 
            } 
				
            break;  
				
         case "candidate": 
            console.log("Sending candidate to:",data.name); 
            var conn = users[data.name];  
				
            if(conn != null) { 
               sendTo(conn, { 
                  type: "candidate", 
                  candidate: data.candidate 
               });
            } 
				
            break;  
				
         case "leave": 
            console.log("Disconnecting from", data.name); 
            var conn = users[data.name]; 
            conn.otherName = null; 
				
            //notify the other user so he can disconnect his peer connection 
            if(conn != null) { 
               sendTo(conn, { 
                  type: "leave" 
               }); 
            }  
				
            break;  
				
         default: 
            sendTo(connection, { 
               type: "error", 
               message: "Command not found: " + data.type 
            }); 
				
            break; 
      }  
   });  
	
   //when user exits, for example closes a browser window 
   //this may help if we are still in "offer","answer" or "candidate" state 
   connection.on("close", function() { 
	
      if(connection.name) { 
      delete users[connection.name]; 
		
         if(connection.otherName) { 
            console.log("Disconnecting from ", connection.otherName);
            var conn = users[connection.otherName]; 
            conn.otherName = null;  
				
            if(conn != null) { 
               sendTo(conn, { 
                  type: "leave" 
               });
            }  
         } 
      } 
   });  
	
   connection.send("Hello world"); 
	
});  

function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}

Böylece iş bitti ve sinyal sunucumuz hazır. Bir WebRTC bağlantısı kurarken işleri sırayla yapmanın sorunlara neden olabileceğini unutmayın.

Özet

Bu bölümde, basit ve anlaşılır bir sinyal sunucusu oluşturduk. Sinyalleme sürecini, kullanıcı kaydını ve teklif / cevap mekanizmasını inceledik. Kullanıcılar arasında aday göndermeyi de uyguladık.

Web çok hızlı hareket ediyor ve sürekli gelişiyor. Her gün yeni standartlar oluşturulmaktadır. Tarayıcılar, güncellemelerin kullanıcının haberi olmadan yüklenmesine izin verir, bu nedenle Web ve WebRTC dünyasında neler olup bittiğini takip etmelisiniz. İşte bunun bugün ne yaptığına genel bir bakış.

Tarayıcı Desteği

Her tarayıcı aynı anda aynı WebRTC özelliklerine sahip değildir. Farklı tarayıcılar eğrinin önünde olabilir, bu da bazı WebRTC özelliklerinin bir tarayıcıda çalışmasını ve başka bir tarayıcıda çalışmasını sağlar. Tarayıcıdaki mevcut WebRTC desteği aşağıdaki resimde gösterilmektedir.

Güncel bir WebRTC destek durumunu şu adresten kontrol edebilirsiniz: http://caniuse.com/#feat=rtcpeerconnection.

Chrome, Firefox ve Opera

Mac OS X, Windows ve Linux gibi genel PC işletim sistemlerindeki Chrome, Firefox ve Opera'nın en son sürümlerinin tümü, kullanıma hazır WebRTC'yi destekler. Ve en önemlisi, Chrome ve Firefox geliştirici ekiplerinden mühendisler, bu iki tarayıcının birbirleriyle kolayca iletişim kurabilmesi için sorunları çözmek için birlikte çalışıyorlardı.

Android işletim sistemi

Android işletim sistemlerinde, Chrome ve Firefox için WebRTC uygulamaları kutudan çıkar çıkmaz çalışmalıdır. Android Ice Cream Sandwich sürümünden (4.0) sonra diğer tarayıcılarla çalışabilirler. Bu, masaüstü ve mobil sürümler arasındaki kod paylaşımından kaynaklanmaktadır.

elma

Apple, OS X üzerinde Safari'de WebRTC'yi destekleme planları hakkında henüz herhangi bir açıklama yapmadı. Hibrit yerel iOS uygulamalarının WebRTC kodunu doğrudan uygulamaya yerleştirmesi ve bu uygulamayı bir WebView'a yüklemesi için olası çözümlerden biri.

Internet Explorer

Microsoft, masaüstlerinde WebRTC'yi desteklemez. Ancak, IE'nin (Edge) gelecekteki sürümlerinde ORTC'yi (Nesne Gerçek Zamanlı İletişim) uygulayacaklarını resmi olarak onayladılar. WebRTC 1.0'ı desteklemeyi planlamıyorlar. ORTC'lerini WebRTC 1.1 olarak etiketlediler, ancak bu resmi bir standart değil, yalnızca bir topluluk geliştirmesi. Yakın zamanda ORTC desteğini en son Microsoft Edge sürümüne eklediler. Daha fazla bilgi edinebilirsinizhttps://blogs.windows.com/msedgedev/2015/09/18/ortc-api-is-now-available-in-microsoftedge/.

Özet

WebRTC'nin tek bir API değil, API'ler ve protokollerden oluşan bir koleksiyon olduğuna dikkat edin. Bunların her biri için destek, farklı tarayıcılarda ve işletim sistemlerinde farklı bir düzeyde geliştirilmektedir. En son destek düzeyini kontrol etmenin harika bir yolu,http://canisue.com.Modern API'lerin birden çok tarayıcıda benimsenmesini izler. WebRTC demolarının yanı sıra tarayıcı destekleriyle ilgili en son bilgileri şu adreste bulabilirsiniz:http://www.webrtc.org, Mozilla, Google ve Opera tarafından desteklenen.

Mobil dünyada, WebRTC desteği masaüstlerindekiyle aynı seviyede değildir. Mobil cihazların kendi yöntemleri vardır, bu nedenle WebRTC, mobil platformlarda da farklı bir şeydir.

Masaüstü için bir WebRTC uygulaması geliştirirken, Chrome, Firefox veya Opera kullanmayı düşünüyoruz. Hepsi WebRTC'yi kutudan çıktığı gibi destekler. Genel olarak, sadece bir tarayıcıya ihtiyacınız var ve masaüstünün donanımı hakkında endişelenmenize gerek yok.

Mobil dünyada bugün WebRTC için üç olası mod vardır -

  • Yerel uygulama
  • Tarayıcı uygulaması
  • Yerel tarayıcı

Android

2013 yılında, Android için Firefox web tarayıcısı kutudan çıkar çıkmaz WebRTC desteği ile sunuldu. Artık Firefox mobil tarayıcısını kullanarak Android cihazlarda görüntülü görüşme yapabilirsiniz.

Üç ana WebRTC bileşenine sahiptir -

  • PeerConnection - tarayıcılar arasında aramaları etkinleştirir

  • getUserMedia - kamera ve mikrofona erişim sağlar

  • DataChannels - eşler arası veri aktarımı sağlar

Android için Google Chrome, WebRTC desteği de sağlar. Daha önce fark ettiğiniz gibi, en ilginç özellikler genellikle ilk olarak Chrome'da görünür.

Geçtiğimiz yıl Opera mobil tarayıcısı WebRTC desteği ile ortaya çıktı. Yani Android için Chrome, Firefox ve Opera'ya sahipsiniz. Diğer tarayıcılar WebRTC'yi desteklemez.

iOS

Maalesef WebRTC şu anda iOS'ta desteklenmiyor. WebRTC, Firefox, Opera veya Chrome kullanırken Mac'te iyi çalışsa da, iOS'ta desteklenmez.

Günümüzde WebRTC uygulamanız, kutudan çıktığı anda Apple mobil cihazlarda çalışmayacaktır. Ama bir tarayıcı var - Bowser. Ericsson tarafından geliştirilmiş bir web tarayıcısıdır ve WebRTC'yi kutudan çıkar çıkmaz destekler. Ana sayfasını şu adresten kontrol edebilirsiniz:http://www.openwebrtc.org/bowser/.

Bugün, iOS'ta WebRTC uygulamanızı desteklemenin tek kolay yolu budur. Başka bir yol da yerel bir uygulamayı kendiniz geliştirmektir.

Windows Telefonları

Microsoft, mobil platformlarda WebRTC'yi desteklemez. Ancak IE'nin gelecekteki sürümlerinde ORTC'yi (Object Realtime Communications) uygulayacaklarını resmi olarak onayladılar. WebRTC 1.0'ı desteklemeyi planlamıyorlar. ORTC'lerini WebRTC 1.1 olarak etiketlediler, ancak bu resmi bir standart değil, yalnızca bir topluluk geliştirmesi.

Yani bugün Window Phone kullanıcıları WebRTC uygulamalarını kullanamıyor ve bu durumu yenmenin bir yolu yok.

Böğürtlen

WebRTC uygulamaları da hiçbir şekilde Blackberry'de desteklenmemektedir.

WebRTC Yerel Tarayıcı Kullanma

Kullanıcıların WebRTC'yi kullanması için en uygun ve rahat durum, cihazın yerel tarayıcısını kullanmaktır. Bu durumda, cihaz herhangi bir ek konfigürasyonda çalışmaya hazırdır.

Bugün yalnızca sürüm 4 veya üzeri olan Android cihazlar bu özelliği sağlamaktadır. Apple hala WebRTC desteğiyle herhangi bir etkinlik göstermiyor. Yani Safari kullanıcıları WebRTC uygulamalarını kullanamaz. Microsoft ayrıca bunu Windows Phone 8'de tanıtmadı.

Tarayıcı Uygulamaları aracılığıyla WebRTC'yi Kullanma

Bu, WebRTC özelliklerini sağlamak için üçüncü taraf uygulamaları (yerel olmayan web tarayıcıları) kullanmak anlamına gelir. Şimdilik, bu tür iki üçüncü taraf uygulaması var. WebRTC özelliklerini iOS cihaza getirmenin tek yolu olan Bowser ve Android platformu için güzel bir alternatif olan Opera. Mevcut mobil tarayıcıların geri kalanı WebRTC'yi desteklemez.

Yerel Mobil Uygulamalar

Gördüğünüz gibi, WebRTC henüz mobil dünyada büyük bir desteğe sahip değil. Dolayısıyla, olası çözümlerden biri, WebRTC API kullanan yerel bir uygulama geliştirmektir. Ancak, ana WebRTC özelliği çapraz platform çözümü olduğu için daha iyi bir seçim değildir. Her neyse, bazı durumlarda bu, yerel bir uygulamanın cihaza özgü işlevleri veya HTML5 tarayıcıları tarafından desteklenmeyen özellikleri kullanabilmesinin tek yoludur.

Mobil ve Masaüstü Cihazlar için Video Akışını Kısıtlama

GetUserMedia API'sinin ilk parametresi , tarayıcıya akışları nasıl işleyeceğini söyleyen bir anahtarlar ve değerler nesnesi bekler. Tüm kısıtlamaları şu adresten kontrol edebilirsiniz:https://tools.ietf.org/html/draft-alvestrand-constraints-resolution-03. Video en boy oranını, frameRate'i ve diğer isteğe bağlı parametreleri ayarlayabilirsiniz.

Mobil cihazların desteklenmesi en büyük sorunlardan biridir çünkü mobil cihazlar sınırlı kaynakların yanı sıra sınırlı ekran alanına sahiptir. Güç ve bant genişliğinden tasarruf etmek için mobil aygıtın yalnızca 480x320 çözünürlüklü veya daha küçük bir video akışını yakalamasını isteyebilirsiniz. Tarayıcıdaki kullanıcı aracısı dizesini kullanmak, kullanıcının bir mobil cihazda olup olmadığını test etmenin iyi bir yoludur. Bir örnek görelim. İndex.html dosyasını oluşturun -

<!DOCTYPE html> 
<html lang = "en">
 
   <head> 
      <meta charset = "utf-8" /> 
   </head> 
	
   <body> 
      <video autoplay></video> 
      <script src = "client.js"></script> 
   </body>
	
</html>

Ardından aşağıdaki client.js dosyasını oluşturun -

//constraints for desktop browser 
var desktopConstraints = { 

   video: { 
      mandatory: { 
         maxWidth:800,
         maxHeight:600   
      }  
   }, 
	
   audio: true 
}; 
 
//constraints for mobile browser 
var mobileConstraints = { 

   video: { 
      mandatory: { 
         maxWidth: 480, 
         maxHeight: 320, 
      } 
   }, 
	
   audio: true 
}
  
//if a user is using a mobile browser 
if(/Android|iPhone|iPad/i.test(navigator.userAgent)) { 
   var constraints = mobileConstraints;   
} else { 
   var constraints = desktopConstraints; 
}
  
function hasUserMedia() { 
   //check if the browser supports the WebRTC 
   return !!(navigator.getUserMedia || navigator.webkitGetUserMedia || 
      navigator.mozGetUserMedia); 
}
  
if (hasUserMedia()) {
  
   navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || 
      navigator.mozGetUserMedia;
	
   //enabling video and audio channels 
   navigator.getUserMedia(constraints, function (stream) { 
      var video = document.querySelector('video');
		
      //inserting our stream to the video tag     
      video.src = window.URL.createObjectURL(stream);
		
   }, function (err) {}); 
} else { 
   alert("WebRTC is not supported"); 
}

Statik komutu kullanarak web sunucusunu çalıştırın ve sayfayı açın. 800x600 olduğunu görmelisiniz. Ardından bu sayfayı krom araçlarını kullanarak bir mobil görünümde açın ve çözünürlüğü kontrol edin. 480x320 boyutunda olmalıdır.

Kısıtlamalar, WebRTC uygulamanızın performansını artırmanın en kolay yoludur.

Özet

Bu bölümde, mobil cihazlar için WebRTC uygulamaları geliştirirken ortaya çıkabilecek sorunları öğrendik. Mobil platformlarda WebRTC API'yi desteklemenin farklı sınırlamalarını keşfettik. Ayrıca masaüstü ve mobil tarayıcılar için farklı kısıtlamalar belirlediğimiz bir demo uygulaması başlattık.

Bu bölümde, iki kullanıcının ayrı cihazlarda WebRTC kullanarak iletişim kurmasına izin veren bir istemci uygulaması oluşturacağız. Uygulamamız iki sayfadan oluşacaktır. Biri oturum açmak için diğeri başka bir kullanıcıyı aramak için.

İki sayfa div etiketleri olacaktır. Girişlerin çoğu basit olay işleyicileri aracılığıyla yapılır.

Sinyal Sunucusu

Bir WebRTC bağlantısı oluşturmak için, istemcilerin bir WebRTC eş bağlantısı kullanmadan mesajları aktarabilmesi gerekir. Burada, iki uç nokta arasında çift yönlü bir soket bağlantısı olan HTML5 WebSockets'i kullanacağımız yer - bir web sunucusu ve bir web tarayıcısı. Şimdi WebSocket kitaplığını kullanmaya başlayalım. Server.js dosyasını oluşturun ve aşağıdaki kodu ekleyin -

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090});
  
//when a user connects to our sever 
wss.on('connection', function(connection) { 
   console.log("user connected"); 
	
   //when server gets a message from a connected user 
   connection.on('message', function(message) { 
      console.log("Got message from a user:", message); 
   }); 
	
   connection.send("Hello from server");
});

İlk satır, önceden kurmuş olduğumuz WebSocket kitaplığını gerektirir. Daha sonra 9090 portunda bir soket sunucusu oluşturuyoruz. Daha sonra bağlantı olayını dinliyoruz . Bu kod, bir kullanıcı sunucuya bir WebSocket bağlantısı yaptığında çalıştırılacaktır. Daha sonra kullanıcı tarafından gönderilen mesajları dinliyoruz. Son olarak bağlı kullanıcıya “Sunucudan Merhaba” diyen bir yanıt gönderiyoruz.

Sinyal verme sunucumuzda, her bağlantı için dizeye dayalı bir kullanıcı adı kullanacağız, böylece mesajları nereye göndereceğimizi bileceğiz. Bağlantı işleyicimizi biraz değiştirelim -

connection.on('message', function(message) { 
   var data; 
	
   //accepting only JSON messages 
   try { 
      data = JSON.parse(message); 
   } catch (e) { 
      console.log("Invalid JSON"); 
      data = {}; 
   } 
});

Bu şekilde sadece JSON mesajlarını kabul ediyoruz. Sonra, tüm bağlı kullanıcıları bir yerde saklamamız gerekiyor. Bunun için basit bir Javascript nesnesi kullanacağız. Dosyamızın üstünü değiştirin -

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users 
var users = {};

Müşteriden gelen her mesaj için bir tip alanı ekleyeceğiz . Örneğin, bir kullanıcı giriş yapmak isterse, giriş türü mesajını gönderir . Tanımlayalım -

connection.on('message', function(message) { 
   var data; 
	
   //accepting only JSON messages 
   try { 
      data = JSON.parse(message);
   } catch (e) { 
      console.log("Invalid JSON"); 
      data = {}; 
   } 
	
   //switching type of the user message 
   switch (data.type) { 
      //when a user tries to login 
      case "login": 
         console.log("User logged:", data.name); 
			
         //if anyone is logged in with this username then refuse 
         if(users[data.name]) { 
            sendTo(connection, { 
               type: "login", 
               success: false 
            }); 
         } else { 
            //save user connection on the server 
            users[data.name] = connection; 
            connection.name = data.name; 
				
            sendTo(connection, { 
               type: "login", 
               success: true 
            }); 
         } 
			
         break;
			
      default: 
         sendTo(connection, { 
            type: "error", 
            message: "Command no found: " + data.type 
         }); 
			
         break;
   }  
});

Kullanıcı, giriş türü ile bir mesaj gönderirse , biz -

  • Bu kullanıcı adıyla zaten giriş yapan biri olup olmadığını kontrol edin

  • Öyleyse, kullanıcıya başarılı bir şekilde oturum açmadığını söyleyin

  • Bu kullanıcı adını kimse kullanmıyorsa, kullanıcı adını bağlantı nesnesine anahtar olarak ekleriz.

  • Bir komut tanınmazsa, bir hata göndeririz.

Aşağıdaki kod, bir bağlantıya mesaj göndermek için yardımcı bir işlevdir. Bunu server.js dosyasına ekleyin -

function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}

Kullanıcı bağlantısı kesildiğinde bağlantısını temizlemeliyiz. Kapanış olayı tetiklendiğinde kullanıcıyı silebiliriz . Aşağıdaki kodu bağlantı işleyicisine ekleyin -

connection.on("close", function() { 
   if(connection.name) { 
      delete users[connection.name]; 
   } 
});

Başarılı bir şekilde oturum açtıktan sonra, kullanıcı başka birini aramak ister. Bunu başarmak için başka bir kullanıcıya teklif vermelidir . Teklif işleyiciyi ekleyin -

case "offer": 
   //for ex. UserA wants to call UserB 
   console.log("Sending offer to: ", data.name);
	
   //if UserB exists then send him offer details 
   var conn = users[data.name]; 
	
   if(conn != null) { 
      //setting that UserA connected with UserB 
      connection.otherName = data.name; 
		
      sendTo(conn, { 
         type: "offer",
         offer: data.offer, 
         name: connection.name 
      }); 
		
   }  
	
   break;

Öncelikle aramaya çalıştığımız kullanıcının bağlantısını alıyoruz. Varsa ona teklif ayrıntılarını göndeririz . Biz de eklemek otherName için bağlantı nesnesi. Bu, daha sonra bulmanın basitliği için yapılmıştır.

Yanıtı yanıtlamak, teklif işleyicide kullandığımız benzer bir modele sahiptir . Sunucumuz tüm mesajları başka bir kullanıcıya cevap olarak geçirir . Teklif işleyiciden sonra aşağıdaki kodu ekleyin -

case "answer": 
   console.log("Sending answer to: ", data.name); 
	
   //for ex. UserB answers UserA 
   var conn = users[data.name]; 
	
   if(conn != null) { 
      connection.otherName = data.name; 
		
      sendTo(conn, { 
         type: "answer", 
         answer: data.answer 
      }); 
   } 
	
   break;

Son kısım, kullanıcılar arasında ICE adayını ele almaktır. Kullanıcılar arasında mesaj gönderirken aynı tekniği kullanıyoruz. Temel fark, aday mesajlarının herhangi bir sırada kullanıcı başına birden çok kez olabilmesidir. Aday işleyiciyi ekleyin -

case "candidate": 
   console.log("Sending candidate to:",data.name); 
   var conn = users[data.name];
	
   if(conn != null) { 
      sendTo(conn, { 
         type: "candidate", 
         candidate: data.candidate 
      }); 
   } 
	
   break;

Kullanıcılarımızın başka bir kullanıcıyla bağlantısını kesmelerine izin vermek için, kapatma işlevini uygulamalıyız. Ayrıca sunucuya tüm kullanıcı referanslarını silmesini söyleyecektir. İzin işleyicisini ekleyin -

case "leave": 
   console.log("Disconnecting from", data.name); 
   var conn = users[data.name]; 
   conn.otherName = null; 
	
   //notify the other user so he can disconnect his peer connection 
   if(conn != null) { 
      sendTo(conn, { 
         type: "leave" 
      }); 
   } 
	
   break;

Bu aynı zamanda diğer kullanıcıya ayrılma olayını da gönderecek, böylece eş bağlantısını uygun şekilde kesebilecektir. Ayrıca, bir kullanıcının sinyal verme sunucusundan bağlantısını kestiği durumu da ele almalıyız. Yakın işleyicimizi değiştirelim -

connection.on("close", function() { 

   if(connection.name) { 
      delete users[connection.name]; 
		
      if(connection.otherName) { 
         console.log("Disconnecting from ", connection.otherName); 
         var conn = users[connection.otherName]; 
         conn.otherName = null;  
			
         if(conn != null) { 
            sendTo(conn, { 
               type: "leave" 
            });
         }
			
      } 
   } 
});

Aşağıdaki, sinyal sunucumuzun kodunun tamamıdır -

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users 
var users = {};
  
//when a user connects to our sever 
wss.on('connection', function(connection) {
  
   console.log("User connected");
	
   //when server gets a message from a connected user 
   connection.on('message', function(message) { 
	
      var data; 
		
      //accepting only JSON messages 
      try { 
         data = JSON.parse(message); 
      } catch (e) { 
         console.log("Invalid JSON"); 
         data = {}; 
      }
		
      //switching type of the user message 
      switch (data.type) { 
         //when a user tries to login
         case "login": 
            console.log("User logged", data.name); 
				
            //if anyone is logged in with this username then refuse 
            if(users[data.name]) { 
               sendTo(connection, { 
                  type: "login", 
                  success: false 
               }); 
            } else { 
               //save user connection on the server 
               users[data.name] = connection; 
               connection.name = data.name; 
					
               sendTo(connection, { 
                  type: "login", 
                  success: true 
               }); 
            } 
				
            break;
				
         case "offer": 
            //for ex. UserA wants to call UserB 
            console.log("Sending offer to: ", data.name);
				
            //if UserB exists then send him offer details 
            var conn = users[data.name]; 
				
            if(conn != null) { 
               //setting that UserA connected with UserB 
               connection.otherName = data.name; 
					
               sendTo(conn, { 
                  type: "offer", 
                  offer: data.offer, 
                  name: connection.name 
               }); 
            }
				
            break;
				
         case "answer": 
            console.log("Sending answer to: ", data.name); 
            //for ex. UserB answers UserA 
            var conn = users[data.name]; 
				
            if(conn != null) { 
               connection.otherName = data.name; 
               sendTo(conn, { 
                  type: "answer", 
                  answer: data.answer 
               }); 
            } 
				
            break; 
				
         case "candidate": 
            console.log("Sending candidate to:",data.name); 
            var conn = users[data.name];
				
            if(conn != null) { 
               sendTo(conn, { 
                  type: "candidate", 
                  candidate: data.candidate 
               }); 
            } 
				
            break;
				
         case "leave": 
            console.log("Disconnecting from", data.name); 
            var conn = users[data.name]; 
            conn.otherName = null; 
				
            //notify the other user so he can disconnect his peer connection 
            if(conn != null) {
               sendTo(conn, { 
                  type: "leave" 
              }); 
            }
				
            break;
				
         default: 
            sendTo(connection, { 
               type: "error", 
               message: "Command not found: " + data.type 
            }); 
				
            break; 
      }
		
   }); 
	
   //when user exits, for example closes a browser window 
   //this may help if we are still in "offer","answer" or "candidate" state 
   connection.on("close", function() { 
	
      if(connection.name) { 
         delete users[connection.name]; 
			
         if(connection.otherName) { 
            console.log("Disconnecting from ", connection.otherName); 
            var conn = users[connection.otherName]; 
            conn.otherName = null;
				
            if(conn != null) { 
               sendTo(conn, { 
                  type: "leave" 
               }); 
            }
         } 
      }
		
   });  
	
   connection.send("Hello world");  
});
  
function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}

İstemci Uygulaması

Bu uygulamayı test etmenin bir yolu, iki tarayıcı sekmesi açıp birbirini aramaya çalışmaktır.

Öncelikle bootstrap kütüphanesini kurmamız gerekiyor . Bootstrap, web uygulamaları geliştirmek için bir ön uç çerçevesidir. Daha fazla bilgi edinebilirsinizhttp://getbootstrap.com/.Örneğin "görüntülü sohbet" adlı bir klasör oluşturun. Bu bizim kök uygulama klasörümüz olacak. Bu klasörün içinde package.json adlı bir dosya oluşturun (npm bağımlılıklarını yönetmek için gereklidir) ve aşağıdakileri ekleyin -

{ 
   "name": "webrtc-videochat", 
   "version": "0.1.0", 
   "description": "webrtc-videochat", 
   "author": "Author", 
   "license": "BSD-2-Clause" 
}

Ardından npm install bootstrap komutunu çalıştırın . Bu, bootstrap kitaplığını videochat / node_modules klasörüne yükleyecektir .

Şimdi temel bir HTML sayfası oluşturmamız gerekiyor. Aşağıdaki kodla kök klasörde bir index.html dosyası oluşturun -

<html> 
 
   <head> 
      <title>WebRTC Video Demo</title>
      <link rel = "stylesheet" href = "node_modules/bootstrap/dist/css/bootstrap.min.css"/>
   </head>
	
   <style>
	
      body { 
         background: #eee; 
         padding: 5% 0; 
      } 
		
      video { 
         background: black; 
         border: 1px solid gray; 
      }
		
      .call-page { 
         position: relative; 
         display: block; 
         margin: 0 auto; 
         width: 500px; 
         height: 500px; 
      } 
		
      #localVideo { 
         width: 150px; 
         height: 150px; 
         position: absolute; 
         top: 15px; 
         right: 15px; 
      }
		
      #remoteVideo { 
         width: 500px; 
         height: 500px; 
      }
		
   </style>
	
   <body>
	
   <div id = "loginPage" class = "container text-center"> 
	
      <div class = "row"> 
         <div class = "col-md-4 col-md-offset-4">
			
            <h2>WebRTC Video Demo. Please sign in</h2> 
            <label for = "usernameInput" class = "sr-only">Login</label> 
            <input type = "email" id = "usernameInput" c
               lass = "form-control formgroup" placeholder = "Login" 
               required = "" autofocus = ""> 
            <button id = "loginBtn" class = "btn btn-lg btn-primary btnblock">
               Sign in</button>
				
         </div> 
      </div> 
		
   </div>
	
   <div id = "callPage" class = "call-page"> 
      <video id = "localVideo" autoplay></video> 
      <video id = "remoteVideo" autoplay></video>
		
      <div class = "row text-center"> 
         <div class = "col-md-12"> 
            <input id = "callToUsernameInput" type = "text"
               placeholder = "username to call" /> 
            <button id = "callBtn" class = "btn-success btn">Call</button> 
            <button id = "hangUpBtn" class = "btn-danger btn">Hang Up</button> 
         </div>	
      </div> 
		
   </div>
	
   <script src = "client.js"></script> 
	
   </body>
	
</html>

Bu sayfa size tanıdık gelmelidir. Bootstrap css dosyasını ekledik . Ayrıca iki sayfa tanımladık. Son olarak, kullanıcıdan bilgi almak için birkaç metin alanı ve düğme oluşturduk. Yerel ve uzak video akışları için iki video öğesini görmelisiniz. Client.js dosyasına bir bağlantı eklediğimize dikkat edin .

Şimdi sinyal sunucumuzla bir bağlantı kurmamız gerekiyor. Aşağıdaki kodla kök klasörde client.js dosyasını oluşturun -

//our username 
var name; 
var connectedUser;
  
//connecting to our signaling server 
var conn = new WebSocket('ws://localhost:9090');
  
conn.onopen = function () { 
   console.log("Connected to the signaling server"); 
};
  
//when we got a message from a signaling server 
conn.onmessage = function (msg) { 
   console.log("Got message", msg.data);
	
   var data = JSON.parse(msg.data);
	
   switch(data.type) { 
      case "login": 
         handleLogin(data.success);
         break; 
      //when somebody wants to call us 
      case "offer": 
         handleOffer(data.offer, data.name); 
         break; 
      case "answer": 
         handleAnswer(data.answer); 
         break; 
      //when a remote peer sends an ice candidate to us 
      case "candidate": 
         handleCandidate(data.candidate); 
         break; 
      case "leave": 
         handleLeave(); 
         break; 
      default: 
         break; 
   } 
};
  
conn.onerror = function (err) { 
   console.log("Got error", err); 
};
  
//alias for sending JSON encoded messages 
function send(message) { 
   //attach the other peer username to our messages 
   if (connectedUser) { 
      message.name = connectedUser; 
   } 
	
   conn.send(JSON.stringify(message)); 
};

Şimdi sinyal sunucumuzu düğüm sunucusu aracılığıyla çalıştırın . Ardından, kök klasörün içinde statik komutu çalıştırın ve sayfayı tarayıcının içinde açın. Aşağıdaki konsol çıktısını görmelisiniz -

Bir sonraki adım, benzersiz bir kullanıcı adıyla bir kullanıcı girişi uygulamaktır. Sadece sunucuya bir kullanıcı adı göndeririz, bu da bize onun alınıp alınmadığını söyler. Aşağıdaki kodu client.js dosyanıza ekleyin -

//****** 
//UI selectors block 
//****** 

var loginPage = document.querySelector('#loginPage'); 
var usernameInput = document.querySelector('#usernameInput'); 
var loginBtn = document.querySelector('#loginBtn'); 

var callPage = document.querySelector('#callPage'); 
var callToUsernameInput = document.querySelector('#callToUsernameInput');
var callBtn = document.querySelector('#callBtn'); 

var hangUpBtn = document.querySelector('#hangUpBtn');
  
//hide call page 
callPage.style.display = "none"; 
 
// Login when the user clicks the button 
loginBtn.addEventListener("click", function (event) { 
   name = usernameInput.value; 
	
   if (name.length > 0) { 
      send({ 
         type: "login", 
         name: name 
      }); 
   } 
	
});
 
function handleLogin(success) { 

   if (success === false) { 
      alert("Ooops...try a different username"); 
   } else { 
      //display the call page if login is successful 
      loginPage.style.display = "none"; 
      callPage.style.display = "block";  
      //start peer connection 
   } 
};

İlk olarak, sayfadaki elemanlara bazı referanslar seçiyoruz. Çağrı sayfasını gizleriz. Ardından, giriş butonuna bir olay dinleyicisi ekliyoruz. Kullanıcı tıkladığında, kullanıcı adını sunucuya göndeririz. Son olarak, handleLogin geri aramasını gerçekleştiriyoruz. Giriş başarılı olduysa, arama sayfasını gösterir ve bir eş bağlantı kurmaya başlarız.

Akran bağlantısı başlatmak için ihtiyacımız olan -

  • Web kamerasından bir akış alın.
  • RTCPeerConnection nesnesini oluşturun.

Aşağıdaki kodu "UI seçici bloğuna" ekleyin -

var localVideo = document.querySelector('#localVideo'); 
var remoteVideo = document.querySelector('#remoteVideo');
 
var yourConn; 
var stream;

HandleLogin işlevini değiştirin -

function handleLogin(success) { 

   if (success === false) { 
      alert("Ooops...try a different username"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block";
		  
      //********************** 
      //Starting a peer connection 
      //********************** 
		
      //getting local video stream 
      navigator.webkitGetUserMedia({ video: true, audio: true }, function (myStream) { 
         stream = myStream; 
			
         //displaying local video stream on the page 
         localVideo.src = window.URL.createObjectURL(stream);
			
         //using Google public stun server 
         var configuration = { 
            "iceServers": [{ "url": "stun:stun2.1.google.com:19302" }] 
         }; 
			
         yourConn = new webkitRTCPeerConnection(configuration);
			
         // setup stream listening 
         yourConn.addStream(stream); 
			
         //when a remote user adds stream to the peer connection, we display it 
         yourConn.onaddstream = function (e) { 
            remoteVideo.src = window.URL.createObjectURL(e.stream); 
         };
			
         // Setup ice handling 
         yourConn.onicecandidate = function (event) {
			
            if (event.candidate) { 
               send({ 
                  type: "candidate", 
                  candidate: event.candidate 
               }); 
            } 
				
         };
			
      }, function (error) { 
         console.log(error); 
      }); 
   } 
};

Şimdi kodu çalıştırırsanız, sayfa oturum açmanıza ve sayfada yerel video akışınızı görüntülemenize izin vermelidir.

Şimdi bir arama başlatmaya hazırız. Öncelikle başka bir kullanıcıya teklif gönderiyoruz . Bir kullanıcı teklifi aldıktan sonra bir cevap oluşturur ve ICE adayları ile ticaret yapmaya başlar. Aşağıdaki kodu client.js dosyasına ekleyin -

//initiating a call 
callBtn.addEventListener("click", function () { 
   var callToUsername = callToUsernameInput.value; 
	
   if (callToUsername.length > 0) {
	
      connectedUser = callToUsername;
		
      // create an offer
      yourConn.createOffer(function (offer) { 
         send({ 
            type: "offer", 
            offer: offer 
         }); 
			
         yourConn.setLocalDescription(offer); 
			
      }, function (error) { 
         alert("Error when creating an offer"); 
      });  
   } 
});
  
//when somebody sends us an offer 
function handleOffer(offer, name) { 
   connectedUser = name; 
   yourConn.setRemoteDescription(new RTCSessionDescription(offer));
	
   //create an answer to an offer 
   yourConn.createAnswer(function (answer) { 
      yourConn.setLocalDescription(answer); 
		
      send({ 
         type: "answer", 
         answer: answer 
      }); 
		
   }, function (error) { 
      alert("Error when creating an answer"); 
   }); 
};
  
//when we got an answer from a remote user 
function handleAnswer(answer) { 
   yourConn.setRemoteDescription(new RTCSessionDescription(answer));
}; 
 
//when we got an ice candidate from a remote user 
function handleCandidate(candidate) { 
   yourConn.addIceCandidate(new RTCIceCandidate(candidate)); 
};

Çağrı düğmesine bir teklif başlatan bir tıklama işleyici ekliyoruz . Ardından onmessage işleyicisinin beklediği birkaç işleyiciyi uygularız . Her iki kullanıcı da bir bağlantı kurana kadar eşzamansız olarak işlenecekler.

Son adım, kapatma özelliğini uygulamaktır. Bu, veri aktarımını durduracak ve diğer kullanıcıya aramayı kapatmasını söyleyecektir. Aşağıdaki kodu ekleyin -

//hang up 
hangUpBtn.addEventListener("click", function () { 

   send({ 
      type: "leave" 
   });
	
   handleLeave(); 
});
  
function handleLeave() { 
   connectedUser = null; 
   remoteVideo.src = null; 
	
   yourConn.close(); 
   yourConn.onicecandidate = null; 
   yourConn.onaddstream = null; 
};

Kullanıcı Kapat düğmesini tıkladığında -

  • Diğer kullanıcıya bir "ayrılma" mesajı gönderecektir
  • RTCPeerConnection'ı kapatacak ve bağlantıyı yerel olarak yok edecek

Şimdi kodu çalıştırın. İki tarayıcı sekmesi kullanarak sunucuda oturum açabilmelisiniz. Daha sonra sekmeyi arayabilir ve aramayı kapatabilirsiniz.

Aşağıdaki tüm client.js dosyasıdır -

//our username 
var name; 
var connectedUser;
  
//connecting to our signaling server
var conn = new WebSocket('ws://localhost:9090');
  
conn.onopen = function () { 
   console.log("Connected to the signaling server"); 
};
  
//when we got a message from a signaling server 
conn.onmessage = function (msg) { 
   console.log("Got message", msg.data);
	
   var data = JSON.parse(msg.data); 
	
   switch(data.type) { 
      case "login": 
         handleLogin(data.success); 
         break; 
      //when somebody wants to call us 
      case "offer": 
         handleOffer(data.offer, data.name); 
         break; 
      case "answer": 
         handleAnswer(data.answer); 
         break; 
      //when a remote peer sends an ice candidate to us 
      case "candidate": 
         handleCandidate(data.candidate); 
         break; 
      case "leave": 
         handleLeave(); 
         break; 
      default: 
         break; 
   }
};
  
conn.onerror = function (err) { 
   console.log("Got error", err); 
};
  
//alias for sending JSON encoded messages 
function send(message) { 
   //attach the other peer username to our messages 
   if (connectedUser) { 
      message.name = connectedUser; 
   } 
	
   conn.send(JSON.stringify(message)); 
};
  
//****** 
//UI selectors block 
//******
 
var loginPage = document.querySelector('#loginPage'); 
var usernameInput = document.querySelector('#usernameInput'); 
var loginBtn = document.querySelector('#loginBtn'); 

var callPage = document.querySelector('#callPage'); 
var callToUsernameInput = document.querySelector('#callToUsernameInput');
var callBtn = document.querySelector('#callBtn'); 

var hangUpBtn = document.querySelector('#hangUpBtn');
  
var localVideo = document.querySelector('#localVideo'); 
var remoteVideo = document.querySelector('#remoteVideo'); 

var yourConn; 
var stream;
  
callPage.style.display = "none";

// Login when the user clicks the button 
loginBtn.addEventListener("click", function (event) { 
   name = usernameInput.value;
	
   if (name.length > 0) { 
      send({ 
         type: "login", 
         name: name 
      }); 
   }
	
});
  
function handleLogin(success) { 
   if (success === false) { 
      alert("Ooops...try a different username"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block";
		
      //********************** 
      //Starting a peer connection 
      //********************** 
		
      //getting local video stream 
      navigator.webkitGetUserMedia({ video: true, audio: true }, function (myStream) { 
         stream = myStream; 
			
         //displaying local video stream on the page 
         localVideo.src = window.URL.createObjectURL(stream);
			
         //using Google public stun server 
         var configuration = { 
            "iceServers": [{ "url": "stun:stun2.1.google.com:19302" }]
         }; 
			
         yourConn = new webkitRTCPeerConnection(configuration); 
			
         // setup stream listening 
         yourConn.addStream(stream); 
			
         //when a remote user adds stream to the peer connection, we display it 
         yourConn.onaddstream = function (e) { 
            remoteVideo.src = window.URL.createObjectURL(e.stream); 
         };
			
         // Setup ice handling 
         yourConn.onicecandidate = function (event) { 
            if (event.candidate) { 
               send({ 
                  type: "candidate", 
                  candidate: event.candidate 
               }); 
            } 
         };  
			
      }, function (error) { 
         console.log(error); 
      }); 
		
   } 
};
  
//initiating a call 
callBtn.addEventListener("click", function () { 
   var callToUsername = callToUsernameInput.value;
	
   if (callToUsername.length > 0) { 
	
      connectedUser = callToUsername;
		
      // create an offer 
      yourConn.createOffer(function (offer) { 
         send({ 
            type: "offer", 
            offer: offer 
         }); 
			
         yourConn.setLocalDescription(offer); 
      }, function (error) { 
         alert("Error when creating an offer"); 
      });
		
   } 
});
  
//when somebody sends us an offer 
function handleOffer(offer, name) { 
   connectedUser = name; 
   yourConn.setRemoteDescription(new RTCSessionDescription(offer));
	
   //create an answer to an offer 
   yourConn.createAnswer(function (answer) { 
      yourConn.setLocalDescription(answer); 
		
      send({ 
         type: "answer", 
         answer: answer 
      }); 
		
   }, function (error) { 
      alert("Error when creating an answer"); 
   }); 
};
  
//when we got an answer from a remote user
function handleAnswer(answer) { 
   yourConn.setRemoteDescription(new RTCSessionDescription(answer)); 
};
  
//when we got an ice candidate from a remote user 
function handleCandidate(candidate) { 
   yourConn.addIceCandidate(new RTCIceCandidate(candidate)); 
};
   
//hang up 
hangUpBtn.addEventListener("click", function () { 

   send({ 
      type: "leave" 
   });  
	
   handleLeave(); 
});
  
function handleLeave() { 
   connectedUser = null; 
   remoteVideo.src = null; 
	
   yourConn.close(); 
   yourConn.onicecandidate = null; 
   yourConn.onaddstream = null; 
};

Özet

Bu demo, her WebRTC uygulamasının ihtiyaç duyduğu özelliklerin temelini sağlar. Bu demoyu iyileştirmek için Facebook veya Google gibi platformlar aracılığıyla kullanıcı kimliği ekleyebilir, geçersiz veriler için kullanıcı girişini yönetebilirsiniz. Ayrıca, WebRTC bağlantısı, teknolojiyi desteklememek veya güvenlik duvarlarını geçememek gibi çeşitli nedenlerden dolayı başarısız olabilir. Herhangi bir WebRTC uygulamasını kararlı hale getirmek için önemli bir çalışma yapıldı.

Bu bölümde, iki kullanıcının ayrı cihazlarda WebRTC ses akışlarını kullanarak iletişim kurmasına olanak tanıyan bir istemci uygulaması oluşturacağız. Uygulamamız iki sayfadan oluşacaktır. Biri oturum açmak için, diğeri başka bir kullanıcıya sesli arama yapmak için.

İki sayfa div etiketleri olacaktır. Girişlerin çoğu basit olay işleyicileri aracılığıyla yapılır.

Sinyal Sunucusu

Bir WebRTC bağlantısı oluşturmak için, istemcilerin bir WebRTC eş bağlantısı kullanmadan mesajları aktarabilmesi gerekir. Burada, iki uç nokta arasında çift yönlü bir soket bağlantısı olan HTML5 WebSockets'i kullanacağımız yer - bir web sunucusu ve bir web tarayıcısı. Şimdi WebSocket kitaplığını kullanmaya başlayalım. Server.js dosyasını oluşturun ve aşağıdaki kodu ekleyin -

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090});
  
//when a user connects to our sever 
wss.on('connection', function(connection) { 
   console.log("user connected"); 
	
   //when server gets a message from a connected user 
   connection.on('message', function(message) { 
      console.log("Got message from a user:", message); 
   }); 
	
   connection.send("Hello from server"); 
});

İlk satır, önceden kurmuş olduğumuz WebSocket kitaplığını gerektirir. Daha sonra 9090 portunda bir soket sunucusu oluşturuyoruz. Daha sonra bağlantı olayını dinliyoruz . Bu kod, bir kullanıcı sunucuya bir WebSocket bağlantısı yaptığında çalıştırılacaktır. Daha sonra kullanıcı tarafından gönderilen mesajları dinliyoruz. Son olarak bağlı kullanıcıya “Sunucudan Merhaba” diyen bir yanıt gönderiyoruz.

Sinyal verme sunucumuzda, her bağlantı için dizeye dayalı bir kullanıcı adı kullanacağız, böylece mesajları nereye göndereceğimizi bileceğiz. En bizim değiştirelim bağlantı işleyicisi biraz -

connection.on('message', function(message) { 
   var data; 
	
   //accepting only JSON messages 
   try { 
      data = JSON.parse(message); 
   } catch (e) { 
      console.log("Invalid JSON");
      data = {}; 
   } 
});

Bu şekilde sadece JSON mesajlarını kabul ediyoruz. Sonra, tüm bağlı kullanıcıları bir yerde saklamamız gerekiyor. Bunun için basit bir Javascript nesnesi kullanacağız. Dosyamızın üstünü değiştirin -

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users 
var users = {};

Müşteriden gelen her mesaj için bir tip alanı ekleyeceğiz . Örneğin, bir kullanıcı giriş yapmak isterse, giriş türü mesajını gönderir . Tanımlayalım -

connection.on('message', function(message) {
  
   var data; 
   //accepting only JSON messages 
   try { 
      data = JSON.parse(message); 
   } catch (e) { 
      console.log("Invalid JSON"); 
      data = {}; 
   } 
	
   //switching type of the user message 
   switch (data.type) { 
      //when a user tries to login 
      case "login": 
         console.log("User logged:", data.name); 
			
         //if anyone is logged in with this username then refuse 
         if(users[data.name]) { 
            sendTo(connection, { 
               type: "login",
               success: false 
            }); 
         } else { 
            //save user connection on the server 
            users[data.name] = connection; 
            connection.name = data.name; 
				
            sendTo(connection, { 
               type: "login", 
               success: true 
            }); 
         } 
			
         break;
			
      default: 
         sendTo(connection, { 
            type: "error", 
            message: "Command no found: " + data.type 
         }); 
			
         break; 
   }  
});

Kullanıcı, giriş türü ile bir mesaj gönderirse , biz -

  • Bu kullanıcı adıyla zaten giriş yapan biri olup olmadığını kontrol edin.
  • Öyleyse, kullanıcıya başarıyla oturum açmadığını söyleyin.
  • Bu kullanıcı adını kimse kullanmıyorsa, kullanıcı adını bağlantı nesnesine anahtar olarak ekleriz.
  • Bir komut tanınmazsa, bir hata göndeririz.

Aşağıdaki kod, bir bağlantıya mesaj göndermek için yardımcı bir işlevdir. Bunu server.js dosyasına ekleyin -

function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}

Kullanıcı bağlantısı kesildiğinde bağlantısını temizlemeliyiz. Kapanış olayı tetiklendiğinde kullanıcıyı silebiliriz . Aşağıdaki kodu bağlantı işleyicisine ekleyin−

connection.on("close", function() { 
   if(connection.name) { 
      delete users[connection.name]; 
   } 
});

Başarılı bir şekilde oturum açtıktan sonra, kullanıcı başka birini aramak ister. Bunu başarmak için başka bir kullanıcıya teklif vermelidir . Teklif işleyiciyi ekleyin -

case "offer": 
   //for ex. UserA wants to call UserB 
   console.log("Sending offer to: ", data.name); 
	
   //if UserB exists then send him offer details 
   var conn = users[data.name]; 
	
   if(conn != null) { 
      //setting that UserA connected with UserB 
      connection.otherName = data.name; 
      sendTo(conn, { 
         type: "offer", 
         offer: data.offer, 
         name: connection.name 
      });
   }		
	
   break;

Öncelikle aramaya çalıştığımız kullanıcının bağlantısını alıyoruz. Varsa ona teklif ayrıntılarını göndeririz . Biz de eklemek otherName için bağlantı nesnesi. Bu, daha sonra bulmanın basitliği için yapılmıştır.

Yanıtı yanıtlamak, teklif işleyicide kullandığımız benzer bir modele sahiptir . Sunucumuz tüm mesajları başka bir kullanıcıya cevap olarak geçirir . Teklif işleyiciden sonra aşağıdaki kodu ekleyin -

case "answer": 
   console.log("Sending answer to: ", data.name); 
   //for ex. UserB answers UserA
   var conn = users[data.name]; 
	
   if(conn != null) { 
      connection.otherName = data.name;
		
      sendTo(conn, { 
         type: "answer", 
         answer: data.answer 
      }); 
   } 
	
   break;

Son kısım, kullanıcılar arasında ICE adayını ele almaktır. Kullanıcılar arasında mesaj gönderirken aynı tekniği kullanıyoruz. Temel fark, aday mesajlarının herhangi bir sırada kullanıcı başına birden çok kez olabilmesidir. Aday işleyiciyi ekleyin -

case "candidate": 
   console.log("Sending candidate to:",data.name); 
   var conn = users[data.name];
	
   if(conn != null) { 
      sendTo(conn, { 
         type: "candidate", 
         candidate: data.candidate 
      }); 
   } 
	
   break;

Kullanıcılarımızın başka bir kullanıcıyla bağlantısını kesmelerine izin vermek için, kapatma işlevini uygulamalıyız. Ayrıca sunucuya tüm kullanıcı referanslarını silmesini söyleyecektir. İzin işleyicisini ekleyin -

case "leave": 
   console.log("Disconnecting from", data.name); 
   var conn = users[data.name]; 
   conn.otherName = null; 
	
   //notify the other user so he can disconnect his peer connection 
   if(conn != null) { 
      sendTo(conn, {
         type: "leave" 
      }); 
   }  
	
   break;

Bu aynı zamanda diğer kullanıcıya ayrılma olayını da gönderecek, böylece eş bağlantısını uygun şekilde kesebilecektir. Ayrıca, bir kullanıcının sinyal verme sunucusundan bağlantısını kestiği durumu da ele almalıyız. Yakın işleyicimizi değiştirelim -

connection.on("close", function() { 

   if(connection.name) { 
      delete users[connection.name]; 
		
      if(connection.otherName) { 
         console.log("Disconnecting from ", connection.otherName); 
         var conn = users[connection.otherName]; 
         conn.otherName = null;
			
         if(conn != null) { 
            sendTo(conn, { 
               type: "leave" 
            }); 
         }
			
      } 
   } 
});

Aşağıdaki, sinyal sunucumuzun kodunun tamamıdır -

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users 
var users = {};

//when a user connects to our sever 
wss.on('connection', function(connection) {
  
   console.log("User connected");
	
   //when server gets a message from a connected user 
   connection.on('message', function(message) { 
	
      var data;
		
      //accepting only JSON messages 
      try { 
         data = JSON.parse(message); 
      } catch (e) { 
         console.log("Invalid JSON"); 
         data = {}; 
      }
		
      //switching type of the user message 
      switch (data.type) { 
         //when a user tries to login 
         case "login": 
            console.log("User logged", data.name); 
				
            //if anyone is logged in with this username then refuse 
            if(users[data.name]) { 
               sendTo(connection, { 
                  type: "login", 
                  success: false 
               }); 
            } else { 
               //save user connection on the server 
               users[data.name] = connection; 
               connection.name = data.name;
               sendTo(connection, { 
                  type: "login", 
                  success: true 
               }); 
            } 
				
            break;
				
         case "offer": 
            //for ex. UserA wants to call UserB 
            console.log("Sending offer to: ", data.name); 
				
            //if UserB exists then send him offer details 
            var conn = users[data.name]; 
				
            if(conn != null) { 
               //setting that UserA connected with UserB 
               connection.otherName = data.name; 
               sendTo(conn, { 
                  type: "offer", 
                  offer: data.offer, 
                  name: connection.name 
               }); 
            } 
				
            break;
				
         case "answer": 
            console.log("Sending answer to: ", data.name); 
            //for ex. UserB answers UserA 
            var conn = users[data.name]; 
				
            if(conn != null) { 
               connection.otherName = data.name; 
               sendTo(conn, { 
                  type: "answer", 
                  answer: data.answer 
               });
            } 
				
            break;
				
         case "candidate": 
            console.log("Sending candidate to:",data.name); 
            var conn = users[data.name];  
				
            if(conn != null) { 
               sendTo(conn, { 
                  type: "candidate", 
                  candidate: data.candidate 
               }); 
            } 
				
            break;
				
         case "leave": 
            console.log("Disconnecting from", data.name); 
            var conn = users[data.name]; 
            conn.otherName = null; 
				
            //notify the other user so he can disconnect his peer connection 
            if(conn != null) { 
               sendTo(conn, { 
                  type: "leave" 
               }); 
            }  
				
            break;
				
         default: 
            sendTo(connection, { 
               type: "error", 
               message: "Command not found: " + data.type 
            });
				
            break; 
      }  
   });
	
   //when user exits, for example closes a browser window 
   //this may help if we are still in "offer","answer" or "candidate" state 
   connection.on("close", function() { 
	
      if(connection.name) { 
         delete users[connection.name]; 
			
         if(connection.otherName) { 
            console.log("Disconnecting from ", connection.otherName); 
            var conn = users[connection.otherName]; 
            conn.otherName = null;  
				
            if(conn != null) { 
               sendTo(conn, { 
                  type: "leave" 
              }); 
            }  
         } 
      } 
   });  
	
   connection.send("Hello world"); 
}); 
 
function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}

İstemci Uygulaması

Bu uygulamayı test etmenin bir yolu, iki tarayıcı sekmesi açmak ve birbirlerine sesli arama yapmaya çalışmaktır.

Öncelikle bootstrap kütüphanesini kurmamız gerekiyor . Bootstrap, web uygulamaları geliştirmek için bir ön uç çerçevesidir. Daha fazla bilgi edinebilirsinizhttp://getbootstrap.com/.Örneğin "sesli sohbet" adlı bir klasör oluşturun. Bu bizim kök uygulama klasörümüz olacak. Bu klasörün içinde package.json adlı bir dosya oluşturun (npm bağımlılıklarını yönetmek için gereklidir) ve aşağıdakileri ekleyin -

{ 
   "name": "webrtc-audiochat", 
   "version": "0.1.0", 
   "description": "webrtc-audiochat", 
   "author": "Author", 
   "license": "BSD-2-Clause" 
}

Ardından npm install bootstrap komutunu çalıştırın . Bu, bootstrap kitaplığını audiochat / node_modules klasörüne kuracaktır .

Şimdi temel bir HTML sayfası oluşturmamız gerekiyor. Aşağıdaki kodla kök klasörde bir index.html dosyası oluşturun -

<html>
 
   <head> 
      <title>WebRTC Voice Demo</title> 
      <link rel = "stylesheet" href = "node_modules/bootstrap/dist/css/bootstrap.min.css"/> 
   </head>
 
   <style> 
      body { 
         background: #eee; 
         padding: 5% 0; 
      } 
   </style>
	
   <body> 
      <div id = "loginPage" class = "container text-center"> 
		
         <div class = "row"> 
            <div class = "col-md-4 col-md-offset-4">
				
               <h2>WebRTC Voice Demo. Please sign in</h2>
				
               <label for = "usernameInput" class = "sr-only">Login</label> 
               <input type = "email" id = "usernameInput" 
                  class = "form-control formgroup"
                  placeholder = "Login" required = "" autofocus = ""> 
               <button id = "loginBtn" class = "btn btn-lg btn-primary btnblock">
                  Sign in</button> 
            </div> 
         </div> 
			
      </div>
		
      <div id = "callPage" class = "call-page">
		
         <div class = "row"> 
			
            <div class = "col-md-6 text-right"> 
               Local audio: <audio id = "localAudio" 
               controls autoplay></audio> 
            </div>
				
            <div class = "col-md-6 text-left"> 
               Remote audio: <audio id = "remoteAudio" 
                  controls autoplay></audio> 
            </div> 
				
         </div> 
			
         <div class = "row text-center"> 
            <div class = "col-md-12"> 
               <input id = "callToUsernameInput" 
                  type = "text" placeholder = "username to call" /> 
               <button id = "callBtn" class = "btn-success btn">Call</button> 
               <button id = "hangUpBtn" class = "btn-danger btn">Hang Up</button> 
            </div> 
         </div>
			
      </div> 
		
      <script src = "client.js"></script> 
		
   </body>
	
</html>

Bu sayfa size tanıdık gelmelidir. Bootstrap css dosyasını ekledik . Ayrıca iki sayfa tanımladık. Son olarak, kullanıcıdan bilgi almak için birkaç metin alanı ve düğme oluşturduk. Yerel ve uzak ses akışları için iki ses öğesini görmelisiniz. Client.js dosyasına bir bağlantı eklediğimize dikkat edin .

Şimdi sinyal sunucumuzla bir bağlantı kurmamız gerekiyor. Aşağıdaki kodla kök klasörde client.js dosyasını oluşturun -

//our username 
var name; 
var connectedUser;
  
//connecting to our signaling server 
var conn = new WebSocket('ws://localhost:9090');
  
conn.onopen = function () { 
   console.log("Connected to the signaling server"); 
}; 
 
//when we got a message from a signaling server 
conn.onmessage = function (msg) { 
   console.log("Got message", msg.data);  
   var data = JSON.parse(msg.data);  
	
   switch(data.type) { 
      case "login": 
         handleLogin(data.success); 
         break; 
      //when somebody wants to call us 
      case "offer": 
         handleOffer(data.offer, data.name); 
         break; 
      case "answer": 
         handleAnswer(data.answer); 
         break; 
      //when a remote peer sends an ice candidate to us 
      case "candidate": 
         handleCandidate(data.candidate); 
         break;
      case "leave": 
         handleLeave(); 
         break; 
      default: 
         break; 
   } 
};
  
conn.onerror = function (err) { 
   console.log("Got error", err); 
};
  
//alias for sending JSON encoded messages 
function send(message) { 
   //attach the other peer username to our messages
   if (connectedUser) { 
      message.name = connectedUser; 
   } 
	
   conn.send(JSON.stringify(message)); 
};

Şimdi sinyal sunucumuzu düğüm sunucusu aracılığıyla çalıştırın . Ardından, kök klasörün içinde statik komutu çalıştırın ve sayfayı tarayıcının içinde açın. Aşağıdaki konsol çıktısını görmelisiniz -

Bir sonraki adım, benzersiz bir kullanıcı adıyla bir kullanıcı girişi uygulamaktır. Sadece sunucuya bir kullanıcı adı göndeririz, bu da bize onun alınıp alınmadığını söyler. Aşağıdaki kodu client.js dosyanıza ekleyin -

//****** 
//UI selectors block 
//******

var loginPage = document.querySelector('#loginPage'); 
var usernameInput = document.querySelector('#usernameInput'); 
var loginBtn = document.querySelector('#loginBtn');
 
var callPage = document.querySelector('#callPage'); 
var callToUsernameInput = document.querySelector('#callToUsernameInput');
var callBtn = document.querySelector('#callBtn');
 
var hangUpBtn = document.querySelector('#hangUpBtn');
  
callPage.style.display = "none";
  
// Login when the user clicks the button 
loginBtn.addEventListener("click", function (event) { 
   name = usernameInput.value;
	
   if (name.length > 0) { 
      send({
         type: "login", 
         name: name 
      }); 
   } 
	
}); 
 
function handleLogin(success) { 
   if (success === false) { 
      alert("Ooops...try a different username"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block"; 
		
      //********************** 
      //Starting a peer connection 
      //**********************
		         
   } 
	
};

İlk olarak, sayfadaki elemanlara bazı referanslar seçiyoruz. Çağrı sayfasını gizleriz. Ardından, giriş butonuna bir olay dinleyicisi ekliyoruz. Kullanıcı tıkladığında, kullanıcı adını sunucuya göndeririz. Son olarak, handleLogin geri aramasını gerçekleştiriyoruz. Giriş başarılı olduysa, arama sayfasını gösterir ve bir eş bağlantı kurmaya başlarız.

Akran bağlantısı başlatmak için ihtiyacımız olan -

  • Bir mikrofondan ses akışı elde edin
  • RTCPeerConnection nesnesini oluşturun

Aşağıdaki kodu "UI seçici bloğuna" ekleyin -

var localAudio = document.querySelector('#localAudio'); 
var remoteAudio = document.querySelector('#remoteAudio'); 

var yourConn; 
var stream;

HandleLogin işlevini değiştirin -

function handleLogin(success) { 
   if (success === false) { 
      alert("Ooops...try a different username"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block";
		
      //********************** 
      //Starting a peer connection 
      //********************** 
		
      //getting local audio stream 
      navigator.webkitGetUserMedia({ video: false, audio: true }, function (myStream) { 
         stream = myStream; 
			
         //displaying local audio stream on the page
         localAudio.src = window.URL.createObjectURL(stream);
			
         //using Google public stun server 
         var configuration = { 
            "iceServers": [{ "url": "stun:stun2.1.google.com:19302" }] 
         }; 
			
         yourConn = new webkitRTCPeerConnection(configuration); 
			
         // setup stream listening 
         yourConn.addStream(stream); 
			
         //when a remote user adds stream to the peer connection, we display it 
         yourConn.onaddstream = function (e) { 
            remoteAudio.src = window.URL.createObjectURL(e.stream); 
         }; 
			
         // Setup ice handling 
         yourConn.onicecandidate = function (event) { 
            if (event.candidate) { 
               send({ 
                  type: "candidate", 
               }); 
            } 
         };  
			
      }, function (error) { 
         console.log(error); 
      }); 
		
   } 
};

Şimdi kodu çalıştırırsanız, sayfa oturum açmanıza ve sayfada yerel ses akışınızı görüntülemenize izin vermelidir.

Şimdi bir arama başlatmaya hazırız. Öncelikle başka bir kullanıcıya teklif gönderiyoruz . Bir kullanıcı teklifi aldıktan sonra bir cevap oluşturur ve ICE adayları ile ticaret yapmaya başlar. Aşağıdaki kodu client.js dosyasına ekleyin -

//initiating a call 
callBtn.addEventListener("click", function () { 
   var callToUsername = callToUsernameInput.value; 
	
   if (callToUsername.length > 0) { 
      connectedUser = callToUsername; 
		
      // create an offer 
      yourConn.createOffer(function (offer) { 
         send({ 
            type: "offer", 
            offer: offer 
         }); 
			
         yourConn.setLocalDescription(offer); 
			
      }, function (error) { 
         alert("Error when creating an offer"); 
      }); 
   } 
	
});
 
//when somebody sends us an offer 
function handleOffer(offer, name) { 
   connectedUser = name; 
   yourConn.setRemoteDescription(new RTCSessionDescription(offer)); 
	
   //create an answer to an offer 
   yourConn.createAnswer(function (answer) { 
      yourConn.setLocalDescription(answer); 
		
      send({ 
         type: "answer",
         answer: answer 
      }); 
		
   }, function (error) { 
      alert("Error when creating an answer"); 
   }); 
	
};
 
//when we got an answer from a remote user 
function handleAnswer(answer) { 
   yourConn.setRemoteDescription(new RTCSessionDescription(answer)); 
};
 
//when we got an ice candidate from a remote user 
function handleCandidate(candidate) { 
   yourConn.addIceCandidate(new RTCIceCandidate(candidate)); 
};

Çağrı düğmesine bir teklif başlatan bir tıklama işleyici ekliyoruz . Ardından onmessage işleyicisinin beklediği birkaç işleyiciyi uygularız . Her iki kullanıcı da bir bağlantı kurana kadar eşzamansız olarak işlenecekler.

Son adım, kapatma özelliğini uygulamaktır. Bu, veri aktarımını durduracak ve diğer kullanıcıya aramayı kapatmasını söyleyecektir. Aşağıdaki kodu ekleyin -

//hang up 
hangUpBtn.addEventListener("click", function () { 
   send({ 
      type: "leave" 
   });  
	
   handleLeave(); 
});
  
function handleLeave() { 
   connectedUser = null; 
   remoteAudio.src = null;
	
   yourConn.close(); 
   yourConn.onicecandidate = null; 
   yourConn.onaddstream = null;
};

Kullanıcı Kapat düğmesini tıkladığında -

  • Diğer kullanıcıya bir "ayrılma" mesajı gönderecektir
  • RTCPeerConnection'ı kapatacak ve bağlantıyı yerel olarak yok edecek

Şimdi kodu çalıştırın. İki tarayıcı sekmesi kullanarak sunucuda oturum açabilmelisiniz. Daha sonra sekmeye sesli arama yapabilir ve aramayı kapatabilirsiniz.

Aşağıdaki tüm client.js dosyasıdır -

//our username 
var name; 
var connectedUser;
 
//connecting to our signaling server 
var conn = new WebSocket('ws://localhost:9090');
 
conn.onopen = function () { 
   console.log("Connected to the signaling server"); 
};
 
//when we got a message from a signaling server 
conn.onmessage = function (msg) { 
   console.log("Got message", msg.data); 
   var data = JSON.parse(msg.data); 
	
   switch(data.type) { 
      case "login": 
         handleLogin(data.success); 
         break; 
      //when somebody wants to call us 
      case "offer": 
         handleOffer(data.offer, data.name); 
         break; 
      case "answer": 
         handleAnswer(data.answer); 
         break; 
      //when a remote peer sends an ice candidate to us 
      case "candidate": 
         handleCandidate(data.candidate); 
         break; 
      case "leave": 
         handleLeave(); 
         break; 
      default: 
         break; 
   } 
}; 

conn.onerror = function (err) { 
   console.log("Got error", err); 
};
 
//alias for sending JSON encoded messages 
function send(message) { 
   //attach the other peer username to our messages 
   if (connectedUser) { 
      message.name = connectedUser; 
   } 
	
   conn.send(JSON.stringify(message)); 
};
 
//****** 
//UI selectors block 
//****** 

var loginPage = document.querySelector('#loginPage'); 
var usernameInput = document.querySelector('#usernameInput'); 
var loginBtn = document.querySelector('#loginBtn');

var callPage = document.querySelector('#callPage'); 
var callToUsernameInput = document.querySelector('#callToUsernameInput');
var callBtn = document.querySelector('#callBtn'); 

var hangUpBtn = document.querySelector('#hangUpBtn'); 
var localAudio = document.querySelector('#localAudio'); 
var remoteAudio = document.querySelector('#remoteAudio'); 

var yourConn; 
var stream; 

callPage.style.display = "none";
 
// Login when the user clicks the button 
loginBtn.addEventListener("click", function (event) { 
   name = usernameInput.value; 
	
   if (name.length > 0) { 
      send({ 
         type: "login", 
         name: name 
      }); 
   } 
	
});
 
function handleLogin(success) { 
   if (success === false) { 
      alert("Ooops...try a different username"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block"; 
		
      //********************** 
      //Starting a peer connection 
      //********************** 
		
      //getting local audio stream 
      navigator.webkitGetUserMedia({ video: false, audio: true }, function (myStream) { 
         stream = myStream; 
			
         //displaying local audio stream on the page 
         localAudio.src = window.URL.createObjectURL(stream);
			
         //using Google public stun server 
         var configuration = { 
            "iceServers": [{ "url": "stun:stun2.1.google.com:19302" }] 
         }; 
			
         yourConn = new webkitRTCPeerConnection(configuration); 
			
         // setup stream listening 
         yourConn.addStream(stream); 
			
         //when a remote user adds stream to the peer connection, we display it 
         yourConn.onaddstream = function (e) { 
            remoteAudio.src = window.URL.createObjectURL(e.stream); 
         }; 
			
         // Setup ice handling 
         yourConn.onicecandidate = function (event) { 
            if (event.candidate) { 
               send({ 
                  type: "candidate", 
                  candidate: event.candidate 
               }); 
            } 
         }; 
			
      }, function (error) { 
         console.log(error); 
      }); 
		
   } 
};
 
//initiating a call 
callBtn.addEventListener("click", function () { 
   var callToUsername = callToUsernameInput.value; 
	
   if (callToUsername.length > 0) { 
      connectedUser = callToUsername; 
		
      // create an offer 
      yourConn.createOffer(function (offer) { 
         send({
            type: "offer", 
            offer: offer 
         }); 
			
         yourConn.setLocalDescription(offer); 
      }, function (error) { 
         alert("Error when creating an offer"); 
      }); 
   } 
});
 
//when somebody sends us an offer 
function handleOffer(offer, name) { 
   connectedUser = name; 
   yourConn.setRemoteDescription(new RTCSessionDescription(offer)); 
	
   //create an answer to an offer 
   yourConn.createAnswer(function (answer) { 
      yourConn.setLocalDescription(answer); 
		
      send({ 
         type: "answer", 
         answer: answer 
      });
		
   }, function (error) { 
      alert("Error when creating an answer"); 
   }); 
	
};
 
//when we got an answer from a remote user 
function handleAnswer(answer) { 
   yourConn.setRemoteDescription(new RTCSessionDescription(answer)); 
};
 
//when we got an ice candidate from a remote user 
function handleCandidate(candidate) { 
   yourConn.addIceCandidate(new RTCIceCandidate(candidate)); 
};
 
//hang up
hangUpBtn.addEventListener("click", function () { 
   send({ 
      type: "leave" 
   }); 
	
   handleLeave(); 
});
 
function handleLeave() { 
   connectedUser = null; 
   remoteAudio.src = null; 
	
   yourConn.close(); 
   yourConn.onicecandidate = null; 
   yourConn.onaddstream = null; 
};

Bu bölümde, ayrı cihazlardaki iki kullanıcının WebRTC kullanarak birbirlerine mesaj göndermesine izin veren bir istemci uygulaması oluşturacağız. Uygulamamız iki sayfadan oluşacaktır. Biri oturum açmak ve diğeri başka bir kullanıcıya mesaj göndermek için.

İki sayfa div etiketleri olacaktır. Girişlerin çoğu basit olay işleyicileri aracılığıyla yapılır.

Sinyal Sunucusu

Bir WebRTC bağlantısı oluşturmak için, istemcilerin bir WebRTC eş bağlantısı kullanmadan mesajları aktarabilmesi gerekir. Burada, iki uç nokta arasında çift yönlü bir soket bağlantısı olan HTML5 WebSockets'i kullanacağımız yer - bir web sunucusu ve bir web tarayıcısı. Şimdi WebSocket kitaplığını kullanmaya başlayalım. Server.js dosyasını oluşturun ve aşağıdaki kodu ekleyin -

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090});
  
//when a user connects to our sever 
wss.on('connection', function(connection) { 
   console.log("user connected"); 
	
   //when server gets a message from a connected user 
   connection.on('message', function(message) { 
      console.log("Got message from a user:", message); 
   }); 
	
   connection.send("Hello from server"); 
});

İlk satır, önceden kurmuş olduğumuz WebSocket kitaplığını gerektirir. Daha sonra 9090 portunda bir soket sunucusu oluşturuyoruz. Daha sonra bağlantı olayını dinliyoruz . Bu kod, bir kullanıcı sunucuya bir WebSocket bağlantısı yaptığında çalıştırılacaktır. Daha sonra kullanıcı tarafından gönderilen mesajları dinliyoruz. Son olarak bağlı kullanıcıya “Sunucudan Merhaba” diyen bir yanıt gönderiyoruz.

Sinyal verme sunucumuzda, her bağlantı için dizeye dayalı bir kullanıcı adı kullanacağız, böylece mesajları nereye göndereceğimizi bileceğiz. En bizim değiştirelim bağlantı işleyicisi biraz -

connection.on('message', function(message) { 
   var data; 
	
   //accepting only JSON messages 
   try { 
      data = JSON.parse(message); 
   } catch (e) { 
      console.log("Invalid JSON");
      data = {}; 
   } 
});

Bu şekilde sadece JSON mesajlarını kabul ediyoruz. Sonra, tüm bağlı kullanıcıları bir yerde saklamamız gerekiyor. Bunun için basit bir Javascript nesnesi kullanacağız. Dosyamızın üstünü değiştirin -

//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users 
var users = {};

Müşteriden gelen her mesaj için bir tip alanı ekleyeceğiz . Örneğin, bir kullanıcı giriş yapmak isterse, giriş türü mesajını gönderir . Tanımlayalım -

connection.on('message', function(message) { 
   var data; 
	
   //accepting only JSON messages 
   try { 
      data = JSON.parse(message); 
   } catch (e) { 
      console.log("Invalid JSON"); 
      data = {}; 
   }
	
   //switching type of the user message 
   switch (data.type) { 
      //when a user tries to login 
      case "login": 
         console.log("User logged:", data.name); 
			
         //if anyone is logged in with this username then refuse 
         if(users[data.name]) { 
            sendTo(connection, { 
               type: "login", 
               success: false
            }); 
         } else { 
            //save user connection on the server 
            users[data.name] = connection; 
            connection.name = data.name; 
				
            sendTo(connection, { 
               type: "login", 
               success: true 
            }); 
         } 
			
         break;
			
      default: 
         sendTo(connection, { 
            type: "error", 
            message: "Command no found: " + data.type 
         }); 
			
         break;
   } 
});

Kullanıcı, giriş türü ile bir mesaj gönderirse , biz -

  • Bu kullanıcı adıyla zaten giriş yapan biri olup olmadığını kontrol edin.
  • Öyleyse, kullanıcıya başarıyla oturum açmadığını söyleyin.
  • Bu kullanıcı adını kimse kullanmıyorsa, kullanıcı adını bağlantı nesnesine anahtar olarak ekleriz.
  • Bir komut tanınmazsa, bir hata göndeririz.

Aşağıdaki kod, bir bağlantıya mesaj göndermek için yardımcı bir işlevdir. Bunu server.js dosyasına ekleyin -

function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}

Kullanıcı bağlantısı kesildiğinde bağlantısını temizlemeliyiz. Kapanış olayı tetiklendiğinde kullanıcıyı silebiliriz . Aşağıdaki kodu bağlantı işleyicisine ekleyin -

connection.on("close", function() { 
   if(connection.name) { 
      delete users[connection.name]; 
   } 
});

Başarılı bir şekilde oturum açtıktan sonra, kullanıcı başka birini aramak ister. Bunu başarmak için başka bir kullanıcıya teklif vermelidir . Teklif işleyiciyi ekleyin -

case "offer": 
   //for ex. UserA wants to call UserB 
   console.log("Sending offer to: ", data.name); 
	
   //if UserB exists then send him offer details 
   var conn = users[data.name]; 
	
   if(conn != null){ 
   //setting that UserA connected with UserB 
   connection.otherName = data.name; 
	
      sendTo(conn, { 
         type: "offer", 
         offer: data.offer, 
         name: connection.name 
      }); 
     
   break;

Öncelikle aramaya çalıştığımız kullanıcının bağlantısını alıyoruz. Varsa ona teklif ayrıntılarını göndeririz . Biz de eklemek otherName için bağlantı nesnesi. Bu, daha sonra bulmanın basitliği için yapılmıştır.

Yanıtı yanıtlamak, teklif işleyicide kullandığımız benzer bir modele sahiptir . Sunucumuz tüm mesajları başka bir kullanıcıya cevap olarak geçirir . Teklif işleyiciden sonra aşağıdaki kodu ekleyin -

case "answer": 
   console.log("Sending answer to: ", data.name); 
	
   //for ex. UserB answers UserA 
   var conn = users[data.name]; 
	
   if(conn != null) { 
      connection.otherName = data.name;
      sendTo(conn, { 
         type: "answer", 
         answer: data.answer 
      }); 
   } 
	
   break;

Son kısım, kullanıcılar arasında ICE adayını ele almaktır. Kullanıcılar arasında mesaj gönderirken aynı tekniği kullanıyoruz. Temel fark, aday mesajlarının herhangi bir sırada kullanıcı başına birden çok kez olabilmesidir. Aday işleyiciyi ekleyin -

case "candidate": 
   console.log("Sending candidate to:",data.name); 
   var conn = users[data.name];
	
   if(conn != null) { 
      sendTo(conn, { 
         type: "candidate", 
         candidate: data.candidate 
      }); 
   } 
	
   break;

Kullanıcılarımızın başka bir kullanıcıyla bağlantısını kesmelerine izin vermek için, kapatma işlevini uygulamalıyız. Ayrıca sunucuya tüm kullanıcı referanslarını silmesini söyleyecektir. İzin işleyicisini ekleyin -

case "leave": 
   console.log("Disconnecting from", data.name); 
   var conn = users[data.name]; 
   conn.otherName = null;
	
   //notify the other user so he can disconnect his peer connection 
   if(conn != null) { 
      sendTo(conn, { 
         type: "leave" 
      }); 
   }
	
   break;

Bu aynı zamanda diğer kullanıcıya ayrılma olayını da gönderecek, böylece eş bağlantısını uygun şekilde kesebilecektir. Ayrıca, bir kullanıcının sinyal verme sunucusundan bağlantısını kestiği durumu da ele almalıyız. Yakın işleyicimizi değiştirelim -

connection.on("close", function() { 

   if(connection.name) { 
      delete users[connection.name]; 
		
      if(connection.otherName) { 
         console.log("Disconnecting from ", connection.otherName); 
         var conn = users[connection.otherName]; 
         conn.otherName = null;  
			
         if(conn != null) { 
            sendTo(conn, { 
               type: "leave" 
            }); 
         }  
      } 
   } 
});

Aşağıdaki, sinyal sunucumuzun kodunun tamamıdır -

//require our websocket library 
var WebSocketServer = require('ws').Server;
 
//creating a websocket server at port 9090 
var wss = new WebSocketServer({port: 9090}); 

//all connected to the server users 
var users = {};
  
//when a user connects to our sever 
wss.on('connection', function(connection) {
  
   console.log("User connected");
	 
   //when server gets a message from a connected user 
   connection.on('message', function(message) {
	 
      var data; 
      //accepting only JSON messages 
      try { 
         data = JSON.parse(message); 
      } catch (e) { 
         console.log("Invalid JSON"); 
         data = {}; 
      }
		  
      //switching type of the user message 
      switch (data.type) { 
         //when a user tries to login 
         case "login": 
            console.log("User logged", data.name); 
            //if anyone is logged in with this username then refuse 
            if(users[data.name]) { 
               sendTo(connection, { 
                  type: "login", 
                  success: false 
               }); 
            } else { 
               //save user connection on the server 
               users[data.name] = connection; 
               connection.name = data.name; 
					
                  sendTo(connection, { 
                     type: "login", 
                     success: true 
                  }); 
            }
				
            break;
				
         case "offer": 
            //for ex. UserA wants to call UserB 
            console.log("Sending offer to: ", data.name); 
				
            //if UserB exists then send him offer details 
            var conn = users[data.name]; 
				
            if(conn != null) { 
               //setting that UserA connected with UserB 
               connection.otherName = data.name; 
					
               sendTo(conn, { 
                  type: "offer", 
                  offer: data.offer, 
                  name: connection.name 
               }); 
            } 
				
            break;
				
         case "answer": 
            console.log("Sending answer to: ", data.name); 
            //for ex. UserB answers UserA 
            var conn = users[data.name]; 
				
            if(conn != null) { 
               connection.otherName = data.name; 
               sendTo(conn, { 
                  type: "answer", 
                  answer: data.answer 
               }); 
            } 
				
            break;
				
         case "candidate": 
            console.log("Sending candidate to:",data.name);
            var conn = users[data.name];  
				
            if(conn != null) { 
               sendTo(conn, { 
                  type: "candidate", 
                  candidate: data.candidate 
               }); 
            } 
				
            break;
				
         case "leave": 
            console.log("Disconnecting from", data.name); 
            var conn = users[data.name]; 
            conn.otherName = null; 
				
            //notify the other user so he can disconnect his peer connection 
            if(conn != null) { 
               sendTo(conn, { 
                  type: "leave"
               });
            }  
				
            break;
				
         default: 
            sendTo(connection, { 
               type: "error", 
               message: "Command not found: " + data.type 
            }); 
				
            break;
				
      }  
   });
	
   //when user exits, for example closes a browser window 
   //this may help if we are still in "offer","answer" or "candidate" state 
   connection.on("close", function() { 
	
      if(connection.name) { 
         delete users[connection.name]; 
			
         if(connection.otherName) { 
            console.log("Disconnecting from ", connection.otherName); 
            var conn = users[connection.otherName]; 
            conn.otherName = null;
				
            if(conn != null) { 
               sendTo(conn, { 
                  type: "leave" 
               }); 
            }  
         } 
      } 
   });
	
   connection.send("Hello world");
	
});
  
function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}

İstemci Uygulaması

Bu uygulamayı test etmenin bir yolu, iki tarayıcı sekmesi açıp birbirlerine mesaj göndermeye çalışmaktır.

Öncelikle bootstrap kütüphanesini kurmamız gerekiyor . Bootstrap, web uygulamaları geliştirmek için bir ön uç çerçevesidir. Daha fazla bilgi edinebilirsinizhttp://getbootstrap.com/.Örneğin "textchat" adlı bir klasör oluşturun. Bu bizim kök uygulama klasörümüz olacak. Bu klasörün içinde package.json adlı bir dosya oluşturun (npm bağımlılıklarını yönetmek için gereklidir) ve aşağıdakileri ekleyin -

{ 
   "name": "webrtc-textochat", 
   "version": "0.1.0", 
   "description": "webrtc-textchat", 
   "author": "Author", 
   "license": "BSD-2-Clause" 
}

Ardından npm install bootstrap komutunu çalıştırın . Bu, bootstrap kitaplığını textchat / node_modules klasörüne yükleyecektir .

Şimdi temel bir HTML sayfası oluşturmamız gerekiyor. Aşağıdaki kodla kök klasörde bir index.html dosyası oluşturun -

<html> 

   <head> 
      <title>WebRTC Text Demo</title> 
      <link rel = "stylesheet" href = "node_modules/bootstrap/dist/css/bootstrap.min.css"/> 
   </head>
 
   <style> 
      body { 
         background: #eee; 
         padding: 5% 0; 
      } 
   </style>
	
   <body> 
      <div id = "loginPage" class = "container text-center"> 
		
         <div class = "row"> 
            <div class = "col-md-4 col-md-offset-4"> 
               <h2>WebRTC Text Demo. Please sign in</h2> 
               <label for = "usernameInput" class = "sr-only">Login</label> 
               <input type = "email" id = "usernameInput" 
                  class = "form-control formgroup" placeholder = "Login" 
                  required = "" autofocus = "">
               <button id = "loginBtn" class = "btn btn-lg btn-primary btnblock">
                  Sign in</button> 
            </div> 
         </div> 
			
      </div>
		
      <div id = "callPage" class = "call-page container">
		
         <div class = "row"> 
            <div class = "col-md-4 col-md-offset-4 text-center"> 
               <div class = "panel panel-primary"> 
                  <div class = "panel-heading">Text chat</div> 
                  <div id = "chatarea" class = "panel-body text-left"></div> 
               </div> 
            </div> 
         </div>
			
         <div class = "row text-center form-group"> 
            <div class = "col-md-12"> 
               <input id = "callToUsernameInput" type = "text" 
                  placeholder = "username to call" /> 
               <button id = "callBtn" class = "btn-success btn">Call</button> 
               <button id = "hangUpBtn" class = "btn-danger btn">Hang Up</button> 
            </div> 
         </div> 
			
         <div class = "row text-center"> 
            <div class = "col-md-12"> 
               <input id = "msgInput" type = "text" placeholder = "message" /> 
               <button id = "sendMsgBtn" class = "btn-success btn">Send</button> 
            </div> 
         </div>
			
      </div> 
		
      <script src = "client.js"></script> 
		
   </body>
 
</html>

Bu sayfa size tanıdık gelmelidir. Bootstrap css dosyasını ekledik . Ayrıca iki sayfa tanımladık. Son olarak, kullanıcıdan bilgi almak için birkaç metin alanı ve düğme oluşturduk. "Sohbet" sayfasında, tüm mesajlarımızın görüntüleneceği "sohbet alanı" kimliğine sahip div etiketini görmelisiniz. Client.js dosyasına bir bağlantı eklediğimize dikkat edin .

Şimdi sinyal sunucumuzla bir bağlantı kurmamız gerekiyor. Aşağıdaki kodla kök klasörde client.js dosyasını oluşturun -

//our username 
var name; 
var connectedUser;
  
//connecting to our signaling server 
var conn = new WebSocket('ws://localhost:9090');
  
conn.onopen = function () { 
   console.log("Connected to the signaling server"); 
}; 
 
//when we got a message from a signaling server 
conn.onmessage = function (msg) { 
   console.log("Got message", msg.data);
	
   var data = JSON.parse(msg.data);
	
   switch(data.type) { 
      case "login": 
         handleLogin(data.success); 
         break; 
      //when somebody wants to call us 
      case "offer": 
         handleOffer(data.offer, data.name); 
         break; 
      case "answer": 
         handleAnswer(data.answer); 
         break; 
      //when a remote peer sends an ice candidate to us 
      case "candidate":
         handleCandidate(data.candidate); 
         break; 
      case "leave": 
         handleLeave(); 
         break; 
      default: 
         break; 
   } 
};
  
conn.onerror = function (err) { 
   console.log("Got error", err); 
};
  
//alias for sending JSON encoded messages 
function send(message) { 
   //attach the other peer username to our messages 
   if (connectedUser) { 
      message.name = connectedUser; 
   } 
	
   conn.send(JSON.stringify(message)); 
};

Şimdi sinyal sunucumuzu düğüm sunucusu aracılığıyla çalıştırın . Ardından, kök klasörün içinde statik komutu çalıştırın ve sayfayı tarayıcının içinde açın. Aşağıdaki konsol çıktısını görmelisiniz -

Bir sonraki adım, benzersiz bir kullanıcı adıyla bir kullanıcı girişi uygulamaktır. Sadece sunucuya bir kullanıcı adı göndeririz, bu da bize onun alınıp alınmadığını söyler. Aşağıdaki kodu client.js dosyanıza ekleyin -

//****** 
//UI selectors block
//****** 

var loginPage = document.querySelector('#loginPage'); 
var usernameInput = document.querySelector('#usernameInput'); 
var loginBtn = document.querySelector('#loginBtn'); 

var callPage = document.querySelector('#callPage'); 
var callToUsernameInput = document.querySelector('#callToUsernameInput'); 
var callBtn = document.querySelector('#callBtn'); 

var hangUpBtn = document.querySelector('#hangUpBtn'); 
callPage.style.display = "none"; 

// Login when the user clicks the button 
loginBtn.addEventListener("click", function (event) { 
   name = usernameInput.value;
	
   if (name.length > 0) { 
      send({ 
         type: "login", 
         name: name 
      }); 
   } 
	
});
 
function handleLogin(success) { 

   if (success === false) { 
      alert("Ooops...try a different username"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block"; 
		
      //********************** 
      //Starting a peer connection 
      //********************** 
   } 
	
};

İlk olarak, sayfadaki elemanlara bazı referanslar seçiyoruz. Çağrı sayfasını gizleriz. Ardından, giriş butonuna bir olay dinleyicisi ekliyoruz. Kullanıcı tıkladığında, kullanıcı adını sunucuya göndeririz. Son olarak, handleLogin geri aramasını gerçekleştiriyoruz. Giriş başarılı olduysa, arama sayfasını gösteririz, bir eş bağlantısı kurarız ve bir veri kanalı oluştururuz.

Bir veri kanalıyla eş bağlantı başlatmak için ihtiyacımız olan -

  • RTCPeerConnection nesnesini oluşturun
  • RTCPeerConnection nesnemiz içinde bir veri kanalı oluşturun

Aşağıdaki kodu "UI seçici bloğuna" ekleyin -

var msgInput = document.querySelector('#msgInput'); 
var sendMsgBtn = document.querySelector('#sendMsgBtn'); 
var chatArea = document.querySelector('#chatarea'); 

var yourConn; 
var dataChannel;

HandleLogin işlevini değiştirin -

function handleLogin(success) { 
   if (success === false) { 
      alert("Ooops...try a different username"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block";
		
      //********************** 
      //Starting a peer connection 
      //**********************
		
      //using Google public stun server 
      var configuration = { 
         "iceServers": [{ "url": "stun:stun2.1.google.com:19302" }] 
      }; 
		
      yourConn = new webkitRTCPeerConnection(configuration, {optional: [{RtpDataChannels: true}]});
		
      // Setup ice handling 
      yourConn.onicecandidate = function (event) { 
         if (event.candidate) { 
            send({
               type: "candidate", 
               candidate: event.candidate 
            }); 
         } 
      };
		
      //creating data channel 
      dataChannel = yourConn.createDataChannel("channel1", {reliable:true}); 
		
      dataChannel.onerror = function (error) { 
         console.log("Ooops...error:", error); 
      };
		
      //when we receive a message from the other peer, display it on the screen 
      dataChannel.onmessage = function (event) { 
         chatArea.innerHTML += connectedUser + ": " + event.data + "<br />"; 
      };
		
      dataChannel.onclose = function () { 
         console.log("data channel is closed"); 
      };  
   } 
};

Oturum açma başarılı olursa, uygulama RTCPeerConnection nesnesini oluşturur ve bulunan tüm icecandidates'i diğer eşe gönderen onicecandidate işleyicisini ayarlar . Ayrıca bir dataChannel oluşturur. RTCPeerConnection nesnesini oluştururken, yapıcıdaki ikinci bağımsız değişkenin isteğe bağlı olduğuna dikkat edin: [{RtpDataChannels: true}], Chrome veya Opera kullanıyorsanız zorunludur. Bir sonraki adım, diğer akran için bir teklif oluşturmaktır. Bir kullanıcı teklifi aldıktan sonra bir cevap oluşturur ve ICE adayları ile ticaret yapmaya başlar. Aşağıdaki kodu client.js dosyasına ekleyin -

//initiating a call
callBtn.addEventListener("click", function () { 
   var callToUsername = callToUsernameInput.value;
	
   if (callToUsername.length > 0) {
	
      connectedUser = callToUsername;
		
      // create an offer 
      yourConn.createOffer(function (offer) { 
		
         send({ 
            type: "offer", 
            offer: offer 
         }); 
			
         yourConn.setLocalDescription(offer); 
			
      }, function (error) { 
         alert("Error when creating an offer"); 
      });  
   } 
});
  
//when somebody sends us an offer 
function handleOffer(offer, name) { 
   connectedUser = name; 
   yourConn.setRemoteDescription(new RTCSessionDescription(offer));
	
   //create an answer to an offer 
   yourConn.createAnswer(function (answer) { 
      yourConn.setLocalDescription(answer); 
		
      send({ 
         type: "answer", 
         answer: answer 
      }); 
		
   }, function (error) { 
      alert("Error when creating an answer"); 
   });
};
  
//when we got an answer from a remote user 
function handleAnswer(answer) { 
   yourConn.setRemoteDescription(new RTCSessionDescription(answer)); 
};
  
//when we got an ice candidate from a remote user 
function handleCandidate(candidate) { 
   yourConn.addIceCandidate(new RTCIceCandidate(candidate)); 
};

Çağrı düğmesine bir teklif başlatan bir tıklama işleyici ekliyoruz . Ardından onmessage işleyicisinin beklediği birkaç işleyiciyi uygularız . Her iki kullanıcı da bir bağlantı kurana kadar eşzamansız olarak işlenecekler.

Bir sonraki adım, kapatma özelliğini uygulamaktır. Bu, veri aktarımını durduracak ve diğer kullanıcıya veri kanalını kapatmasını söyleyecektir. Aşağıdaki kodu ekleyin -

//hang up 
hangUpBtn.addEventListener("click", function () { 
   send({ 
      type: "leave" 
   }); 
	
   handleLeave(); 
}); 
 
function handleLeave() { 
   connectedUser = null; 
   yourConn.close(); 
   yourConn.onicecandidate = null; 
};

Kullanıcı Kapat düğmesini tıkladığında -

  • Diğer kullanıcıya bir "ayrıl" mesajı gönderecektir.
  • RTCPeerConnection'ı ve ayrıca veri kanalını kapatacaktır.

Son adım, başka bir akrana mesaj göndermektir. "Mesaj gönder" düğmesine "tıklama" işleyicisini ekleyin -

//when user clicks the "send message" button 
sendMsgBtn.addEventListener("click", function (event) { 
   var val = msgInput.value; 
   chatArea.innerHTML += name + ": " + val + "<br />"; 
	
   //sending a message to a connected peer 
   dataChannel.send(val); 
   msgInput.value = ""; 
});

Şimdi kodu çalıştırın. İki tarayıcı sekmesi kullanarak sunucuda oturum açabilmelisiniz. Daha sonra diğer kullanıcıyla bir eş bağlantı kurabilir ve ona bir mesaj gönderebilir, ayrıca “Kapat” düğmesine tıklayarak veri kanalını kapatabilirsiniz.

Aşağıdaki tüm client.js dosyasıdır -

//our username 
var name; 
var connectedUser; 

//connecting to our signaling server 
var conn = new WebSocket('ws://localhost:9090'); 

conn.onopen = function () { 
   console.log("Connected to the signaling server");
};
 
//when we got a message from a signaling server 
conn.onmessage = function (msg) { 
   console.log("Got message", msg.data); 
   var data = JSON.parse(msg.data); 
	
   switch(data.type) { 
      case "login": 
         handleLogin(data.success); 
         break; 
      //when somebody wants to call us 
      case "offer": 
         handleOffer(data.offer, data.name); 
         break; 
      case "answer": 
         handleAnswer(data.answer); 
         break; 
      //when a remote peer sends an ice candidate to us 
      case "candidate": 
         handleCandidate(data.candidate); 
         break; 
      case "leave": 
         handleLeave(); 
         break; 
      default: 
         break; 
   } 
}; 

conn.onerror = function (err) { 
   console.log("Got error", err); 
}; 

//alias for sending JSON encoded messages 
function send(message) { 

   //attach the other peer username to our messages
   if (connectedUser) { 
      message.name = connectedUser; 
   } 
	
   conn.send(JSON.stringify(message)); 
};
 
//****** 
//UI selectors block 
//****** 

var loginPage = document.querySelector('#loginPage'); 
var usernameInput = document.querySelector('#usernameInput'); 
var loginBtn = document.querySelector('#loginBtn'); 

var callPage = document.querySelector('#callPage'); 
var callToUsernameInput = document.querySelector('#callToUsernameInput');
var callBtn = document.querySelector('#callBtn'); 

var hangUpBtn = document.querySelector('#hangUpBtn'); 
var msgInput = document.querySelector('#msgInput'); 
var sendMsgBtn = document.querySelector('#sendMsgBtn'); 

var chatArea = document.querySelector('#chatarea'); 
var yourConn; 
var dataChannel; 
callPage.style.display = "none"; 

// Login when the user clicks the button 
loginBtn.addEventListener("click", function (event) { 
   name = usernameInput.value; 
	
   if (name.length > 0) { 
      send({ 
         type: "login", 
         name: name 
      }); 
   } 
	
});
 
function handleLogin(success) { 

   if (success === false) {
      alert("Ooops...try a different username"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block"; 
		
      //********************** 
      //Starting a peer connection 
      //********************** 
		
      //using Google public stun server 
      var configuration = { 
         "iceServers": [{ "url": "stun:stun2.1.google.com:19302" }] 
      }; 
		
      yourConn = new webkitRTCPeerConnection(configuration, {optional: [{RtpDataChannels: true}]}); 
		
      // Setup ice handling 
      yourConn.onicecandidate = function (event) { 
         if (event.candidate) { 
            send({ 
               type: "candidate", 
               candidate: event.candidate 
            }); 
         } 
      }; 
		
      //creating data channel 
      dataChannel = yourConn.createDataChannel("channel1", {reliable:true}); 
		
      dataChannel.onerror = function (error) { 
         console.log("Ooops...error:", error); 
      }; 
		
      //when we receive a message from the other peer, display it on the screen 
      dataChannel.onmessage = function (event) { 
         chatArea.innerHTML += connectedUser + ": " + event.data + "<br />"; 
      }; 
		
      dataChannel.onclose = function () { 
         console.log("data channel is closed"); 
      };
		
   } 
};
 
//initiating a call 
callBtn.addEventListener("click", function () { 
   var callToUsername = callToUsernameInput.value; 
	
   if (callToUsername.length > 0) { 
      connectedUser = callToUsername; 
      // create an offer 
      yourConn.createOffer(function (offer) { 
         send({ 
            type: "offer", 
            offer: offer 
         }); 
         yourConn.setLocalDescription(offer); 
      }, function (error) { 
         alert("Error when creating an offer"); 
      }); 
   } 
	
});
 
//when somebody sends us an offer 
function handleOffer(offer, name) { 
   connectedUser = name; 
   yourConn.setRemoteDescription(new RTCSessionDescription(offer)); 
	
   //create an answer to an offer 
   yourConn.createAnswer(function (answer) { 
      yourConn.setLocalDescription(answer); 
      send({ 
         type: "answer", 
         answer: answer 
      }); 
   }, function (error) { 
      alert("Error when creating an answer"); 
   });
	
};
 
//when we got an answer from a remote user 
function handleAnswer(answer) { 
   yourConn.setRemoteDescription(new RTCSessionDescription(answer)); 
};
 
//when we got an ice candidate from a remote user 
function handleCandidate(candidate) { 
   yourConn.addIceCandidate(new RTCIceCandidate(candidate)); 
};
 
//hang up 
hangUpBtn.addEventListener("click", function () { 
   send({ 
      type: "leave" 
   }); 
	
   handleLeave(); 
}); 

function handleLeave() { 
   connectedUser = null; 
   yourConn.close(); 
   yourConn.onicecandidate = null; 
};
 
//when user clicks the "send message" button 
sendMsgBtn.addEventListener("click", function (event) { 
   var val = msgInput.value; 
   chatArea.innerHTML += name + ": " + val + "<br />"; 
	
   //sending a message to a connected peer 
   dataChannel.send(val); 
   msgInput.value = ""; 
});

Bu bölümde, “WebRTC Signaling” bölümünde oluşturduğumuz sinyalizasyon sunucusuna güvenlik özellikleri ekleyeceğiz. İki geliştirme olacak -

  • Redis veritabanını kullanarak kullanıcı kimlik doğrulaması
  • Güvenli soket bağlantısını etkinleştirme

Öncelikle Redis'i kurmalısınız.

  • En son kararlı sürümü şu adresten indirin: http://redis.io/download(3.05 benim durumumda)

  • Paketi aç

  • İndirilen klasörün içinde sudo make install komutunu çalıştırın

  • Kurulum tamamlandıktan sonra, her şeyin doğru çalışıp çalışmadığını kontrol etmek için make test'i çalıştırın .

Redis'in iki çalıştırılabilir komutu vardır -

  • redis-cli - Redis için komut satırı arayüzü (istemci bölümü)

  • redis-server - Redis veri deposu

Redis sunucusunu terminal konsolunda çalıştırmak için redis-server yazın . Aşağıdakileri görmelisiniz -

Şimdi yeni bir terminal penceresi açın ve bir istemci uygulamasını açmak için redis-cli'yi çalıştırın .

Redis temelde bir anahtar-değer veritabanıdır. Dize değerine sahip bir anahtar oluşturmak için SET komutunu kullanmalısınız. Anahtar değerini okumak için GET komutunu kullanmalısınız. Onlar için iki kullanıcı ve şifre ekleyelim. Anahtarlar kullanıcı adları olacak ve bu anahtarların değerleri karşılık gelen şifreler olacaktır.

Şimdi bir kullanıcı kimlik doğrulaması eklemek için sinyal sunucumuzu değiştirmeliyiz. Aşağıdaki kodu server.js dosyasının en üstüne ekleyin -

//require the redis library in Node.js 
var redis = require("redis");
 
//creating the redis client object 
var redisClient = redis.createClient();

Yukarıdaki kodda, Node.js için Redis kitaplığına ve sunucumuz için bir redis istemcisi oluşturmaya ihtiyacımız var.

Kimlik doğrulamasını eklemek için bağlantı nesnesindeki mesaj işleyiciyi değiştirin -

//when a user connects to our sever 
wss.on('connection', function(connection) { 
   console.log("user connected");
	
   //when server gets a message from a connected user 
   connection.on('message', function(message) { 
	
      var data; 
      //accepting only JSON messages 
      try { 
         data = JSON.parse(message); 
      } catch (e) { 
         console.log("Invalid JSON"); 
         data = {}; 
      }
		
      //check whether a user is authenticated 
      if(data.type != "login") { 
		
         //if user is not authenticated 
         if(!connection.isAuth) { 
            sendTo(connection, { 
               type: "error", 
               message: "You are not authenticated" 
            }); 
            return; 
         } 
      } 
		
      //switching type of the user message 
      switch (data.type) { 
         //when a user tries to login 
         case "login": 
            console.log("User logged:", data.name); 
            //get password for this username from redis database 
				
            redisClient.get(data.name, function(err, reply) {  
               //check if password matches with the one stored in redis 
               var loginSuccess = reply === data.password;
				
               //if anyone is logged in with this username or incorrect password 
                  then refuse 
               if(users[data.name] || !loginSuccess) { 
                  sendTo(connection, { 
                     type: "login", 
                     success: false 
                  }); 
               } else { 
                  //save user connection on the server 
                  users[data.name] = connection; 
                  connection.name = data.name;
                  connection.isAuth = true; 
						
                  sendTo(connection, { 
                     type: "login", 
                     success: true 
                  }); 
               }  
            }); 
				
            break;
      }
   });
	
}		
		
//... 
//*****other handlers*******

Yukarıdaki kodda, bir kullanıcı oturum açmaya çalışırsa, şifresini Redis'ten alırız, saklananla eşleşip eşleşmediğini kontrol ederiz ve başarılı olursa, kullanıcı adını sunucuda saklarız. Ayrıca , kullanıcının kimliğinin doğrulanmış olup olmadığını kontrol etmek için bağlantıya isAuth bayrağını da ekliyoruz . Bu koda dikkat edin -

//check whether a user is authenticated 
if(data.type != "login") { 

   //if user is not authenticated 
   if(!connection.isAuth) { 
      sendTo(connection, { 
         type: "error", 
         message: "You are not authenticated" 
      });
		
      return; 
   } 
}

Kimliği doğrulanmamış bir kullanıcı teklif göndermeye veya bağlantıyı kesmeye çalışırsa, yalnızca bir hata göndeririz.

Sonraki adım, güvenli bir soket bağlantısı sağlamaktır. WebRTC uygulamaları için şiddetle tavsiye edilir. PKI (Genel Anahtar Altyapısı), bir CA'dan (Sertifika Yetkilisi) gelen dijital bir imzadır. Kullanıcılar daha sonra bir sertifikayı imzalamak için kullanılan özel anahtarın, CA'nın sertifikasının genel anahtarıyla eşleşip eşleşmediğini kontrol eder. Geliştirme amaçlı. kendinden imzalı bir güvenlik sertifikası kullanacağız.

Openssl'yi kullanacağız. SSL (Secure Sockets Layer) ve TLS (Transport Layer Security) protokollerini uygulayan açık kaynaklı bir araçtır. Genellikle varsayılan olarak Unix sistemlerine kurulur. Kurulu olup olmadığını kontrol etmek için openssl version -a komutunu çalıştırın .

Genel ve özel güvenlik sertifika anahtarları oluşturmak için aşağıda verilen adımları izlemelisiniz -

  • Generate a temporary server password key

openssl genrsa -des3 -passout pass:x -out server.pass.key 2048

  • Generate a server private key

openssl rsa -passin pass:12345 -in server.pass.key -out server.key

  • Generate a signing request. You will be asked additional questions about your company. Just hit the “Enter” button all the time.

openssl req -new -key server.key -out server.csr

  • Generate the certificate

openssl x509 -req -days 1095 -in server.csr -signkey server.key -out server.crt

Artık iki dosyanız var, sertifika (server.crt) ve özel anahtar (server.key). Bunları sinyalizasyon sunucusu kök klasörüne kopyalayın.

Güvenli soket bağlantısını etkinleştirmek için sinyal sunucumuzu değiştirin.

//require file system module 
var fs = require('fs'); 
var httpServ = require('https');
  
//https://github.com/visionmedia/superagent/issues/205 
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
  
//out secure server will bind to the port 9090 
var cfg = { 
   port: 9090, 
   ssl_key: 'server.key', 
   ssl_cert: 'server.crt' 
};
  
//in case of http request just send back "OK" 
var processRequest = function(req, res) { 
   res.writeHead(200); 
   res.end("OK"); 
};
  
//create our server with SSL enabled 
var app = httpServ.createServer({ 
   key: fs.readFileSync(cfg.ssl_key), 
   cert: fs.readFileSync(cfg.ssl_cert) 
}, processRequest).listen(cfg.port);
	
//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({server: app}); 

//all connected to the server users 
var users = {};
  
//require the redis library in Node.js
var redis = require("redis"); 

//creating the redis client object 
var redisClient = redis.createClient(); 
 
//when a user connects to our sever 
wss.on('connection', function(connection){ 
//...other code

Yukarıdaki kodda, fs kütüphanesinin özel anahtarı ve sertifikayı okumasını, özel anahtar ve sertifika için bağlayıcı port ve yollarla cfg nesnesini oluşturmasını istiyoruz . Ardından, 9090 portunda WebSocket sunucusu ile birlikte anahtarlarımızla bir HTTPS sunucusu oluşturuyoruz.

Şimdi aç https://localhost:9090Opera'da. Aşağıdakileri görmelisiniz -

"Yine de devam et" düğmesini tıklayın. "Tamam" mesajını görmelisiniz.

Güvenli sinyal sunucumuzu test etmek için, "WebRTC Metin Demosu" eğitiminde oluşturduğumuz sohbet uygulamasını değiştireceğiz. Sadece bir şifre alanı eklememiz gerekiyor. Aşağıda tüm index.html dosyası yer almaktadır -

<html>
  
   <head> 
      <title>WebRTC Text Demo</title>  
      <link rel = "stylesheet" href = "node_modules/bootstrap/dist/css/bootstrap.min.css"/>  
   </head> 

   <style>  
      body { 
         background: #eee; 
         padding: 5% 0; 
      }  
   </style>
	
   <body>  
      <div id = "loginPage" class = "container text-center"> 
		
         <div class = "row"> 
            <div class = "col-md-4 col-md-offset-4">  
               <h2>WebRTC Text Demo. Please sign in</h2> 
               <label for = "usernameInput" class = "sr-only">Login</label> 
               <input type = "email" id = "usernameInput" 
                  class = "form-control formgroup" placeholder = "Login" 
                  required = "" autofocus = ""> 
               <input type = "text" id = "passwordInput" 
                  class = "form-control form-group" placeholder = "Password"
                  required = "" autofocus = ""> 
               <button id = "loginBtn" class = "btn btn-lg btn-primary btnblock"
                  >Sign in</button>  
            </div> 
         </div> 
			
      </div> 
		
      <div id = "callPage" class = "call-page container">
		
         <div class = "row"> 
            <div class = "col-md-4 col-md-offset-4 text-center"> 
               <div class = "panel panel-primary"> 
                  <div class = "panel-heading">Text chat</div> 
                  <div id = "chatarea" class = "panel-body text-left"></div> 
               </div> 
            </div> 
         </div>
			
         <div class = "row text-center form-group"> 
            <div class = "col-md-12"> 
               <input id = "callToUsernameInput" type = "text" 
                  placeholder = "username to call" /> 
               <button id = "callBtn" class = "btn-success btn">Call</button> 
               <button id = "hangUpBtn" class = "btn-danger btn">Hang Up</button> 
            </div> 
         </div>
			
         <div class = "row text-center"> 
            <div class = "col-md-12"> 
               <input id = "msgInput" type = "text" placeholder = "message" /> 
               <button id = "sendMsgBtn" class = "btn-success btn">Send</button> 
            </div> 
         </div>
			
      </div>  
		
      <script src = "client.js"></script> 
  
   </body> 
	
</html>

Ayrıca bu satır aracılığıyla client.js dosyasında güvenli bir soket bağlantısını etkinleştirmemiz gerekiyor var conn = new WebSocket ('wss: // localhost: 9090'); . Wss protokolüne dikkat edin . Ardından, kullanıcı adı ile birlikte şifre göndermek için oturum açma düğmesi işleyicisi değiştirilmelidir -

loginBtn.addEventListener("click", function (event) { 
   name = usernameInput.value; 
   var pwd = passwordInput.value;
	
   if (name.length > 0) { 
      send({ 
         type: "login", 
         name: name, 
         password: pwd 
      }); 
   } 
	
});

Aşağıdaki tüm client.js dosyasıdır -

//our username 
var name; 
var connectedUser;
  
//connecting to our signaling server 
var conn = new WebSocket('wss://localhost:9090');
  
conn.onopen = function () { 
   console.log("Connected to the signaling server"); 
};
  
//when we got a message from a signaling server 
conn.onmessage = function (msg) { 
   console.log("Got message", msg.data);
	
   var data = JSON.parse(msg.data);
	
   switch(data.type) { 
      case "login": 
         handleLogin(data.success); 
         break; 
      //when somebody wants to call us 
      case "offer":
         handleOffer(data.offer, data.name); 
         break; 
      case "answer": 
         handleAnswer(data.answer); 
         break; 
      //when a remote peer sends an ice candidate to us 
      case "candidate": 
         handleCandidate(data.candidate); 
         break; 
      case "leave": 
         handleLeave(); 
         break; 
      default: 
         break; 
   } 
};
  
conn.onerror = function (err) { 
   console.log("Got error", err); 
};  

//alias for sending JSON encoded messages 
function send(message) { 
   //attach the other peer username to our messages 
   if (connectedUser) { 
      message.name = connectedUser; 
   } 
	
   conn.send(JSON.stringify(message)); 
}; 
 
//****** 
//UI selectors block 
//******

var loginPage = document.querySelector('#loginPage'); 
var usernameInput = document.querySelector('#usernameInput'); 
var passwordInput = document.querySelector('#passwordInput'); 
var loginBtn = document.querySelector('#loginBtn'); 

var callPage = document.querySelector('#callPage'); 
var callToUsernameInput = document.querySelector('#callToUsernameInput');
var callBtn = document.querySelector('#callBtn'); 
var hangUpBtn = document.querySelector('#hangUpBtn');
  
var msgInput = document.querySelector('#msgInput'); 
var sendMsgBtn = document.querySelector('#sendMsgBtn'); 
var chatArea = document.querySelector('#chatarea'); 

var yourConn; 
var dataChannel;
  
callPage.style.display = "none";
  
// Login when the user clicks the button 
loginBtn.addEventListener("click", function (event) { 
   name = usernameInput.value; 
   var pwd = passwordInput.value;  
	
   if (name.length > 0) { 
      send({ 
         type: "login", 
         name: name, 
         password: pwd 
      }); 
   } 
	
}); 
 
function handleLogin(success) { 
   if (success === false) {
      alert("Ooops...incorrect username or password"); 
   } else { 
      loginPage.style.display = "none"; 
      callPage.style.display = "block";
		
      //********************** 
      //Starting a peer connection 
      //********************** 
		
      //using Google public stun server 
      var configuration = { 
         "iceServers": [{ "url": "stun:stun2.1.google.com:19302" }] 
      }; 
		
      yourConn = new webkitRTCPeerConnection(configuration, {optional: [{RtpDataChannels: true}]}); 
		
      // Setup ice handling 
      yourConn.onicecandidate = function (event) { 
         if (event.candidate) { 
            send({ 
               type: "candidate", 
               candidate: event.candidate 
            }); 
         } 
      };
		
      //creating data channel 
      dataChannel = yourConn.createDataChannel("channel1", {reliable:true}); 
		
      dataChannel.onerror = function (error) { 
         console.log("Ooops...error:", error); 
      };
		
      //when we receive a message from the other peer, display it on the screen 
      dataChannel.onmessage = function (event) { 
         chatArea.innerHTML += connectedUser + ": " + event.data + "<br />"; 
      };  
      dataChannel.onclose = function () { 
         console.log("data channel is closed"); 
      };  
   } 
	
};
  
//initiating a call 
callBtn.addEventListener("click", function () { 
   var callToUsername = callToUsernameInput.value;
	
   if (callToUsername.length > 0) {
	
      connectedUser = callToUsername;
		
      // create an offer 
      yourConn.createOffer(function (offer) { 
         send({ 
            type: "offer", 
            offer: offer 
         }); 
			
         yourConn.setLocalDescription(offer); 
			
      }, function (error) { 
         alert("Error when creating an offer"); 
      });  
   } 
});
 
//when somebody sends us an offer 
function handleOffer(offer, name) { 
   connectedUser = name; 
   yourConn.setRemoteDescription(new RTCSessionDescription(offer));
	
   //create an answer to an offer 
   yourConn.createAnswer(function (answer) { 
      yourConn.setLocalDescription(answer); 
		
      send({ 
         type: "answer", 
         answer: answer 
      }); 
		
   }, function (error) { 
      alert("Error when creating an answer"); 
   }); 
	
};
  
//when we got an answer from a remote user 
function handleAnswer(answer) { 
   yourConn.setRemoteDescription(new RTCSessionDescription(answer)); 
};
  
//when we got an ice candidate from a remote user 
function handleCandidate(candidate) { 
   yourConn.addIceCandidate(new RTCIceCandidate(candidate)); 
};
   
//hang up 
hangUpBtn.addEventListener("click", function () { 

   send({ 
      type: "leave"
   }); 
	
   handleLeave(); 
});
  
function handleLeave() { 
   connectedUser = null; 
   yourConn.close(); 
   yourConn.onicecandidate = null; 
}; 
 
//when user clicks the "send message" button 
sendMsgBtn.addEventListener("click", function (event) { 
   var val = msgInput.value; 
   chatArea.innerHTML += name + ": " + val + "<br />"; 
	
   //sending a message to a connected peer 
   dataChannel.send(val); 
   msgInput.value = ""; 
});

Şimdi güvenli sinyal sunucumuzu düğüm sunucusu aracılığıyla çalıştırın . Düğüm statikini değiştirilmiş sohbet demo klasörünün içinde çalıştırın . Açıklocalhost:8080iki tarayıcı sekmesinde. Giriş yapmayı deneyin. Yalnızca “password1” ile “user1” ve “password2” ile “user2” nin oturum açmasına izin verildiğini unutmayın. Ardından RTCPeerConnection'ı kurun (başka bir kullanıcıyı arayın) ve bir mesaj göndermeyi deneyin.

Aşağıda, güvenli sinyalleme sunucumuzun tüm kodu yer almaktadır -

//require file system module 
var fs = require('fs'); 
var httpServ = require('https');

//https://github.com/visionmedia/superagent/issues/205 
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
  
//out secure server will bind to the port 9090 
var cfg = { 
   port: 9090, 
   ssl_key: 'server.key', 
   ssl_cert: 'server.crt' 
};
  
//in case of http request just send back "OK" 
var processRequest = function(req, res){ 
   res.writeHead(200); 
   res.end("OK"); 
};
  
//create our server with SSL enabled 
var app = httpServ.createServer({ 
   key: fs.readFileSync(cfg.ssl_key), 
   cert: fs.readFileSync(cfg.ssl_cert) 
}, processRequest).listen(cfg.port);
  
//require our websocket library 
var WebSocketServer = require('ws').Server; 

//creating a websocket server at port 9090 
var wss = new WebSocketServer({server: app}); 

//all connected to the server users 
var users = {};
  
//require the redis library in Node.js 
var redis = require("redis"); 

//creating the redis client object 
var redisClient = redis.createClient();

//when a user connects to our sever 
wss.on('connection', function(connection) { 
   console.log("user connected"); 
	
   //when server gets a message from a connected user 
   connection.on('message', function(message) {  
	
      var data; 
      //accepting only JSON messages 
      try { 
         data = JSON.parse(message); 
      } catch (e) { 
         console.log("Invalid JSON"); 
         data = {}; 
      } 
		
      //check whether a user is authenticated 
      if(data.type != "login") { 
         //if user is not authenticated 
         if(!connection.isAuth) { 
            sendTo(connection, { 
               type: "error", 
               message: "You are not authenticated" 
            }); 
				
            return; 
         } 
      }
		
      //switching type of the user message 
      switch (data.type) { 
         //when a user tries to login 
         case "login":
            console.log("User logged:", data.name); 
            //get password for this username from redis database 
            redisClient.get(data.name, function(err, reply) {
			  
               //check if password matches with the one stored in redis 
               var loginSuccess = reply === data.password;
				  
               //if anyone is logged in with this username or incorrect password 
                  then refuse 
               if(users[data.name] || !loginSuccess) { 
                  sendTo(connection, { 
                     type: "login", 
                     success: false 
                  }); 
               } else { 
                  //save user connection on the server 
                  users[data.name] = connection; 
                  connection.name = data.name; 
                  connection.isAuth = true; 
						
                  sendTo(connection, { 
                     type: "login", 
                     success: true 
                  }); 
               }  
            }); 
				
            break;
				
         case "offer": 
            //for ex. UserA wants to call UserB 
            console.log("Sending offer to: ", data.name); 
				
            //if UserB exists then send him offer details 
            var conn = users[data.name];
				
            if(conn != null) { 
               //setting that UserA connected with UserB 
               connection.otherName = data.name;
					
               sendTo(conn, { 
                  type: "offer", 
                  offer: data.offer, 
                  name: connection.name 
               }); 
            } 
				
            break;
				
         case "answer": 
            console.log("Sending answer to: ", data.name); 
            //for ex. UserB answers UserA 
            var conn = users[data.name]; 
				
            if(conn != null) { 
               connection.otherName = data.name;
					
               sendTo(conn, { 
                  type: "answer", 
                  answer: data.answer 
               }); 
            } 
				
            break;
				
         case "candidate": 
            console.log("Sending candidate to:",data.name); 
            var conn = users[data.name];
				
            if(conn != null) { 
               sendTo(conn, { 
                  type: "candidate", 
                  candidate: data.candidate 
               });
            } 
				
            break;
				
         case "leave": 
            console.log("Disconnecting from", data.name); 
            var conn = users[data.name]; 
            conn.otherName = null; 
				
            //notify the other user so he can disconnect his peer connection 
            if(conn != null) { 
               sendTo(conn, { 
                  type: "leave" 
               }); 
            }  
				
            break;
				
         connection.on("close", function() {
			
            if(connection.name) { 
               delete users[connection.name]; 
					
               if(connection.otherName) { 
                  console.log("Disconnecting from ", connection.otherName); 
                  var conn = users[connection.otherName]; 
                  conn.otherName = null;  
						
                  if(conn != null) { 
                     sendTo(conn, { 
                        type: "leave" 
                    }); 
                  } 
						
               } 
            } 
         });
			
         default: 
            sendTo(connection, { 
               type: "error", 
               message: "Command no found: " + data.type 
            }); 
				
            break; 
      }  
   });
	
   //when user exits, for example closes a browser window 
   //this may help if we are still in "offer","answer" or "candidate" state 
   connection.on("close", function() { 
      if(connection.name) { 
         delete users[connection.name]; 
      } 
   });
	
   connection.send("Hello from server"); 
});
  
function sendTo(connection, message) { 
   connection.send(JSON.stringify(message)); 
}

Özet

Bu bölümde, sinyalleme sunucumuza kullanıcı kimlik doğrulaması ekledik. Kendinden imzalı SSL sertifikalarının nasıl oluşturulacağını ve WebRTC uygulamaları kapsamında nasıl kullanılacağını da öğrendik.