我创建了一个库/npm模块来处理RTCPeerConnection的信令过程。还有很多工作需要完成(错误处理,在信令过程中处理断开连接的用户,等等),但目前我主要是在寻找关于总体架构的建议/想法。
这是Github回购。
下面我包含了服务器端的模块和客户端的脚本。
var io = require('socket.io');
module.exports = function () {
// Is used to give each peer a unique ID
var peerIdCounter = 0;
// create next ID and return it
var incrementPeerIdCounter = function () {
peerIdCounter++;
return peerIdCounter;
};
// // // // /
/////////////////
// Peer Object //
/*****************************************************************************/
var createPeer = function (socket) {
/////////////////////////////////////
// Peer Object's Private Variables //
//////////////////////////////////////////////////
// //
var self;
var id;
// holds the peerConnect offer for this peer
var offer;
// list of peers that can connect
var connectingPeers = {};
// The Peer class
function Peer(socket) {
this.socket = socket;
id = incrementPeerIdCounter();
self = this;
// listen for client sending offer
self.socket.on('clientSendingOffer', receiveOfferFromClient);
// detect receiving an answer from clietn
self.socket.on('clientSendingAnswer', receiveAnswerFromClient);
// listen for ice candidates
self.socket.on('clientSendingIce', receiveIceCandidate);
}
// //
//////////////////////////////////////////////////
//////////////////////////////////
// Peer Object's Public Methods //
//////////////////////////////////////////////////
// //
// Initiates signaling process
Peer.prototype.connectToPeer = function (peer, successCallback, failCallback) {
// answering peer starts waiting for an offer
peer.acceptOfferFrom(self);
// This peer will make an offer
this.makeOfferRequest(peer);
};
// Request a peerConnection offer from the current peer
Peer.prototype.makeOfferRequest = function (peer) {
// add peer to list of accepted requests
connectingPeers[peer.getPeerId()] = peer;
// ask client for an offer
self.socket.emit('serverRequestingOffer', {
peerId: peer.getPeerId()
});
};
// Accept peerConnection offers from the specified peer
Peer.prototype.acceptOfferFrom = function (peer) {
// add peer to list of accepted requests
connectingPeers[peer.getPeerId()] = peer;
};
// getter for the peer ID
Peer.prototype.getPeerId = function () {
return id;
};
// //
//////////////////////////////////////////////////
///////////////////////////////////
// Peer Object's Private Methods //
//////////////////////////////////////////////////
// //
// Handle an offer sent from the client
var receiveOfferFromClient = function (data) {
offer = data.offer;
sendOfferToClient(data.peerId);
};
// Send offer to the other peer
var sendOfferToClient = function (peerId) {
connectingPeers[peerId].socket.emit(
'serverSendingOffer', {
peerId: self.getPeerId(),
offer: offer
});
};
// Send answer to initiating peer
var receiveAnswerFromClient = function (data) {
var peerId = data.peerId;
data.peerId = self.getPeerId();
connectingPeers[peerId].socket.emit('serverSendingAnswer', data);
};
//Handle ICE candidates received
var receiveIceCandidate = function (data) {
var iceInfo = {
peerId: self.getPeerId(),
candidate: data.candidate
};
connectingPeers[data.peerId].socket.emit('serverSendingIce', iceInfo);
};
// //
//////////////////////////////////////////////////
// Return an instance of the Peer class
return new Peer(socket);
};
/*____________________________________________________________________________*/
// // // // // // // /
/////////////////////////////
// Module's Public Methods //
/*****************************************************************************/
// Initiates listening for signaling requests
///////////////////////////////////////////////
var listen = function (port, successCallback, failCallback) {
if(typeof port != 'number') {
return false;
}
// Handle socket requests
/////////////////////////////
var server = io.listen(port);
server.sockets.on('connection', function (socket) {
var peer;
try {
peer = createPeer(socket);
} catch(e) {
failCallback(e);
} finally {
successCallback(peer);
}
});
return server;
};
// Return the public methods
return {
listen: listen
};
/*____________________________________________________________________________*/
}();(function () {
var signalfire = function () {
// // // // /
/////////////////
// Peer Object //
/*****************************************************************************/
var createPeer = function (socket, options) {
/////////////////////////////////////
// Peer Object's Private Variables //
//////////////////////////////////////////////////
// //
var self;
//holds the ids of the peers that are connecting
var connectingPeers = {};
// holds function to call when server requests a connection offer
// or provides a connection offer
var makeRTCPeer = options.connector || function () {};
// holds function to call when signaling process is complete
var signalingComplete = options.onSignalingComplete || function () {};
// The Peer class
function Peer(socket) {
this.socket = socket;
self = this;
// listen for server requesting offer
self.socket.on('serverRequestingOffer', sendOfferToServer);
// detect receiving an offer from server
self.socket.on('serverSendingOffer', sendAnswerToServer);
// listen for ice candidates
self.socket.on('serverSendingAnswer', receiveAnswerFromServer);
// listen for ice candidates
self.socket.on('serverSendingIce', receiveIceCandidate);
}
// //
//////////////////////////////////////////////////
///////////////////////////////////
// Peer Object's Private Methods //
//////////////////////////////////////////////////
// //
// Receive offer request and return an offer
var sendOfferToServer = function (data) {
// create RTCPeerConnection
var rtcPeerConnection = makeRTCPeer();
// initialize ice candidate listeners
iceSetup(rtcPeerConnection, data);
// Add connection to list of peers
connectingPeers[data.peerId] = rtcPeerConnection;
// create offer and send to server
rtcPeerConnection.createOffer(function (offerResponse) {
data.offer = offerResponse;
rtcPeerConnection.setLocalDescription(offerResponse, function () {
socket.emit('clientSendingOffer', data);
});
});
};
// Receive peer offer from server. Return an answer.
var sendAnswerToServer = function (data) {
// create RTCPeerConnection
var rtcPeerConnection = makeRTCPeer();
// initialize ice candidate listeners
iceSetup(rtcPeerConnection, data);
// Add connection to list of peers
connectingPeers[data.peerId] = rtcPeerConnection;
// Create description from offer received
var offer = new RTCSessionDescription(data.offer);
// Set Description, create answer, send answer to server
rtcPeerConnection.setRemoteDescription(offer, function () {
rtcPeerConnection.createAnswer(function (answer) {
rtcPeerConnection.setLocalDescription(answer);
var answerData = {
peerId: data.peerId,
answer: answer
};
socket.emit('clientSendingAnswer', answerData);
});
});
};
// Receive peer answer from server
var receiveAnswerFromServer = function (data) {
var peerConn = connectingPeers[data.peerId];
var answer = new RTCSessionDescription(data.answer);
peerConn.setRemoteDescription(answer, function () {});
};
// receive ice candidates from server
var receiveIceCandidate = function (data) {
if(data.candidate !== null) {
connectingPeers[data.peerId].addIceCandidate(new RTCIceCandidate(data.candidate));
}
};
// setup ice candidate handling
var iceSetup = function (rtcPeerConnection, data) {
// check if connection has been created
rtcPeerConnection.onicechange = function (evt) {
if(rtcPeerConnection.iceConnectionState === 'connected') {
signalingComplete(rtcPeerConnection);
}
};
// listen for ice candidates and send to server
rtcPeerConnection.onicecandidate = function (iceData) {
var sendingIceInfo = {
peerId: data.peerId,
candidate: iceData.candidate
};
socket.emit('clientSendingIce', sendingIceInfo);
};
};
// //
//////////////////////////////////////////////////
// Return an instance of the Peer class
return new Peer(socket);
};
/*____________________________________________________________________________*/
// // // // //
////////////////////
// Public Methods //
/*****************************************************************************/
var connect = function (options, successCallback, failCallback) {
/*
Options:
"server" - The url + port of the server to connect to
"connector" - A function that is called when the server requests
an RTC offer. Must return an RTCPeerConnction object.
*/
if(typeof options != 'object') {
return false;
}
var socket = io.connect(options.server, {
'force new connection': true
});
socket.on('connect', function () {
var peer;
try {
peer = createPeer(socket, options);
} catch(e) {
failCallback(e);
} finally {
successCallback(peer);
}
});
socket.on('connect_error', function (e) {
failCallback(e);
});
return socket;
};
return {
connect: connect
};
/*____________________________________________________________________________*/
};
// set the signalfire global variable
window.signalfire = signalfire();
}());发布于 2014-04-21 20:47:51
有趣的代码,
我有点惊讶于您将connectToPeer分配给Peer.prototype。在每次调用Peer时,都会创建一个不同的createPeer函数对象,这使得向prototype分配任何内容都是毫无意义的。这也意味着从内存的角度来看,每个连接的对等点都有自己的闭包,所有的代码都在createPeer中。我认为你这样做是因为你想要私人的功能,我不相信这是值得的。
此外,我感到惊讶的是,您在successCallback中调用了finally,这意味着在失败的情况下也会调用它,这是不直观的。
客户端和服务器之间显然有共同的代码,您应该提取该公共代码。
除此之外,您的代码有很好的注释,并且非常容易理解,而且JsHint没有什么可抱怨的。
https://codereview.stackexchange.com/questions/26178
复制相似问题