해당 글은 https://codelabs.developers.google.com/ 의 듀토리얼을 기반으로 작성하였습니다.
이번에는 RTCDataChannel을 이용하여 데이터를 교환하는 것을 해보려고 한다.
Step 03 - RTCDataChannel을 사용하여 데이터 교환
(코드분석)
'use strict';
var servers;
var localConnection;
var remoteConnection;
var sendChannel;
var receiveChannel;
var pcConstraint;
var dataConstraint;
var dataChannelSend = document.querySelector('textarea#dataChannelSend'); // 텍스트 입력 영역
var dataChannelReceive = document.querySelector('textarea#dataChannelReceive'); // 텍스트 출력 영역
var startButton = document.querySelector('button#startButton');
var sendButton = document.querySelector('button#sendButton');
var closeButton = document.querySelector('button#closeButton');
startButton.onclick = createConnection; // 스타트버튼을 누르면 createConnention을 실행
sendButton.onclick = sendData;
closeButton.onclick = closeDataChannels;
function enableStartButton() {
startButton.disabled = false;
}
function disableSendButton() {
sendButton.disabled = true;
}
function createConnection() {
dataChannelSend.placeholder = '';
servers = null;
pcConstraint = null;
dataConstraint = null;
trace('Using SCTP based data channels');
// For SCTP, reliable and ordered delivery is true by default.
// Add localConnection to global scope to make it visible
// from the browser console.
// SCTP는 신뢰할 수 있고 순서대로 딜리버리는 true가 default이다.
// 로컬연결을 글로벌 스코프에 추가하여 브라우저 콘솔로 부터 볼 수 있도록 한다.
window.localConnection = localConnection =
new RTCPeerConnection(servers, pcConstraint);
trace('Created local peer connection object localConnection');
// 로컬 커넥션을 RTCPeerConnection 객체로 선언
sendChannel = localConnection.createDataChannel('sendDataChannel',
dataConstraint);
trace('Created send data channel');
// 보내는 데이터 채널을 만든다.
localConnection.onicecandidate = iceCallback1;
sendChannel.onopen = onSendChannelStateChange; // html 상태를 바꿔주는 함수
sendChannel.onclose = onSendChannelStateChange; // html 상태를 바꿔주는 함수
// Add remoteConnection to global scope to make it visible
// from the browser console.
// 원격연결을 글로벌 스코프에 추가하여 브라우저 콘솔로 부터 볼 수 있도록 한다.
window.remoteConnection = remoteConnection =
new RTCPeerConnection(servers, pcConstraint);
trace('Created remote peer connection object remoteConnection');
remoteConnection.onicecandidate = iceCallback2;
remoteConnection.ondatachannel = receiveChannelCallback;
localConnection.createOffer().then(
gotDescription1,
onCreateSessionDescriptionError
);
startButton.disabled = true;
closeButton.disabled = false;
}
function onCreateSessionDescriptionError(error) {
trace('Failed to create session description: ' + error.toString());
}
function sendData() {
var data = dataChannelSend.value;
sendChannel.send(data);
trace('Sent Data: ' + data);
}
function closeDataChannels() {
trace('Closing data channels');
sendChannel.close();
trace('Closed data channel with label: ' + sendChannel.label);
receiveChannel.close();
trace('Closed data channel with label: ' + receiveChannel.label);
localConnection.close();
remoteConnection.close();
localConnection = null;
remoteConnection = null;
trace('Closed peer connections');
startButton.disabled = false;
sendButton.disabled = true;
closeButton.disabled = true;
dataChannelSend.value = '';
dataChannelReceive.value = '';
dataChannelSend.disabled = true;
disableSendButton();
enableStartButton();
}
function gotDescription1(desc) {
localConnection.setLocalDescription(desc);
trace('Offer from localConnection \n' + desc.sdp);
remoteConnection.setRemoteDescription(desc);
remoteConnection.createAnswer().then(
gotDescription2,
onCreateSessionDescriptionError
);
}
function gotDescription2(desc) {
remoteConnection.setLocalDescription(desc);
trace('Answer from remoteConnection \n' + desc.sdp);
localConnection.setRemoteDescription(desc);
}
function iceCallback1(event) {
trace('local ice callback');
if (event.candidate) {
remoteConnection.addIceCandidate(
event.candidate
).then(
onAddIceCandidateSuccess,
onAddIceCandidateError
);
trace('Local ICE candidate: \n' + event.candidate.candidate);
}
}
function iceCallback2(event) {
trace('remote ice callback');
if (event.candidate) {
localConnection.addIceCandidate(
event.candidate
).then(
onAddIceCandidateSuccess,
onAddIceCandidateError
);
trace('Remote ICE candidate: \n ' + event.candidate.candidate);
}
}
function onAddIceCandidateSuccess() {
trace('AddIceCandidate success.');
}
function onAddIceCandidateError(error) {
trace('Failed to add Ice Candidate: ' + error.toString());
}
function receiveChannelCallback(event) {
trace('Receive Channel Callback');
receiveChannel = event.channel;
receiveChannel.onmessage = onReceiveMessageCallback;
receiveChannel.onopen = onReceiveChannelStateChange;
receiveChannel.onclose = onReceiveChannelStateChange;
}
function onReceiveMessageCallback(event) {
trace('Received Message');
dataChannelReceive.value = event.data;
}
function onSendChannelStateChange() {
var readyState = sendChannel.readyState;
trace('Send channel state is: ' + readyState);
if (readyState === 'open') {
dataChannelSend.disabled = false;
dataChannelSend.focus();
sendButton.disabled = false;
closeButton.disabled = false;
} else {
dataChannelSend.disabled = true;
sendButton.disabled = true;
closeButton.disabled = true;
}
}
function onReceiveChannelStateChange() {
var readyState = receiveChannel.readyState;
trace('Receive channel state is: ' + readyState);
}
function trace(text) {
if (text[text.length - 1] === '\n') {
text = text.substring(0, text.length - 1);
}
if (window.performance) {
var now = (window.performance.now() / 1000).toFixed(3);
console.log(now + ': ' + text);
} else {
console.log(text);
}
}
(작동원리)
Websocket과 비슷한 원리로 동작한다.
다만, 여기서는 어떤 특정 url로 지정되어서 교환한다기 보다는, remote와 local의 peer 객체를 각각 생성하여 일방적으로 보내는?? 개념이라고 볼 수 있겠다. 추후에는 서버를 만들어서 해당 서버로 메세지를 send하면 해당 세션에 존져하는 모든 사용자에게 receive할수 있게 구성하면 되겠거니? 정도로 이해했다.
'Framework & Library > WebRTC' 카테고리의 다른 글
WebRTC 듀토리얼 클론/분석 - RTCPeerConnection으로 비디오 스트리밍 (0) | 2021.06.17 |
---|---|
WebRTC 듀토리얼 클론/분석 - 웹캠으로 비디오 스트리밍 하기 (0) | 2021.06.15 |
WebRTC 개념 정리 (0) | 2021.06.14 |