RTCDataChannel
æ¥å£æ¯ WebRTC API çä¸ä¸ªåè½ï¼å¯ä»¥è®©ä½ å¨ä¸¤ä¸ªå¯¹çç¹ä¹é´æå¼ä¸ä¸ªééï¼ä½ å¯ä»¥éè¿è¯¥ééåé忥æ¶ä»»ææ°æ®ãAPI ææå°ç±»ä¼¼äº WebSocket APIï¼å æ¤å¯ä»¥ä¸ºæ¯ä¸ª API 使ç¨ç¸åçç¼ç¨æ¨¡åã
卿¬ç¤ºä¾ä¸ï¼æä»¬ä¼å¨ä¸ä¸ªé¡µé¢å
建ç«ä¸æ¡ RTCDataChannel
è¿æ¥ãè¿ä¸ªåºæ¯æ¯ä¸ºäºæ¼ç¤ºå¦ä½è¿æ¥ä¸¤ä¸ª Peerï¼å®é
åºæ¯å¹¶ä¸å¸¸è§ã卿¬ç¤ºä¾ä¸è§£éäºååå建ç«è¿æ¥çè¿ç¨ï¼å®ä½åè¿æ¥å¦å¤ä¸å°ä¸»æºçåºæ¯å¨å¦å¤çä¸ä¸ªç¤ºä¾ä¸ã
é¦å 让æä»¬ççæä»¬éè¦ç HTML 代ç ãå ¶å®å¾ç®åï¼æä»¬å æä¸¤ä¸ªæé®ç¨æ¥é¾æ¥åæå¼è¿æ¥ã
<button id="connectButton" name="connectButton" class="buttonleft">
Connect
</button>
<button
id="disconnectButton"
name="disconnectButton"
class="buttonright"
disabled>
Disconnect
</button>
ç¶åæä»¬è¿æä¸ä¸ªè¾å
¥æ¡ï¼ç¨æ¥è¾å
¥æ¶æ¯ãä¸ä¸ªæé®ï¼æ¥è§¦ååéäºä»¶ãè¿ä¸ª <div>
æ¯ç» channel ä¸ç¬¬ä¸ä¸ªèç¹ä½¿ç¨çã
<div class="messagebox">
<label for="message"
>Enter a message:
<input
type="text"
name="message"
id="message"
placeholder="Message text"
inputmode="latin"
size="60"
maxlength="120"
disabled />
</label>
<button id="sendButton" name="sendButton" class="buttonright" disabled>
Send
</button>
</div>
æåï¼è¿æä¸ä¸ªå° DIV ç¨æ¥æ¾ç¤ºæ¶å°çå
容ãè¿ä¸ª <div>
æ¯ç» channel ä¸ç¬¬äºä¸ª peer 使ç¨çã
<div class="messagebox" id="receivebox">
<p>Messages received:</p>
</div>
JavaScript 代ç
ä½ å¯ä»¥ç´æ¥å° GitHub 䏿¥ç代ç ï¼ä¸é¢æä»¬ä¹ä¼ä¸æ¥ä¸æ¥çè§£éã
å¯å¨å½èæ¬å¼å§è¿è¡æ¶ï¼æä»¬è®¾ç½® load
äºä»¶çå¬å¨ï¼å æ¤ä¸æ¦é¡µé¢å®å
¨å è½½ï¼startup()
彿°å°è¢«è°ç¨ã
let connectButton = null;
let disconnectButton = null;
let sendButton = null;
let messageInputBox = null;
let receiveBox = null;
let localConnection = null; // RTCPeerConnection for our "local" connection
let remoteConnection = null; // RTCPeerConnection for the "remote"
let sendChannel = null; // RTCDataChannel for the local (sender)
let receiveChannel = null; // RTCDataChannel for the remote (receiver)
function startup() {
connectButton = document.getElementById("connectButton");
disconnectButton = document.getElementById("disconnectButton");
sendButton = document.getElementById("sendButton");
messageInputBox = document.getElementById("message");
receiveBox = document.getElementById("receivebox");
// Set event listeners for user interface widgets
connectButton.addEventListener("click", connectPeers, false);
disconnectButton.addEventListener("click", disconnectPeers, false);
sendButton.addEventListener("click", sendMessage, false);
}
ä¸è¿°é»è¾ä¸ç®äºç¶ãæä»¬æ¿å°ææéè¦æä½ç页é¢å ç´ å¼ç¨ï¼ä¹å对ä¸ä¸ªæé®è®¾ç½®äºä»¶çå¬å¨ã
建ç«è¿æ¥å½ç¨æ·ç¹å»âConnectâæé®ï¼connectPeers()
æ¹æ³è¢«è°ç¨ãä¸é¢å°éä¸åæè¯¥æ¹æ³ä¸çç»èã
夿³¨ï¼ 尽管åä¸è¿æ¥ç两端é½å¨åä¸é¡µé¢ï¼æä»¬å°å¯å¨è¿æ¥çä¸ç«¯ç§°ä¸ºâæ¬å°â端ï¼å¦ä¸ç«¯ç§°ä¸ºâè¿ç¨â端ã
å»ºç«æ¬å°èç¹localConnection = new RTCPeerConnection();
sendChannel = localConnection.createDataChannel("sendChannel");
sendChannel.onopen = handleSendChannelStatusChange;
sendChannel.onclose = handleSendChannelStatusChange;
ç¬¬ä¸æ¥æ¯å»ºç«è¯¥è¿æ¥çâæ¬å°â端ï¼å®æ¯åèµ·è¿æ¥è¯·æ±ç䏿¹ãä¸ä¸æ¥æ¯éè¿è°ç¨ RTCPeerConnection.createDataChannel()
æ¥å建 RTCDataChannel
并设置äºä»¶ä¾¦å¬ä»¥çè§è¯¥æ°æ®ééï¼ä»èè·ç¥è¯¥ééçæå¼æå
³éï¼å³è·å¾è¯¥å¯¹çè¿æ¥çééæå¼æè
å
³éçæ¶æºï¼ã
请å¡å¿
è®°ä½è¯¥ééçæ¯ä¸ç«¯é½æ¥æèªå·±ç RTCDataChannel
对象ã
remoteConnection = new RTCPeerConnection();
remoteConnection.ondatachannel = receiveChannelCallback;
è¿ç¨ç«¯ç建ç«è¿ç¨ç±»ä¼¼âæ¬å°â端ï¼ä½å®æ éèªå·±å建 RTCDataChannel
ï¼å 为æä»¬å°éè¿ä¸é¢å»ºç«çæ¸ éè¿è¡è¿æ¥ãæä»¬å建对 datachannel
çäºä»¶å¤çåè°ï¼æ°æ®ééæå¼æ¶è¯¥é»è¾å°è¢«æ§è¡ï¼è¯¥åè°å¤çå°æ¥æ¶å°ä¸ä¸ª RTCDataChannel
å¯¹è±¡ï¼æ¤è¿ç¨å°å¨æç« åé¢é¨åæè¿°ã
ä¸ä¸æ¥ä¸ºæ¯ä¸ªè¿æ¥å»ºç« ICE åé侦å¬å¤çï¼å½è¿æ¥ç䏿¹åºç°æ°ç ICE åéæ¶è¯¥ä¾¦å¬é»è¾å°è¢«è°ç¨ä»¥åç¥è¿æ¥çå¦ä¸æ¹æ¤æ¶æ¯ã
夿³¨ï¼ å¨ç°å®åºæ¯ï¼å½åä¸è¿æ¥ç两èç¹è¿è¡äºä¸åçä¸ä¸æï¼å»ºç«è¿æ¥çè¿ç¨æç¨å¾®å¤æäºï¼æ¯ä¸æ¬¡åæ¹éè¿è°ç¨ RTCPeerConnection.addIceCandidate()
ï¼æåºè¿æ¥æ¹å¼ç建议ï¼ä¾å¦ï¼UDPãä¸ç»§ UDPãTCP ä¹ç±»çï¼ï¼åæ¹æ¥åå¾å¤ç´å°è¾¾æä¸è´ãæ¬ææ¢ç¶ä¸æ¶åç°å®ç½ç»ç¯å¢ï¼å æ¤æä»¬åå®åæ¹æ¥å馿¬¡è¿æ¥å»ºè®®ã
localConnection.onicecandidate = (e) =>
!e.candidate ||
remoteConnection.addIceCandidate(e.candidate).catch(handleAddCandidateError);
remoteConnection.onicecandidate = (e) =>
!e.candidate ||
localConnection.addIceCandidate(e.candidate).catch(handleAddCandidateError);
æä»¬é
ç½®æ¯ä¸ª RTCPeerConnection
对äºäºä»¶ icecandidate
建ç«äºä»¶å¤çã
建ç«èç¹è¿æ¥çæåä¸é¡¹æ¯å建ä¸ä¸ªè¿æ¥ offerã
localConnection
.createOffer()
.then((offer) => localConnection.setLocalDescription(offer))
.then(() =>
remoteConnection.setRemoteDescription(localConnection.localDescription),
)
.then(() => remoteConnection.createAnswer())
.then((answer) => remoteConnection.setLocalDescription(answer))
.then(() =>
localConnection.setRemoteDescription(remoteConnection.localDescription),
)
.catch(handleCreateDescriptionError);
éè¡è§£è¯»ä¸é¢ç代ç ï¼
RTCPeerConnection.createOffer()
æ¹æ³å建 SDPï¼Session Description Protocolï¼åèåç¨äºæè¿°æä»¬æå¾
建ç«çè¿æ¥ãè¯¥æ¹æ³å¯éå°æ¥åä¸ä¸ªæè¿°è¿æ¥éå¶ç对象ï¼ä¾å¦è¿æ¥æ¯å¦å¿
é¡»æ¯æé³é¢ãè§é¢æè
两è
齿¯æã卿们çç®å示ä¾ä¸ï¼æ²¡æå¼å
¥è¯¥éå¶ãRTCPeerConnection.setLocalDescription()
æ¹æ³ãç¨äºé
ç½® local 端çè¿æ¥ãremoteConnection.
RTCPeerConnection.setRemoteDescription()
ï¼åç¥ remote èç¹ä¸è¿°æè¿°ï¼å° local èç¹è¿æ¥å°å°è¿ç¨ãç°å¨ remoteConnection
äºè§£æ£å¨å»ºç«çè¿æ¥ãcreateAnswer()
æ¹æ³äºä»¥ååºãè¯¥æ¹æ³çæä¸ä¸ª SDP äºè¿å¶åï¼ç¨äºæè¿° remote èç¹æ¿æå¹¶ä¸æè½å建ç«çè¿æ¥ãè¿æ ·çè¿æ¥é
ç½®æ¯ä¸¤ç«¯åå¯ä»¥æ¯æå¯é项çç»åãRTCPeerConnection.setLocalDescription()
ä¼ å
¥ remoteConnectionã该è°ç¨å®æäº remote ç«¯è¿æ¥ç建ç«ï¼å¯¹äºå¯¹ç«¯ç remote èç¹èè¨ï¼æ¯å®ç local 端ãè¿ç§å述容æä½¿äººå°æï¼ä½æ¯çå¤äºä½ å°±ä¹ æ¯äºï¼ãRTCPeerConnection.setRemoteDescription()
æ¹æ³ï¼æ¬å°è¿æ¥çè¿ç«¯æè¿°è¢«è®¾ç½®ä¸ºæå remote èç¹ãcatch()
è°ç¨ä¸ä¸ªç¨äºå¤çä»»ä½å¼å¸¸çé»è¾ã夿³¨ï¼ 忬¡ç³æï¼ä¸è¿°å¤çè¿ç¨å¹¶éé对ç°å®ä¸ççå®ç°ï¼å¨æ£å¸¸ç¯å¢ä¸ï¼å»ºç«è¿æ¥çä¸¤ç«¯çæºå¨ï¼è¿è¡ä¸¤åä¸åç代ç ï¼ç¨äºäº¤äºåååè¿æ¥è¿ç¨ã
对æåç对çè¿æ¥çå¤çå½ peer-to-peer è¿æ¥çä»»ä½ä¸æ¹æåè¿æ¥ï¼ç¸åºç RTCPeerConnection
ç icecandidate
äºä»¶å°è¢«è§¦åãå¨äºä»¶çå¤çä¸å¯ä»¥æ§è¡ä»»ä½éè¦çæä½ï¼ä½å¨æ¬ä¾ä¸ï¼æä»¬æéè¦åçåªæ¯æ´æ°ç¨æ·çé¢ã
function handleCreateDescriptionError(error) {
console.log(`Unable to create an offer: ${error.toString()}`);
}
function handleLocalAddCandidateSuccess() {
connectButton.disabled = true;
}
function handleRemoteAddCandidateSuccess() {
disconnectButton.disabled = false;
}
function handleAddCandidateError() {
console.log("Oh noes! addICECandidate failed!");
}
彿¬å°å¯¹çç¹è¿æ¥æåæ¶ï¼ç¦ç¨âConnectâæé®ï¼å½è¿ç¨å¯¹çç¹è¿æ¥æ¶è®¸ç¨âDisconnectâæé®ã
æ°æ®ééçè¿æ¥RTCPeerConnection
䏿¦æå¼ï¼äºä»¶ datachannel
被åéå°è¿ç«¯ä»¥å®ææå¼æ°æ®ééçå¤çï¼è¯¥äºä»¶è§¦å receiveChannelCallback()
æ¹æ³ï¼å¦ä¸æç¤ºï¼
function receiveChannelCallback(event) {
receiveChannel = event.channel;
receiveChannel.onmessage = handleReceiveMessage;
receiveChannel.onopen = handleReceiveChannelStatusChange;
receiveChannel.onclose = handleReceiveChannelStatusChange;
}
äºä»¶ datachannel
å¨å®ç channel 屿§ä¸å
æ¬äºï¼å¯¹ä»£è¡¨ remote èç¹ç channel ç RTCDataChannel
çæåï¼å®ä¿åäºæä»¬ç¨ä»¥å¨è¯¥ channel ä¸å¯¹æä»¬å¸æå¤ççäºä»¶å»ºç«çäºä»¶çå¬ã䏿¦ä¾¦å¬å»ºç«ï¼æ¯å½ remote èç¹æ¥æ¶å°æ°æ® handleReceiveMessage()
æ¹æ³å°è¢«è°ç¨ï¼æ¯å½ééçè¿æ¥ç¶æåçæ¹å handleReceiveChannelStatusChange()
æ¹æ³å°è¢«è°ç¨ï¼å æ¤ééå®å
¨æå¼æè
å
³éæ¶æä»¬é½å¯ä»¥ä½åºç¸åºçç¸åºã
local èç¹å remote èç¹éç¨åæ ·çæ¹æ³å¤ç表示ééè¿æ¥ç¶æåæ´çäºä»¶ã
å½ local èç¹éé open æè
close äºä»¶ï¼handleSendChannelStatusChange()
æ¹æ³è¢«è°ç¨ï¼
function handleSendChannelStatusChange(event) {
if (sendChannel) {
const state = sendChannel.readyState;
if (state === "open") {
messageInputBox.disabled = false;
messageInputBox.focus();
sendButton.disabled = false;
disconnectButton.disabled = false;
connectButton.disabled = true;
} else {
messageInputBox.disabled = true;
sendButton.disabled = true;
connectButton.disabled = false;
disconnectButton.disabled = true;
}
}
}
妿ééç¶æå·²ç»åæ´ä¸ºâopenâï¼æå³çæä»¬å·²ç»å®æäºå¨ä¸¤å¯¹çèç¹ä¹é´å»ºç«è¿æ¥ãç¸åºå°ç¨æ·ç颿 ¹æ®ç¶ææ´æ°ï¼è®¸ç¨å¹¶å°è¾å ¥å æ èç¦å¨ææ¬è¾å ¥æ¡ï¼ä»¥ä¾¿ç¨æ·å¯ä»¥ç«å³è¾å ¥è¦åéç»å¯¹æ¹çææ¬æ¶æ¯ï¼åæ¶çé¢è®¸ç¨âSendâåâDisconnectâæé®ï¼å 为å®ä»¬å·²ç»åå¤å¥½äºï¼ï¼ç¦ç¨âConnectâæé®ï¼å 为å¨å·²ç»å»ºç«è¿æ¥çæ åµä¸ç¨ä¸çå®ï¼ã
å½è¿æ¥ç¶æåæ´ä¸ºâclosedâæ¶ï¼ç颿§è¡ç¸åçæä½ï¼ç¦ç¨ææ¬è¾å ¥æ¡åâSendâæé®ï¼è®¸ç¨âConnectâæé®ï¼ä»¥ä¾¿ç¨æ·å¨éè¦æ¶å¯ä»¥æå¼æ°çè¿æ¥ï¼ï¼ç¦ç¨âDisconnectâæé®ï¼å 为没æè¿æ¥æ¶ç¨ä¸çå®ï¼ã
å¦ä¸æ¹é¢ï¼ä½ä¸ºæä»¬ä¾åç remote èç¹ï¼åæ è§è¿äºç¶ææ¹åäºä»¶ï¼ä» ä» æ¯å¨æ§å¶å°è¾åºå®ä»¬ï¼
function handleReceiveChannelStatusChange(event) {
if (receiveChannel) {
console.log(
`Receive channel's status has changed to ${receiveChannel.readyState}`,
);
}
}
handleReceiveChannelStatusChange()
æ¹æ³æ¥æ¶å°åççäºä»¶ï¼äºä»¶ç±»å为 RTCDataChannelEvent
ã
å½ç¨æ·æä¸âSendâæé®ï¼è§¦åæä»¬å·²å»ºç«ç该æé®ç click
äºä»¶å¤çå¨ï¼å¨å¤çé»è¾ä¸è°ç¨ sendMessage() æ¹æ³ãè¯¥æ¹æ³ä¹è¶³å¤ç®åï¼
function sendMessage() {
const message = messageInputBox.value;
sendChannel.send(message);
messageInputBox.value = "";
messageInputBox.focus();
}
é¦å
ï¼å¾
åéçæ¶æ¯ææ¬ä»ææ¬è¾å
¥æ¡ç value
屿§è·å¾ï¼ä¹åè¯¥ææ¬éè¿è°ç¨ sendChannel.send()
åéå°è¿ç¨å¯¹çç¹ã齿å®äºï¼ä½ä¸çåªæ¯äºç¨æ·ä½éªç³ââæ¸
空并èç¦ææ¬è¾å
¥æ¡ï¼ä»¥ä¾¿ç¨æ·å¯ä»¥ç«å³å¼å§ä¸ä¸æ¡æ¶æ¯çè¾å
¥ã
å½è¿ç¨ééåçâmessageâäºä»¶æ¶ï¼æä»¬ç handleReceiveMessage() æ¹æ³è¢«è°ç¨æ¥å¤çäºä»¶ã
function handleReceiveMessage(event) {
var el = document.createElement("p");
var txtNode = document.createTextNode(event.data);
el.appendChild(txtNode);
receiveBox.appendChild(el);
}
è¯¥æ¹æ³åªæ¯ç®åå°æ³¨å
¥äºä¸äº DOMï¼å®åå»ºäº <p>
å
ç´ ï¼ç¶ååå»ºäº Text
ç¨äºæ¾ç¤ºä»äºä»¶ç data
屿§æ¿å°çæ¶æ¯ææ¬ãè¯¥ææ¬èç¹ä½ä¸ºåèç¹éå å° receiveBox
åï¼æ¾ç¤ºå¨æµè§å¨çªå£å
容åºã
å½ç¨æ·ç¹å»âDisconnectâæé®ï¼æ ¹æ®ä¹åæä»¬è®¾ç½®çæé®äºä»¶å¤çé»è¾ï¼å°±ä¼è°ç¨ disconnectPeers()
ã
function disconnectPeers() {
// Close the RTCDataChannels if they're open.
sendChannel.close();
receiveChannel.close();
// Close the RTCPeerConnections
localConnection.close();
remoteConnection.close();
sendChannel = null;
receiveChannel = null;
localConnection = null;
remoteConnection = null;
// Update user interface elements
connectButton.disabled = false;
disconnectButton.disabled = true;
sendButton.disabled = true;
messageInputBox.value = "";
messageInputBox.disabled = true;
}
è¯¥æ¹æ³é¦å
å
³éæ¯ä¸ªèç¹ç RTCDataChannel
ï¼ä¹å类似å°å
³éæ¯ä¸ªèç¹ç RTCPeerConnection
ãå°ææå¯¹å®ä»¬çæå置为 null
以é¿å
æå¤çå¤ç¨ãä¹åæ´æ°çé¢ç¶æä»¥ç¬¦åç®åå·²ç»ä¸åå¨è¿æ¥çäºå®ã
æ¥ç GitHub 䏿ä¾ç webrtc-simple-datachannel æºä»£ç ã
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4