对RTCPeerConnection做了简单的封装,实现最简单的两点通过RTCDataChannel来发送数据
需要一个websocket server做调度。
刚开始接触webrtc的点对点通讯还是有些懵逼,其实最好的教程就是一些大站的官方教程。
例如MDN这种 https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API
看完之后是完全用不到买什么书啊,或者网上的教程。
<style>
a.user {
display: block;
height: 30px;
width: auto;
}
</style>
<script>
class RTCPeerRequest {
peerConnection;
dataChannel;
peerId;
targetId;
icecandidate;
message;
constructor() {
var conf = {
"iceServers": [{ "url": "stun:stun.l.google.com:19302" }]
};
this.peerConnection = new RTCPeerConnection(conf, { optional: [{ RtpDataChannel: true }] });
this.peerConnection.onicecandidate = this.handleIceCandidate.bind(this);
this.peerConnection.ondatachannel = this.handleDataChannel.bind(this);
}
onicecandidate(callback) {
if(callback instanceof Function) {
this.icecandidate = callback;
}
return this;
}
onmessage(callback) {
if(callback instanceof Function) {
this.message = callback;
}
return this;
}
offer(offercallback) {
this.openChannel();
var peer = this.peerConnection;
this.peerConnection.createOffer()
.then(function (offer) {
console.log("offer=%o", offer)
return peer.setLocalDescription(offer);
})
.then(() => {
if(offercallback instanceof Function) {
offercallback(peer.localDescription);
}
})
.catch((err) => { console.log("offer error %o", err); });
return this;
}
answer(answercallback) {
var peer = this.peerConnection;
this.peerConnection.createAnswer()
.then((answer) => {
return peer.setLocalDescription(answer);
})
.then(() => {
if(answercallback instanceof Function) {
answercallback(peer.localDescription);
}
})
.catch((err) => { console.log("answer error %o", err); });
return this;
}
openChannel() {
var dataChannelOptions = {
reliable: true
}
this.dataChannel = this.peerConnection.createDataChannel("datachannel" + this.targetId, dataChannelOptions);
this.dataChannel.onopen = this.handleChannelOpen.bind(this);
this.dataChannel.onerror = this.handleChannelError.bind(this);
this.dataChannel.onmessage = this.handChannelData.bind(this);
return this;
}
send(data) {
this.dataChannel.send(data);
return this;
}
remoteSdp(sdp) {
this.peerConnection.setRemoteDescription(sdp);
return this;
}
addIceCandidate(candidate) {
console.log("addIceCandidate:%o", candidate);
this.peerConnection.addIceCandidate(candidate);
return this;
}
handleIceCandidate(e) {
console.log("handle candidate=%o", e.candidate);
if (e.candidate != null && this.icecandidate != null) {
this.icecandidate(e.candidate);
}
}
handleDataChannel(e) {
console.log("create channel");
this.dataChannel = e.channel;
this.dataChannel.onopen = this.handleChannelOpen.bind(this);
this.dataChannel.onerror = this.handleChannelError.bind(this);
this.dataChannel.onmessage = this.handChannelData.bind(this);
}
handleChannelOpen(){
console.log("data channel opened.")
}
handleChannelError(){
console.log("data channel error.")
}
handChannelData(e) {
console.log("data channel message = %o", e);
if(this.message) {
this.message(e.data);
}
}
}
</script>
<input type="text" id="name" />
<input type="button" id="loginButton" onclick="joinServer()" />
<div id="onlinelist" style="width:500px;height:300px;border:1px solid #9c9ace"></div>
<input type="text" id="message" />
<input type="button" id="chatButton" onclick="sendMessage()" />
<div id="messagelist" style="width:500px;height:300px;border:1px solid #9c9ace"></div>
<script>
var websocket = null;
var onlines = [];
var userObject = null;
var peerConnection = null;
var target = null;
var dataChannel = null;
var rtcRequest = null;
function joinServer() {
websocket = new WebSocket("ws://127.0.0.1:8080/" + document.getElementById("name").value);
websocket.onmessage = function (message) {
let command = JSON.parse(message.data);
console.log("command=%o", command)
switch (command.type) {
case "login":
userObject = command.data;
break;
case "online":
let onlines = command.data;
for (var i = 0; i < onlines.length; i++) {
listUser(onlines[i].id, onlines[i].name);
}
break;
case "join":
listUser(command.data.id, command.data.name);
break;
case "offer":
let offer = command.data;
let fromid = command.from;
createAnswer(fromid, offer);
break;
case "answer":
let answer = command.data;
rtcRequest.remoteSdp(command.data);
break;
case "candidate":
let candidate = command.data;
rtcRequest.addIceCandidate(candidate);
break;
case "leave":
var users = document.getElementById("onlinelist").children;
for (var i = 0; i < users.length; i++) {
var user = users[i];
if (user.nodeName == "A" && user.getAttribute("uid") == command.data.id) {
user.parentNode.removeChild(user);
break;
}
}
break;
}
};
}
function listUser(id, name) {
var a = document.createElement("A");
a.innerHTML = name;
a.setAttribute("uid", id);
a.href = "#none";
a.className = "user";
a.onclick = function () {
createOffer(a.getAttribute("uid"));
}
document.getElementById("onlinelist").appendChild(a);
}
function createOffer(id) {
if (id == userObject.id) {
alert("is self");
return;
}
rtcRequest = new RTCPeerRequest()
.onicecandidate(candidate=>{
websocket.send(JSON.stringify({
type: "candidate",
from: userObject.id,
target: id,
data: candidate}));
})
.onmessage(message=>{
console.log("receive %o",message);
})
.offer((offer)=>{
var command = {
type: "offer",
from: userObject.id,
target: id,
data: offer
};
websocket.send(JSON.stringify(command));
});
}
function createAnswer(fromid, offer) {
rtcRequest = new RTCPeerRequest()
.onicecandidate((candidate)=>{
websocket.send(JSON.stringify({
type: "candidate",
from: userObject.id,
target: fromid,
data: candidate
}));
})
.onmessage((message)=>{
console.log("receive %o",message);
})
.remoteSdp(offer)
.answer((answer)=>{
var command = {
type: "answer",
from: userObject.id,
target: fromid,
data: answer
};
websocket.send(JSON.stringify(command));
});
}
function sendMessage() {
var input = document.getElementById("message");
if(input.value != "") {
rtcRequest.send(input.value);
input.value = "";
input.focus();
}
}
</script>