05.webrtc实例

1、项目结构:

 

2、前端:

a、index.html

<!DOCTYPE html>
<html>
    <head>
        <title>
            WebRTC demo
        </title>
    </head>
    <body>
        <h1>
            WebRTC Demo
        </h1>
        <div id="buttons">
            <input id="zero-roomid" type="text" placeholder="请输入房间ID" maxlength="40" />
            <input type="button" id="joinBtn" value="加入" />
            <input type="button" id="leaveBtn" value="离开" />
        </div>
        <div id="videos">
            <video id="localVideo" autoplay muted playsinline>本地窗口</video>
            <video id="remoteVideo" autoplay playsinline>远端窗口</video>
        </div>

        <script type="text/javascript" src="js/main.js"></script>
        <script type="text/javascript" src="js/adapter-latest.js"></script>
    </body>
</html>
View Code

b、main.js

'use strict'

const SIGNAL_TYPE_JOIN = 'join'; //join 主动加入房间
const SIGNAL_TYPE_RESP_JOIN='resp-join';  //告知加入者对方是谁
const SIGNAL_TYPE_LEAVE ='leave';  //主动离开房间
const SIGNAL_TYPE_NEW_PEER ='new-peer';  //有人加入房间,通知已经在房间的人
const SIGNAL_TYPE_PEER_LEAVE ='peer-leave'; //有人离开房间,通知已经在房间的人
const SIGNAL_TYPE_OFFER = 'offer'; //发送offer给对端peer
const SIGNAL_TYPE_ANSWER = 'answer'; // 对端发送回的answer
const SIGNAL_TYPE_CANDIDATE='candidate';  // candidate 发送candidate给对端peer


var localVideo = document.querySelector('#localVideo');
var remoteVideo = document.querySelector('#remoteVideo');
var localStream = null;
var remoteStream = null;
var pc = null;

var localUserId = Math.random().toString(36).substring(2);
var remoteUserId = -1;
var roomId = 0;

var yhRTCEngine;

//
//说明:发送本机candidate到信令服务器
//
function handleIceCandidate(event){
    console.info("handleIceCandidate");
    if(event.candidate){
       // console.info(event.candidate);

        var jsonMsg = {
            'cmd':SIGNAL_TYPE_CANDIDATE,
            'roomId':roomId,
            'uid':localUserId,
            'remoteUid':remoteUserId,
            'msg':JSON.stringify(event.candidate)
        };
        var message = JSON.stringify(jsonMsg);
        yhRTCEngine.sendMessage(message);  //candidate信息发送给信令服务器
        console.info("handleIceCandidate message"+message);
        //console.info("handleIceCandidate message");
    }
    else {
        console.info(event.candidate)
        console.error("end of candidates");
    }
}

//
//说明:显示对端视频
//
function handleRemoteStreamAdd(event){
    console.info("handleRemoteStreamAdd");
    remoteStream = event.streams[0];
    remoteVideo.srcObject = remoteStream;
}

function handleConnectionStateChange(){
    if(pc != null){
        console.info("connectionState->"+pc.connectionState);
    }


}

function handleIceConnectionStateChange(){
    if(pc != null){
        console.info("IceConnectionState->"+pc.iceConnectionState);
    }

}
//
//说明:创建RTCPeerConnection(由本地计算机到远端的WebRTC连接)
//
function createPeerConnection(){
    console.info("createPeerConnection")
    var defaultConfiguration = {
        bundlePolicy:"max-bundle",
        rtcpMuxPolicy:'require',
        iceTransportPolicy:'all',  //relay只能中继,all可以p2p
        iceServers:[
            {
                "urls":[
                    "turn:192.168.0.5:3478?transport=udp",
                    "turn:192.168.0.5:3478?transport=tcp"
                ],
                "username":'ziff',
                "credential":"123456"
            },
            {
                "urls":[
                    "stun:192.168.0.5:3478"
                ]
            }
        ]
    }
    //1.创建RTCPeerConnection
    //pc = new RTCPeerConnection(defaultConfiguration);
    pc = new RTCPeerConnection();
    //2.绑定事件
    pc.onicecandidate = handleIceCandidate;  //发送本机candidate到信令服务器
    pc.ontrack = handleRemoteStreamAdd;  //显示对端视频
    pc.onconnectionstatechange = handleConnectionStateChange;
    pc.oniceconnectionstatechange = handleIceConnectionStateChange

    //3.加入本地视频流
    localStream.getTracks().forEach(track=>{
        pc.addTrack(track,localStream);
    })

}

//
//说明:设置本机的SDP信息添加到PeerConnection,并将信息发送给信令服务器,通知对端客户端B
//
function createOfferAndSendMessage(session){
    //1.设置本机的SDP信息添加到PeerConnection
    pc.setLocalDescription(session)
    .then(()=>{
        var jsonMsg = {
            'cmd':SIGNAL_TYPE_OFFER,
            'roomId':roomId,
            'uid':localUserId,
            'remoteUid':remoteUserId,
            'msg':JSON.stringify(session)  //本机的sdp信息
        };
        var message = JSON.stringify(jsonMsg);
        //2.将sdp信息发送给信令服务器,通知对端客户端B
        yhRTCEngine.sendMessage(message);  //发送到信令服务器
        //console.info("send offer message:"+message);
        console.info("send offer message:");
    })
    .catch(error=>{
        console.error("offer setLocalDiscription failed:"+error);
    });

}

//
//处理offer异常
//
function handleCreateOfferError(error){
    console.error("handleCreateOfferError:"+error);
}

//
//说明:用户 B 创建一个 Answer,并添加到 PeerConnection 中(setLocalDescription);
//用户 B 通过信令服务器将 answer 转发给用户 A;
function createAnswerAndSendMessage(session){
    //1.设置本机的SDP信息添加到PeerConnection
    console.info("session")
    console.info(session)
    pc.setLocalDescription(session)
    .then(()=>{
        var jsonMsg = {
            'cmd':SIGNAL_TYPE_ANSWER,
            'roomId':roomId,
            'uid':localUserId,
            'remoteUid':remoteUserId,
            'msg':JSON.stringify(session)  //B端本机的sdp信息
        };
        var message = JSON.stringify(jsonMsg);
        //2.将sdp信息发送给信令服务器,通知对端客户端A
        yhRTCEngine.sendMessage(message);  //发送到信令服务器
        //console.info("send answer message:"+message);
        console.info("send answer message:");
    })
    .catch(error=>{
        console.error("answer setLocalDiscription failed:"+error);
    });
}

//
//处理answer异常
//
function handleCreateAnswerError(error){
    console.error("handleCreateAnswerError:"+error);
}


var YHRTCEngine = function(wsUrl){
    this.init(wsUrl);
    yhRTCEngine = this;
    return this;
}

YHRTCEngine.prototype.init = function(wsUrl){
    //设置websocket url
    this.wsUrl = wsUrl;
    //websocket对象
    this.signaling = null;
}

YHRTCEngine.prototype.createWebSocket = function(){
    yhRTCEngine = this;
    yhRTCEngine.signaling = new WebSocket(this.wsUrl);   //websocket对象

    yhRTCEngine.signaling.onpen = function(){   //连接websocket
        yhRTCEngine.onOpen();
    }

    yhRTCEngine.signaling.onmessage = function(event){   //监听客户端信息
        yhRTCEngine.onMessage(event);
    }

    yhRTCEngine.signaling.onerror = function(){
        yhRTCEngine.onError();
    }

    yhRTCEngine.signaling.onclose = function(){
        yhRTCEngine.onClose();
    }
}


YHRTCEngine.prototype.onOpen = function(){
    console.log("websocket onopen");

}

//
//说明:接收信令服务器返回信息
//
YHRTCEngine.prototype.onMessage = function(evet){
    console.log("websocket onMessage");
    //console.log(evet.data);
    var jsonMsg;
    try
    {
        jsonMsg = JSON.parse(evet.data);
    }
    catch(e){
        console.warn("onMessage "+e);
        return;
    }
    switch(jsonMsg.cmd){
        case SIGNAL_TYPE_NEW_PEER:  //1.有新的人加入房间,create peerConnect,send offer
            handleRemoteNewPeer(jsonMsg);
            break;
        case SIGNAL_TYPE_RESP_JOIN:  //通知新进来的用户,房间原来的用户信息
            handleResponseJoin(jsonMsg);
            break;    
        case SIGNAL_TYPE_PEER_LEAVE:   //有人离开房间,告知房间里的人
            handleRemotePeerLeave(jsonMsg);
            break;
        case SIGNAL_TYPE_OFFER:  //接收对端offer
            handleRemoteOffer(jsonMsg);
            break;
        case SIGNAL_TYPE_ANSWER: //接收对端发送回的answer
            handleRemoteAnswer(jsonMsg);
            break;
        case SIGNAL_TYPE_CANDIDATE:  //用户 A 和 用户 B 都接收到了 candidate 信息后,都通过信令服务器转发给对方并添加到 PeerConnection 中(addIceCandidate);
            handleRemoteCandidate(jsonMsg);
            break;
    }
}

YHRTCEngine.prototype.onError = function(event){
    console.log("websocket onError");

}

YHRTCEngine.prototype.onClose = function(event){
    console.log("websocket onClose"+event.code+", reason:"+EventTarget.reason);

}

YHRTCEngine.prototype.sendMessage = function(message){
    this.signaling.send(message);

}

yhRTCEngine = new YHRTCEngine("ws://127.0.0.1:3000");
//yhRTCEngine = new YHRTCEngine("ws://192.168.0.5:3000");
yhRTCEngine.createWebSocket();


//
//说明:将新进来的用户信息通知房间原来已经在里面的用户
//
function handleRemoteNewPeer(message){
    console.info("handleRemoteNewPeer, remoteUid:"+message.remoteUid);
    remoteUserId = message.remoteUid;

    doOffer();
}

//
//说明:通知新进来的用户,房间原来的用户信息
//
function handleResponseJoin(message){
    console.info("handleResponseJoin, remoteUid:"+message.remoteUid);
    remoteUserId = message.remoteUid;
}

//
//说明:收到有人离开
//
function handleRemotePeerLeave(message){
    console.info("handleRemotePeerLeave, remoteUid:"+message.remoteUid);
    remoteVideo.srcObject = null;  //关闭对方视频
    if(pc !=null){
        pc.close();
        pc = null;
    }

}

//
//说明:处理对端A传过来的offer
//
function handleRemoteOffer(message){
    console.info("handleRemoteOffer");
    if(pc == null){
        //1.创建PeerConnection
        createPeerConnection();
    }
    var desc = JSON.parse(message.msg);
    //2.将对端的SDP信息添加到 PeerConnection 中(setRemoteDescription)
    pc.setRemoteDescription(desc);
    //3.用户 B 创建一个 Answer,并添加到 PeerConnection 中(setLocalDescription);通过信令服务器将 answer 转发给用户 A
    doAnswer();
}


//
//说明:处理对端B发送回来的answer
//用户 A 接收到 answer 后将其添加到 PeerConnection 中;
//
function handleRemoteAnswer(message){
    console.info("handleRemoteAnswer");
    var desc = JSON.parse(message.msg);
    pc.setRemoteDescription(desc);  //设置对端B的SDP信息

}

//
//说明:对端在创建PeerConnection会获取candidate信息并发给信令服务器
//将对方的Candidate添加到 PeerConnection 中(addIceCandidate);
//
function handleRemoteCandidate(message){
    console.info("handleRemoteCandidate");
    var candidate =JSON.parse(message.msg);
    pc.addIceCandidate(candidate).catch(e=>{
        console.error("addIceCandidate failed:"+e.name);
    })
}

//
//用户 A 作为发起方创建 offer(offer 中包含了 SDP 信息),
//并将获取的本地 SDP 信息添加到 PeerConnection 中(setLocalDescription),
//然后再通过信令服务器转发给用户 B;
//
function doOffer(){
    //1.创建RTCPeerConnection
    if(pc == null){
        createPeerConnection();
    }
    pc.createOffer().then(createOfferAndSendMessage).catch(handleCreateOfferError);
}

//
//说明:用户 B 创建一个 Answer,并添加到 PeerConnection 中(setLocalDescription);
//用户 B 通过信令服务器将 answer 转发给用户 A
//用户 A 接收到 answer 后将其添加到 PeerConnection 中;
//
function doAnswer(){
    pc.createAnswer().then(createAnswerAndSendMessage).catch(handleCreateAnswerError);
}

//
//加入
//roomId:房间号
//
function doJoin(roomId){
    var jsonMsg = {
        'cmd':SIGNAL_TYPE_JOIN,
        'roomId':roomId,
        'uid':localUserId
    };

    var message = JSON.stringify(jsonMsg);
    yhRTCEngine.sendMessage(message);
    console.info("发送给服务端信息:"+message);

}

//
//说明:离开房间
//
function doLeave(){
    var jsonMsg = {
        'cmd':SIGNAL_TYPE_LEAVE,
        'roomId':roomId,
        'uid':localUserId
    }

    var message = JSON.stringify(jsonMsg);
    yhRTCEngine.sendMessage(message);   //通知信令服务器,有人离开
    console.info("doLeave message:"+message);

    hangup();  //挂断
}

//
//说明:挂断
//
function hangup(){
    localVideo.srcObject = null;   //0.关闭自动
    remoteVideo.srcObject = null;  //1.不显示对方
    closeLocalStream();   //2.关闭本地

    if(pc !=null){
        pc.close();   //3.关闭RTCPeerConnection
        pc = null;
    }
}

function closeLocalStream(){
    if(localStream != null){
        localStream.getTracks().forEach(track=>{
            track.stop();
        })
    }
}
//打开本地视频
function openLocalStream(stream){
    console.log(stream)
    doJoin(roomId);
    localVideo.srcObject = stream;   //页面显示本地视频
    localStream = stream;
}

function initLocalStream(){
    navigator.mediaDevices.getUserMedia({
        audio:true,
        video:true
    }).then(openLocalStream)
    .catch(e=>{
        alert("getUserMedia() error:"+e);
    })
}

document.getElementById('joinBtn').onclick= function(){
    console.log('加入按钮被点击');
    roomId = document.getElementById("zero-roomid").value;

    if(roomId == "" || roomId == "请输入房间ID"){
        alert('请输入房间ID');
        return;
    }
    //初始化本地码流
    initLocalStream();
}

//离开
document.getElementById('leaveBtn').onclick= function(){
    console.log('离开按钮被点击');
    
    doLeave();
}
View Code

 

3、后端:

signal_server.js

var ws = require('nodejs-websocket')
var port = 3000

const SIGNAL_TYPE_JOIN = 'join'; //join 主动加入房间
const SIGNAL_TYPE_RESP_JOIN='resp-join';  //告知加入者对方是谁
const SIGNAL_TYPE_LEAVE ='leave';  //主动离开房间
const SIGNAL_TYPE_NEW_PEER ='new-peer';  //有人加入房间,通知已经在房间的人
const SIGNAL_TYPE_PEER_LEAVE ='peer-leave'; //有人离开房间,通知已经在房间的人
const SIGNAL_TYPE_OFFER = 'offer'; //发送offer给对端peer
const SIGNAL_TYPE_ANSWER = 'answer'; // 发送offer给对端peer
const SIGNAL_TYPE_CANDIDATE='candidate';  // candidate 发送candidate给对端peer


var YHRTCMap = function(){
    this._entrys = new Array();

    this.put = function (key,value){
        if(key == null || key == undefined){
            return;
        }
        var index = this._getIndex(key);
        if(index == -1){
            var entry = new Object();
            entry.key = key;
            entry.value = value;
            this._entrys[this._entrys.length] = entry;  //添加
        }
        else {
            this._entrys[index].value = value;  //覆盖原来的值
        }
    }

    this.get = function(key){
        var index = this._getIndex(key);
        return (index != -1) ? this._entrys[index].value : null;
    }

    this.remove = function(key){
        var index = this._getIndex(key);
        if(index != -1){
            this._entrys.splice(index,1);
        }
    }

    this.clear = function(){
        this._entrys.length = 0;
    }

    this.contains = function(key){
        var index = this._getIndex(key);
        return (index != -1) ? true : false;
    }

    this.size = function(){
        return this._entrys.length;
    }

    this.getEntrys = function(){
        return this._entrys;
    }

    this._getIndex = function(key){
        if(key == null || key == undefined){
            return -1;
        }
        var _length = this._entrys.length;
        for(var i = 0; i < _length; i++){
            var entry = this._entrys[i];
            if(entry == null || entry == undefined){
                continue;
            }
            if(entry.key === key){
                return i;
            }
        }
        return -1;
    }
}

var roomTableMap = new YHRTCMap();

//
//说明:客户端信息
//
function Client(uid,conn,roomId){
    this.uid = uid;  //用户所属的id
    this.conn = conn; // uid对应的websocket连接
    this.roomId = roomId;
}

//
//说明:客户端新加进来操作
//参数:mesage客户端的信息:roomId房间号,uid用户ID
//conn客户端websocket链接
function handleJoin(message,conn){
    var roomId = message.roomId;   //房间号
    var uid = message.uid;   //用户ID

    console.log('uid:'+uid+' try to join romm '+roomId);
    //1.这个房间号是否已创建
    var roomMap = roomTableMap.get(roomId);  
    if(roomMap == null){
        roomMap = new YHRTCMap();   //没有则创建
        roomTableMap.put(roomId,roomMap);
    }

    //2.房间已满
    if(roomMap.size() >= 2){
        console.error("roomId:"+roomId +"已经有两人存在,请使用其它房间");
        //通知客户端,房间已满

        return null;
    }

    //3.添加用户信息到房间
    var client = new Client(uid,conn,roomId);  //客户端信息
    roomMap.put(uid,client);  //添加用户信息到房间
    //4./房间里面已经有人,通知对方
    if(roomMap.size() > 1){
        //房间里面已经有人,通知对方
        var clients = roomMap.getEntrys();
        for(var i in clients){
            var remoteUid = clients[i].key;  //客户端(对端的)uid
            if(remoteUid != uid){  //跳过自己
                var jsonMsg = {
                    'cmd':SIGNAL_TYPE_NEW_PEER,
                    'remoteUid':uid  //新进来的用户ID
                }
                var msg = JSON.stringify(jsonMsg);
                var remoteClient = roomMap.get(remoteUid);
                console.info('new-peer:'+msg);
                remoteClient.conn.sendText(msg);   //将新进来的用户信息通知房间原来已经在里面的用户

                jsonMsg = {
                    'cmd':SIGNAL_TYPE_RESP_JOIN,
                    'remoteUid':remoteUid  //房间原来在里面的用户ID
                }
                msg = JSON.stringify(jsonMsg);
                console.info('resp-join:'+msg);
                conn.sendText(msg); // 通知新进来的用户,房间原来的用户信息
            }
        }
    }
    return client;
}

//
//说明:客户端有人离开
//
function handleLeave(message){
    var roomId = message.roomId;
    var uid = message.uid;

    console.info("uid:"+uid+"try to leave room "+roomId);
    //1.获取所在房间号
    var roomMap = roomTableMap.get(roomId);
    if(roomMap == null){
        console.error("can't find the roomId "+roomId);
        return;
    }
    //2.删除用户
    roomMap.remove(uid);  //删除房间用户
    //3.通知其它人,有人离开
    if(roomMap.size() >= 1){
        var clients = roomMap.getEntrys();  //该房间所有客户端信息
        for(var i in clients){
            var jsonMsg = {
                'cmd':SIGNAL_TYPE_PEER_LEAVE,
                'remoteUid':uid
            }
            
            var msg = JSON.stringify(jsonMsg);
            var remoteUid = clients[i].key;
            //console.info("查看下客户端信息");
            //console.info(clients[i].value);
            var remoteClient = clients[i].value; // roomMap.get(remoteUid);  //获取客户端信息
            //console.info(remoteClient);
            if(remoteClient){
                console.info("notify peer :"+ remoteClient.uid+" uid:"+uid+" leave");
                remoteClient.conn.sendText(msg);
            }
        }
    }
}

//
//说明:断开连接,强制用户离开房间
//
function handleForceLeave(client){
    var roomId = client.roomId;
    var uid = client.uid;

   
    //1.获取所在房间号
    var roomMap = roomTableMap.get(roomId);
    if(roomMap == null){
        console.warn("can't find the roomId "+roomId);
        return;
    }

    //2.判断uid是否在房间
    if(!roomMap.contains(uid)){
        console.warn("uid:"+uid +" have leave roomId "+roomId);
        return;
    }

    console.info("uid:"+uid+" force leave room "+roomId);
    //2.删除用户
    roomMap.remove(uid);  //删除房间用户
    //3.通知其它人,有人离开
    if(roomMap.size() >= 1){
        var clients = roomMap.getEntrys();  //该房间所有客户端信息
        for(var i in clients){
            var jsonMsg = {
                'cmd':SIGNAL_TYPE_PEER_LEAVE,
                'remoteUid':uid
            }
            
            var msg = JSON.stringify(jsonMsg);
            var remoteUid = clients[i].key;
            //console.info("查看下客户端信息");
            //console.info(clients[i].value);
            var remoteClient = clients[i].value; // roomMap.get(remoteUid);  //获取客户端信息
            //console.info(remoteClient);
            if(remoteClient){
                console.info("notify peer :"+ remoteClient.uid+" uid:"+uid+" leave");
                remoteClient.conn.sendText(msg);
            }
        }
    }
}

//
//说明:处理客户端发送过来的offer,将其转发给对端B
//
function handleOffer(message){
    var roomId = message.roomId;
    var uid = message.uid; //自己用户ID
    var remoteUid = message.remoteUid;  //对端用户ID

    console.info("handleOffer uid:"+uid+" transfer offer to remoteUid "+remoteUid);

    var roomMap = roomTableMap.get(roomId); //获取房间信息
    if(roomMap == null){
        console.error("handleOffer can't find the roomId "+roomId);
        return;
    }

    if(roomMap.get(uid) == null){
        console.error("handleOffer can't find the uid "+uid);
        return;
    }

    var remoteClient = roomMap.get(remoteUid);  //对端用户信息
    if(remoteClient){
        var msg = JSON.stringify(message);
        remoteClient.conn.sendText(msg);   //转发给对端客户端含SDP信息
    }
    else{
        console.error("can't find remoteUid "+remoteUid);
    }
}


//
//说明:
//
function handleAnswer(message){
    var roomId = message.roomId;
    var uid = message.uid; //自己用户ID
    var remoteUid = message.remoteUid;  //对端用户ID

    console.info("handleAnswer uid:"+uid+" transfer answer to remoteUid "+remoteUid);

    var roomMap = roomTableMap.get(roomId); //获取房间信息
    if(roomMap == null){
        console.error("handleAnswer can't find the roomId "+roomId);
        return;
    }

    if(roomMap.get(uid) == null){
        console.error("handleAnswer can't find the uid "+uid);
        return;
    }

    var remoteClient = roomMap.get(remoteUid);  //对端用户信息
    if(remoteClient){
        var msg = JSON.stringify(message);
        //console.info(msg);
        remoteClient.conn.sendText(msg);   //转发给对端
    }
    else{
        console.error("can't find remoteUid "+remoteUid);
    }
}


//
//说明:
//
function handleCandidate(message){
    var roomId = message.roomId;
    var uid = message.uid; //自己用户ID
    var remoteUid = message.remoteUid;  //对端用户ID

    console.info("handleCandidate uid:"+uid+" transfer candidate to remoteUid "+remoteUid);

    var roomMap = roomTableMap.get(roomId); //获取房间信息
    if(roomMap == null){
        console.error("handleCandidate can't find the roomId "+roomId);
        return;
    }

    if(roomMap.get(uid) == null){
        console.error("handleCandidate can't find the uid "+uid);
        return;
    }

    var remoteClient = roomMap.get(remoteUid);  //对端用户信息
    if(remoteClient){
        var msg = JSON.stringify(message);
        //console.info("remoteUid:"+remoteUid+"  uid:"+uid);
        remoteClient.conn.sendText(msg);   //转发给对端
    }
    else{
        console.error("can't find remoteUid "+remoteUid);
    }
}


var server = ws.createServer(conn=>{
    console.log("创建一个新的连接....");

    //conn.sendText("我是服务端,收到你客户端的连接了");
    conn.client = null;

    conn.on('text',str=>{
       // console.info("recv msg:"+str);
        var jsonMsg = JSON.parse(str);

        switch(jsonMsg.cmd){
            case SIGNAL_TYPE_JOIN:  
                conn.client = handleJoin(jsonMsg,conn);
                break;
            case SIGNAL_TYPE_LEAVE:
                handleLeave(jsonMsg);
                break;
            case SIGNAL_TYPE_OFFER:
                handleOffer(jsonMsg);
                break;
            case SIGNAL_TYPE_ANSWER:
                handleAnswer(jsonMsg);
                break;
            case SIGNAL_TYPE_CANDIDATE:
                handleCandidate(jsonMsg);
                break;
        }
    });

    conn.on("close",(code,reason)=>{
        console.info("连接关闭 code:"+code+",reason:"+reason);
        if(conn.client != null){
            handleForceLeave(conn.client);   //强制离开
        }
    });

    conn.on("error",err=>{
        console.info("监听错误:"+err);
    });

}).listen(port);
View Code

 

posted @ 2023-09-11 17:41  ziff123  阅读(20)  评论(0编辑  收藏  举报