今天抽空整理了下 实现webrtc的代码部分
- 服务端使用 ws模块 搭建websocket 服务,这个以前就研究过搭建了 websocket 服务,地址 http://link.ibeeger.com/
const WebSocket = require('ws')
const query = require('querystring')
const path = require('path')
const down = 11; /*每道题倒计时*/
const clients = {}
const roomList = {};
const rtcinfo = {}
const getQuery = function(url) {
let _query = url.split('?')[1]
let obj = query.parse(_query);
return obj
}
const wss = new WebSocket.Server({
port: 9191,
verifyClient: function(info, done) {
console.log(info.res);
let obj = getQuery(info.req.url);
if (!obj.cid) {
done(false)
return
}
let num = wss.clients.size || wss.clients.length;
if (num >= 500) {
done(false)
return
}
done(true)
}
});
wss.on('connection', function connection(ws, req) {
console.log(req.connection.remoteAddress)
ws.rid = getQuery(req.url).rid
ws.cid = getQuery(req.url).cid
ws.ip = req.connection.remoteAddress;
sendOnlineNum()
ws.on('message', function(data) {
// console.log(ws.cid + ':', data)
data = JSON.parse(data);
let msg = data['data'];
let type = data['type'];
console.log(ws.rid +'==='+ ws.cid + ':' + type);
if (type == 'TOALL') {
ctrlMessage(msg, ws)
} else {
console.log('个人', data['to'])
ctrlSignalMessage(msg, data['to'], ws);
}
})
ws.on('close', function() {
if (ws.rid) {
deleteUser(ws.rid, ws)
}
delete rtcinfo[ws.cid];
ws.terminate()
sendOnlineNum()
console.log(ws.rid, ws.cid, 'close')
})
});
//处理指向信令
function ctrlSignalMessage(msg, toid, ws) {
for(let i = 0; i<clients[ws.rid].length; i++) {
if (clients[ws.rid][i]['cid'] == toid) {
clients[ws.rid][i].send(JSON.stringify({
code: 3,
data: msg,
from: ws.cid
}))
break;
}
}
}
//处理toall信令
function ctrlMessage(msg, ws) {
switch (msg.type) {
case 'join':
sendInit(ws, msg);
break;
case 'call':
sendToRoom(ws.rid, {
code: 1,
data: msg.data
}, ws);
rtcinfo[ws.cid] = {
call: msg.data
}
break;
case 'answer':
sendToRoom(ws.rid, {
code: 2,
data: msg.data
}, ws);
rtcinfo[ws.cid]['answer'] = msg.data;
break;
case 'idcard':
sendToRoom(ws.rid,{
code: 3,
data: msg.data,
is: msg.self
}, ws)
rtcinfo[ws.cid]['card'] = msg.data;
break;
case 'watch':
ws.send(JSON.stringify({code: 100, rtcinfo}));
break;
}
}
/**进来房间**/
function sendInit(ws, msg) {
let list = []
let ofas = []
if (clients[ws.rid]) {
clients[ws.rid].push(ws);
} else {
clients[ws.rid] = [ws];
}
clients[ws.rid].forEach(function(item) {
list.push(item.cid);
let obj = {};
if (item.offer) {
obj['offer'] = item.offer;
}
if (item.answer) {
obj['answer'] = item.answer;
}
ofas.push(obj);
})
if (list.length>1) {
sendToRoom(ws.rid,{
code: 101
})
}
}
function sendOnlineNum() {
if (wss.clients.size || wss.clients.length) {
wss.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({
code: 10000,
data: wss.clients.length || wss.clients.size
}));
}
});
}
};
/*断开用户处理*/
function deleteUser(rid, ws) {
if (!rid) {
return
}
if (clients[rid]) {
if (clients[rid].length == 1) {
delete clients[rid]
} else {
for(let i = 0; i<clients[rid].length; i++) {
if (ws.cid == clients[rid][i]['cid']) {
clients[rid].splice(i,1);
sendToRoom(rid, {
code: 400,
data: ws.cid
})
break;
}
}
}
}
}
/*发送消息处理*/
function sendToRoom(rid, msg, sp) {
console.log('sendToRoom', rid, clients[rid].length);
clients[rid].forEach(function(ws) {
if (ws.readyState == WebSocket.OPEN) {
if (!sp) {
ws.send(JSON.stringify(msg))
} else {
if (sp.cid != ws.cid) {
ws.send(JSON.stringify(Object.assign(msg, {
from: sp.cid
})))
}
}
}
})
}
- 前端代码部分
(function() {
var cid = location.hash.replace('#', '') || Date.now().toString(32);
var wsclient = new WebSocket('ws://192.168.1.146:9191/?token=e285d09f8704d466c4f5207399455175b7d23a1f&rid=webrtc&cid=' + cid);
var server = null;
var self = new RTCPeerConnection(server);
var remote = new RTCPeerConnection(server);
var main = document.getElementById('main');
var selfvideo = document.createElement('video');
var remotevideo = document.createElement('video');
// remotevideo.controls = true;
remotevideo.id = "remotevideo";
selfvideo.id = "selfvideo";
var setanswer = false; //self设置过answer
var remoteset = false; //remoteset
var hascall = false;
var iswatch = location.search.indexOf('watch') > -1;
var hasSendSelfCard = false,
hasSendRemoteCard = false;
self.addEventListener('icecandidate', getIceCand, false);
remote.addEventListener('icecandidate', getIceCand, false);
remote.addEventListener('addstream', getStreamRtc, false);
const log = function(arg){
console.log(arg, Date.now());
}
async function initMedia() {
var stream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: true
});
selfvideo.autoplay = true;
// selfvideo.controls = true;
selfvideo.srcObject = stream;
self.addStream(stream);
main.appendChild(selfvideo);
main.appendChild(remotevideo);
sendMessage({
type: 'join'
});
}
function getIceCand(e) {
const iceCandidate = e.candidate;
if (iswatch) {
if (iceCandidate) {
if (e.target == remote) {
log('处理 getIceCand', e.target == remote);
if (!hasSendRemoteCard) {
sendMessage({
type: 'idcard',
data: iceCandidate,
self: false
});
hasSendRemoteCard = true;
}
}
}
return
} else {
if (iceCandidate) {
if (e.target == remote) {
if (!hasSendRemoteCard) {
sendMessage({
type: 'idcard',
data: iceCandidate,
self: false
});
hasSendRemoteCard = true;
}
} else {
if (!hasSendSelfCard) {
sendMessage({
type: 'idcard',
data: iceCandidate,
self: true
});
hasSendSelfCard = true;
}
}
}
}
}
function getStreamRtc(e) {
log('获取流', e.stream)
if (e.stream) {
remotevideo.srcObject = e.stream;
remotevideo.autoplay = true;
}
}
/**信令建立**/
function socketOpen() {
if (iswatch) {
main.appendChild(remotevideo);
sendMessage({
type: 'join'
});
} else {
initMedia()
}
}
//接受socket消息
function socketMessage(rec) {
let rcdata = JSON.parse(rec.data);
switch (rcdata.code) {
case 100: //进来之后初始化
// showOnlineUser(rcdata.data, rcdata.sdps);
break;
case 400: //掉线后移除掉线用户结构
// deleteUser(rcdata.data);
break;
case 101:
DoCall();
break;
case 1:
CtrlCall(rcdata);
break;
case 2:
CtrlAnswer(rcdata);
break;
case 3:
CtrlIceCard(rcdata);
break;
}
}
//后进来docall
async function DoCall() {
if (iswatch) {
return;
}
try {
let offersession = await self.createOffer();
self.setLocalDescription(offersession, function() {
sendMessage({
type: 'call',
data: offersession
})
}, errlog);
} catch (e) {
log('createOffer err', e);
}
log('处理呼叫')
}
//接受
async function CtrlCall(data) {
log('处理 offer');
if (!remoteset) {
remote.setRemoteDescription(new RTCSessionDescription(data.data));
}
try {
let answer = await remote.createAnswer();
remote.setLocalDescription(answer, function() {
sendMessage({
type: 'answer',
data: answer
})
}, errlog)
} catch (e) {
}
}
//处理回答
function CtrlAnswer(data) {
log('处理 answer')
if (!setanswer) {
self.setRemoteDescription(new RTCSessionDescription(data.data));
}
}
//处理ice
function CtrlIceCard(rcdata) {
log('CtrlIceCard', rcdata.is);
if (!rcdata.is) {
self.addIceCandidate(new RTCIceCandidate(rcdata.data))
} else {
remote.addIceCandidate(new RTCIceCandidate(rcdata.data))
}
}
wsclient.addEventListener('open', socketOpen, false);
wsclient.addEventListener('message', socketMessage, false);
wsclient.addEventListener('close', socketError, false);
wsclient.addEventListener('error', socketError, false);
function socketError(e) {
log('socketError', e)
}
function errlog(e) {
log('errlog', e)
}
function sendMessage(message) {
wsclient.send(JSON.stringify({
type: 'TOALL',
data: message
}));
};
window.onbeforeunload = function() {
self.close();
remote.close();
}
})();
文章来源: webRTC【四】1对1场景代码实现整理