WebRTC是基于P2P的,通道连接建立好之前,需要服务器帮助传递信令、帮助进行网络穿透
ice(Interactive Connectivity Establishment)交互式连接建立,一种综合性的NAT/FW穿越技术,是一个统一的框架,它整合了各种NAT/FW穿越技术如STUN(发现公网IP,判断路由器行为和防火墙)、TURN(Traversal Using Relay NAT 中继NAT实现的穿透,它在无法进行p2p时提供数据中转服务)。
ICE会先使用STUN,尝试建立一个基于UDP的连接,失败了,就会尝试进行TCP连接(先尝试HTTP,然后尝试HTTPS),如果依旧失败ICE就会使用一个中继的TURN服务器。(ICE即将stun和turn的实现放在一起,实现自主选择NAT/FW服务)
var PeerConnection = window.PeerConnection || window.webkitPeerConnection ||window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.RTCPeerConnection;
WebRTC封装库
1、webrtc.io
https://github.com/webRTC-io/webRTC.io
a: 下载(git clone https://github.com/webRTC-io/webRTC.io 或去网站直接下载)、
进入项目目录,命令行安装依赖模块
npm install webrtc.io
b: 进入项目中site目录,命令行执行(启动服务)
node server.js
c: 浏览器地址栏输入http://localhost:8080(端口号来自server.js),
根据界面菜单进行操作即可
2、SimpleWebRTC
https://github.com/andyet/SimpleWebRTC
a: 进入项目根目录,命令行执行(启动服务)
node server.js
(var app = express(); //express 是 node.js Web应用框架,可设置中间件响应 HTTP 请求
…
https.createServer({key: privateKey, cert: certificate}, app).listen(8000);
// 在server.js中创建 https & http 服务器
http.createServer(app).listen(8001);
)
//浏览器地址栏输入https://localhost:8000/,根据界面菜单进行操作即可
3、ProjectRTC
https://github.com/pchab/ProjectRTC
git clone https://github.com/pchab/ProjectRTC.git
cd ProjectRTC
npm install
node app.js //npm start
浏览器地址栏输入localhost:3000, 根据界面菜单进行操作即可
3、webrtc项目
demo : http://download.csdn.net/detail/keen_zuxwang/9837099
命令行,输入:
express mynodewebrtc
cd mynodewebrtc && npm install
修改app.js(服务器端),内容如下:
var express = require('express'),
app = express(),
server = require('http').createServer(app);
server.listen(3000);
app.get('/', function(req, res) {
res.sendfile(__dirname + '/webrtc.html'); // 客户端html
});
var WebSocketServer = require('ws').Server, // ws 网络通信模块
wss = new WebSocketServer({server: server});
// 存储socket的数组,这里只能有2个socket,每次测试需要重启,否则会出错
var wsc = [],
index = 1;
// wss监听ws接入
wss.on('connection', function(ws) {
console.log('connection');
// 将socket存入数组
wsc.push(ws);
// 记下对方socket在数组中的下标,因为这个测试程序只允许2个socket
// 所以第一个连入的socket存入0,第二个连入的就是存入1
// otherIndex就反着来,第一个socket的otherIndex下标为1,第二个socket的otherIndex下标为0
var otherIndex = index--,
desc = null;
if (otherIndex == 1) {
desc = 'first socket';
} else {
desc = 'second socket';
}
// 转发收到的消息
ws.on('message', function(message) {
var json = JSON.parse(message);
console.log('received (' + desc + '): ', json);
wsc[otherIndex].send(message, function (error) {
if (error) {
console.log('Send message error (' + desc + '): ', error);
}
});
});
});
添加webrtc.html(客户端),内容如下:
<html>
<body>
Local: <br>
<video id="localVideo" autoplay>video><br> // 添加video标签
Remote: <br>
<video id="remoteVideo" autoplay>video>
<script>
// 仅仅用于控制哪一端的浏览器发起offer,#号后面有值的一方发起
var isCaller = window.location.href.split('#')[1];
// 与信令服务器的WebSocket连接
var socket = new WebSocket("ws://127.0.0.1:3000");
// stun和turn服务器
var iceServer = {
"iceServers": [{
"url": "stun:stun.l.google.com:19302"
}, {
"url": "turn:numb.viagenie.ca",
"username": "[email protected]",
"credential": "muazkh"
}]
};
// 创建PeerConnection实例 (参数为null则没有iceserver,即使没有stunserver和turnserver,仍可在局域网下通讯)
var pc = new webkitRTCPeerConnection(iceServer);
// 发送ICE候选到其他客户端
pc.onicecandidate = function(event){
if (event.candidate !== null) {
socket.send(JSON.stringify({
"event": "_ice_candidate",
"data": {
"candidate": event.candidate
}
}));
}
};
// 如果检测到媒体流连接到本地,将其绑定到一个video标签上输出
pc.onaddstream = function(event){
document.getElementById('remoteVideo').src = URL.createObjectURL(event.stream);
};
// 发送offer和answer的函数,发送本地session描述
var sendOfferFn = function(desc){
pc.setLocalDescription(desc);
socket.send(JSON.stringify({
"event": "_offer",
"data": {
"sdp": desc
}
}));
},
sendAnswerFn = function(desc){
pc.setLocalDescription(desc);
socket.send(JSON.stringify({
"event": "_answer",
"data": {
"sdp": desc
}
}));
};
// 获取本地音频和视频流
navigator.webkitGetUserMedia({
"audio": true,
"video": true
}, function(stream){
//绑定本地媒体流到video标签用于输出
document.getElementById('localVideo').src = URL.createObjectURL(stream);
//向PeerConnection中加入需要发送的流
pc.addStream(stream);
//如果是发起方则发送一个offer信令
if(isCaller){
pc.createOffer(sendOfferFn, function (error) {
console.log('Failure callback: ' + error);
});
}
}, function(error){
//处理媒体流创建失败错误
console.log('getUserMedia error: ' + error);
});
//处理到来的信令
socket.onmessage = function(event){
var json = JSON.parse(event.data);
console.log('onmessage: ', json);
//如果是一个ICE的候选,则将其加入到PeerConnection中,否则设定对方的session描述为传递过来的描述
if( json.event === "_ice_candidate" ){
pc.addIceCandidate(new RTCIceCandidate(json.data.candidate));
} else {
pc.setRemoteDescription(new RTCSessionDescription(json.data.sdp));
// 如果是一个offer,那么需要回复一个answer
if(json.event === "_offer") {
pc.createAnswer(sendAnswerFn, function (error) {
console.log('Failure callback: ' + error);
});
}
}
};
script>
body>
html>
开启服务,命令行执行:
node app.js
//使用“npm start”则不带任何控制台提示,打开之后关掉,会自启动, browser测试, 地址栏输入:http://localhost:3000/, 会显示音视频信息、操作菜单、窗口