为什么WebRTC断开连接后无法重新连接?

3
我正在使用React构建一个Web应用程序,用户可以在其中输入组呼。我有一个NodeJS服务器运行Socket.IO来管理客户端事件,用户通过简单同行(WebRTC)进行点对点连接。
一切都正常,从加入呼叫、看到其他用户以及能够离开。该呼叫“始终打开”,类似于discord,用户可以自由进出。但是,如果您离开然后尝试重新加入呼叫而不刷新页面,则所有方面都会中断该呼叫。尝试重新加入的用户会出现以下错误:
“错误:对等体被销毁后无法发送信号”
对于呼叫中的另一个用户,它会多次记录“用户已加入”事件,针对尝试重新加入的一个用户。之前也会添加多个对等体,但是现在我确保不存在重复项。
然而,对我来说最奇怪的是,当用户离开时,他们向房间中的所有其他用户发出呼叫。其他用户成功地销毁了对等连接,然后将该用户从其对等数组中删除。在他的回合中离开的用户还会销毁每个连接并将对等数组重置为空数组。因此,我很困惑它正在尝试重新建立哪个PTP连接。
const [roomSize, setRoomSize] = useState(0);

const socketRef = useRef();
const userVideo = useRef();
const peersRef = useRef([]);

const roomId = 'meeting_' + props.zoneId;

useEffect(async () => {
    socketRef.current = io.connect(SERVER_URI, {
        jsonp: false,
        forceNew: true,
        extraHeaders: {
            "x-access-token": window.localStorage.getItem('accessToken'),
            "zone-id": props.zoneId
        }
    });
}, []);
useEffect(async () => {
    if (props.active) {
        try {
            const stream = await navigator.mediaDevices.getUserMedia({ 
                video: {
                    height: window.innerHeight / 2,
                    width: window.innerWidth / 2
                }, 
                audio: true });
            userVideo.current.srcObject = stream;
            console.log(`%cJoined socket at ${SERVER_URI}, connected=${socketRef.current.connected}`, 'color: pink');

            socketRef.current.emit("join room", roomId);

            socketRef.current.on("all users", users => {
                users.forEach(userID => {
                    const peer = createPeer(userID, socketRef.current.id, stream);
                    const peerObj = {
                        peerID: userID,
                        peer,
                    };
                    if (!peersRef.current.find(p => p.peerID === userID))
                        peersRef.current.push(peerObj);
                });
                setRoomSize(peersRef.current.length);
                console.log(`%cNew Room Members: ${peersRef.current.length}; %o`, 'color: cyan', {peersRef: peersRef.current});
            })

            socketRef.current.on("user joined", payload => {
                const peer = addPeer(payload.signal, payload.callerID, stream);
                const peerObj = {
                    peerID: payload.callerID,
                    peer,
                };
                if (!peersRef.current.find(p => p.peerID === payload.callerID))
                    peersRef.current.push(peerObj);
                setRoomSize(peersRef.current.length);
                console.log(`%cSomeone Joined. Members: ${peersRef.current.length}; %o`, 'color: cyan', {peersRef: peersRef.current});
            });

            socketRef.current.on("receiving returned signal", payload => {
                /** @type {Peer} */
                const item = peersRef.current.find(p => p.peerID === payload.id);
                item.peer.signal(payload.signal);
                console.log("%creceiving return signal", 'color: lightgreen');
            });

            socketRef.current.on('user left', id => {
                const peerObj = peersRef.current.find(p => p.peerID === id);
                // console.log("user left", { peerObj });
                if (peerObj)
                    peerObj.peer.destroy();
                const peers = peersRef.current.filter(p => p.peerID !== id);
                peersRef.current = peers;
                setRoomSize(peersRef.current.length);
                console.log(`%cSomeone Left. Members: ${peersRef.current.length}`, 'color: cyan');
            });
        } catch (err) {
            console.trace(err);
        }
    }
    else if (socketRef.current && socketRef.current.connected) {

        socketRef.current.emit("leave room");
        peersRef.current.forEach(peerObj => {
            peerObj.peer.destroy();
        });
        peersRef.current = [];
        setRoomSize(peersRef.current.length);
    }
}, [props.active, peersRef.current]);

const createPeer = (userToSignal, callerID, stream) => {
    const peer = new Peer({
        initiator: true,
        trickle: false,
        stream,
    });
    peer.on("signal", signal => {
        socketRef.current.emit("sending signal", { userToSignal, callerID, signal })
    })
    return peer;
}

const addPeer = (incomingSignal, callerID, stream) => {
    const peer = new Peer({
        initiator: false,
        trickle: false,
        stream,
    })
    peer.on("signal", signal => {
        socketRef.current.emit("returning signal", { signal, callerID })
    })
    peer.signal(incomingSignal);
    return peer;
}

快速编辑:上面的代码是React组件的一部分,它为每个同行者渲染一个视频元素。

props.active变为false时,用户离开通话。这发生在第二个useEffect钩子的结尾处,在此之后,离开的客户端应该已经销毁并删除了所有同行者对象。为什么重新连接时此用户会接收到上述错误?如何避免出现此错误?

编辑:我刚注意到,当两个用户都离开通话,并且没有刷新而尝试重新加入时,错误就不会发生。所以,我的猜测是在一个用户离开与自己离开相比移除同行者时有所不同。


1
有没有托管在某个地方的演示版本,以便查看其实际运行情况? - vijay krishna
1个回答

2

TLDR; 将useEffect中使用的所有引用都放在useEffect deps数组中。


首先,我要确保检查useEffect deps数组。看起来socketRef在hook体中多个地方都是必需的,但它似乎没有出现在deps数组中。这可能会导致hook使用不够最新的数据。

也正因为如此,socketRef引用对象实际上可能永远不会更新,这意味着它可能会正确地从对等体中删除用户,因为peerRefsuseEffect deps数组中,但内部会话(房间)可能不会认识到这一点;房间对用户的内部表示仍然存在。

重复一遍,只是为了让它更清楚,你提到:

所以当一个用户离开时,将一个对等体移除与自己离开相比会有所不同,这是我最好的猜测。

这与上面列出的原因相同。当对等体离开时发生这种情况,是因为peerRefs引用对象位于useEffect deps数组中,所以你描述的效果只是“完美的时机”,因为应用程序状态(所有引用)与彼此正确同步。


1
一定会尝试这个。由于时间不多,您肯定提供了一些很好的见解,告诉我如何继续。谢谢! - MyNameIsGuzse

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接