webRTC【四】1对1场景代码实现整理

今天抽空整理了下 实现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场景代码实现整理