【问题标题】:express server with socket.io does not emit to right recipient带有 socket.io 的快递服务器不会发送给正确的收件人
【发布时间】:2021-11-22 15:27:41
【问题描述】:

我正在尝试创建视频聊天,并在后端使用 expresssocket.io,在前端使用 reactsimple-peer

这是我的服务器代码:

const http = require('http');
const express = require('express');

const app = express();
const httpServer = http.createServer(app);

app.use(require('cors')());

const io = require('socket.io')(httpServer, {
  cors: {
    origin: ['http://localhost:3000', 'http://localhost:3001'],
    method: ['GET', 'POST']
  }
});

const cache = {};

io.on('connection', socket => {
  socket.on('join', id => {
    cache[id] = socket.id;
    console.log('cache', cache);
    socket.join(id);
  });

  socket.on('disconnect', () => {
    socket.broadcast.emit('callEnded');
  });

  socket.on('callUser', data => {
    console.log('calling user', data.userToCall, cache[data.userToCall]);
    io.to(data.userToCall).emit('callUser', {
      signal: data.signalData,
      from: data.from,
      name: data.name
    });
  });

  //   socket.on('answerCall', data => {
  //     console.log('data', data);
  //     io.to(cache[data.to]).emit('callAccepted', data.signal);
  //   });

  socket.on('answerCall', data => {
    console.log('answering call', data);
    io.to(data.to).emit('callAccepted', data.signal);
  });
});

httpServer.listen(4000, () => 'Listening...');

我正在接受来自端口 3000 和 3001 的请求,因为这是我运行我的两个应用程序的地方。由于我目前在后端没有登录系统,因此我正在为每个对等方运行一个应用程序。

第一个应用的代码如下所示:

import { useEffect, useState, useRef } from "react";
import Peer from "simple-peer";
import io from "socket.io-client";
import './App.css';

const ID = 'fey'

function App() {
  const [ stream, setStream ] = useState()
  const [receivingCall, setReceivingCall] = useState(false) ;
  const [caller, setCaller] = useState("") ;
  const [callerSignal, setCallerSignal] = useState() ;
  const [callAccepted, setCallAccepted] = useState(false);
  const [callEnded, setCallEnded] = useState(false);
  const socket = io.connect("http://localhost:4000/");
  const myVideo = useRef()
  const userVideo = useRef()
  const connectionRef = useRef()

  useEffect(() => {
    navigator.mediaDevices.getUserMedia({video: true, audio: true})
    .then((stream)=>{
      setStream(stream)
      myVideo.current.srcObject = stream
    })
    .catch((err) => console.log(err))
  
    socket.emit('join', ID)

    socket.on("callUser", (data)=>{
      setReceivingCall(true)
      setCaller(data.from)
      setCallerSignal(data.signal)
    })
  }, [])


  const callUser = ()=> {
    const peer = new Peer({
       initiator:true,
       trickle:false,
       stream:stream
     })
   
     peer.on("signal", (data)=>{
       socket.emit("callUser",{
         userToCall: 'fey-clone',
         signalData: data,
         from: ID,
         name: "Fey"
       })
     })
   
   
    peer.on("stream", (stream)=> {
      userVideo.current.srcObject = stream
    })
    
    socket.on("callAccepted", (signal) => {
      console.log('call accepted!!')
      setCallAccepted(true)
      peer.signal(signal)
    })

    connectionRef.current = peer
   }

  const answerCall = () => {
    console.log('peer exists')

    setCallAccepted(true)
    const peer = new Peer({
      initiator:false,
      trickle:false,
      stream: stream
    })
  
    peer.on("signal", (data)=> {
      console.log('call answered')
      socket.emit("answerCall", {signal:data, to: caller})
    })
  
    peer.on("stream", (stream) =>{
      userVideo.current.srcObject = stream
    })
    peer.signal(callerSignal)
    connectionRef.current = peer
  }

  const leaveCall = ()=>{
    setCallEnded(true)
    connectionRef.current.destroy()
  }

  return (
    <div className="App">
      <div className="video">
        {stream && <video playsInline muted ref={myVideo} autoPlay style={{width: "300px", height: "300px" }} />}
        {callAccepted && <video playsInline muted ref={userVideo} autoPlay style={{width: "300px", height: "300px" }} />}
      </div>
      {callAccepted && !callEnded ? (
         <button onClick={leaveCall}>
           End Call
         </button>
       ):(
        <button onClick={callUser}>call meeee</button>


       )}
      {receivingCall && !callAccepted ? (
        <div className="caller">
          <h1>Fey is calling ...</h1>
          <button  onClick={answerCall} >
            Answer
          </button>
        </div>
        ) : null
      }
    </div>
  );
}

export default App;

另一个对等点的代码看起来很相似,但 ID 不同,userToCall 是另一个。

import { useEffect, useState, useRef } from "react";
import Peer from "simple-peer";
import io from "socket.io-client";
import './App.css';

const ID = 'fey-clone'

function App() {
  const [ stream, setStream ] = useState()
  const [receivingCall, setReceivingCall] = useState(false) ;
  const [caller, setCaller] = useState("") ;
  const [callerSignal, setCallerSignal] = useState() ;
  const [callAccepted, setCallAccepted] = useState(false);
  const [callEnded, setCallEnded] = useState(false);
  const socket = io.connect("http://localhost:4000/");
  const myVideo = useRef()
  const userVideo = useRef()
  const connectionRef = useRef()

  useEffect(() => {
    navigator.mediaDevices.getUserMedia({video: true, audio: true})
    .then((stream)=>{
      setStream(stream)
      myVideo.current.srcObject = stream
    })
    .catch((err) => console.log(err))
  
    socket.emit('join', ID)

    socket.on("callUser", (data)=>{
      setReceivingCall(true)
      setCaller(data.from)
      setCallerSignal(data.signal)
    })
  }, [])


  const callUser = ()=> {
    const peer = new Peer({
       initiator:true,
       trickle:false,
       stream:stream
     })
   
     peer.on("signal", (data)=>{
       socket.emit("callUser",{
         userToCall: 'fey',
         signalData: data,
         from: ID,
         name: "fey clone"
       })
     })
   
   
    peer.on("stream", (stream)=> {
      userVideo.current.srcObject = stream
    })
    
    socket.on("callAccepted", (signal) => {
      console.log('call accepted!!')
      setCallAccepted(true)
      peer.signal(signal)
    })

    connectionRef.current = peer
   }

  const answerCall = () => {
    setCallAccepted(true)
    const peer = new Peer({
      initiator:false,
      trickle:false,
      stream: stream
    })
  
    peer.on("signal", (data)=> {
      console.log(data, caller)
      socket.emit("answerCall", {signal:data, to: caller})
    })
  
    peer.on("stream", (stream) =>{
      console.log('I streeeam')
      userVideo.current.srcObject = stream
    })
    peer.signal(callerSignal)
    connectionRef.current = peer
  }

  const leaveCall = ()=>{
    setCallEnded(true)
    connectionRef.current.destroy()
  }

  return (
    <div className="App">
      <div className="video">
        {stream && <video playsInline muted ref={myVideo} autoPlay style={{width: "300px", height: "300px" }} />}
        {callAccepted && <video playsInline muted ref={userVideo} autoPlay style={{width: "300px", height: "300px" }} />}
      </div>
      {callAccepted && !callEnded ? (
         <button onClick={leaveCall}>
           End Call
         </button>
       ):(
        <button onClick={callUser}>call meeee</button>


       )}
      {receivingCall && !callAccepted ? (
        <div className="caller">
          <h1>Fey is calling ...</h1>
          <button onClick={answerCall} >
            Answer
          </button>
        </div>
        ) : null
      }
    </div>
  );
}

export default App;

呼叫似乎是通过正确的接收者。当fey 呼叫fey-clone 时,fey-clone 可以接听电话。 signal 似乎也可以正常工作。但是,原来的调用者fey似乎从来没有收到来自服务器的事件callAccepted,所以视频通话无法开始。服务器很可能没有将事件发送给正确的对等方,但我尝试调试无济于事。我在这里有什么遗漏吗?

【问题讨论】:

    标签: node.js reactjs express socket.io webrtc


    【解决方案1】:

    看起来问题出在客户端的连接上。 连接发生在组件内部,这意味着每次重新渲染组件时都会创建一个新连接。大错特错。

    我在客户端的代码现在是这样的:

    import React,{ useEffect, useRef, useState } from "react";
    import Peer from "simple-peer";
    import io from "socket.io-client";
    
    const socket = io.connect('http://localhost:5000')
    
    function Chat() {
      const [ stream, setStream ] = useState()
      const [ receivingCall, setReceivingCall ] = useState(false)
      const [ caller, setCaller ] = useState("")
      const [ callerSignal, setCallerSignal ] = useState()
      const [ callAccepted, setCallAccepted ] = useState(false)
      const [ idToCall, setIdToCall ] = useState("")
      const [ callEnded, setCallEnded] = useState(false)
      const [ name, setName ] = useState("")
      const myVideo = useRef()
      const userVideo = useRef()
      const connectionRef= useRef()
    
      useEffect(() => {
    
        navigator.mediaDevices.getUserMedia({ video: true, audio: true })
        .then((stream) => {
          setStream(stream)
            myVideo.current.srcObject = stream
        })
    
        socket.emit('join', 'fey')
    
        socket.on("callUser", (data) => {
          setReceivingCall(true)
          setCaller(data.from)
          setName(data.name)
          setCallerSignal(data.signal)
        })
      }, [])
    
      const callUser = (id) => {
        const peer = new Peer({
          initiator: true,
          trickle: false,
          stream: stream
        })
    
        peer.on("signal", (data) => {
          socket.emit("callUser", {
            userToCall: 'fey-clone',
            signalData: data,
            from: 'fey',
            name: 'fey'
          })
        })
    
        peer.on("stream", (stream) => {
          userVideo.current.srcObject = stream
        })
        
        socket.on("callAccepted", (signal) => {
          setCallAccepted(true)
          peer.signal(signal)
        })
    
        connectionRef.current = peer
      }
    
      const answerCall =() =>  {
        setCallAccepted(true)
        
        const peer = new Peer({
          initiator: false,
          trickle: false,
          stream: stream
        })
    
        peer.on("signal", (data) => {
          socket.emit("answerCall", { signal: data, to: caller })
        })
    
        peer.on("stream", (stream) => {
          userVideo.current.srcObject = stream
        })
    
        peer.signal(callerSignal)
        connectionRef.current = peer
      }
    
      const leaveCall = () => {
        setCallEnded(true)
        connectionRef.current.destroy()
      }
    
      return (
        <>
          <h1>zoomish</h1>
          <div className="container">
            <div className="video-container">
              <div className="video">
                {stream && <video playsInline muted ref={myVideo} autoPlay style={{width: "300px" }} />}
              </div>
              <div className="video">
                {callAccepted && !callEnded ? 
                  <video playsInline ref={userVideo} autoPlay style={{width:"300px"}}/>
                : null}
              </div>
            </div>
            <div className="myId">
              <div className="call-button">
                {callAccepted && !callEnded ? (
                  <button onClick={leaveCall}>End Call</button>
                ):(
                  <button onClick={()=>callUser(idToCall)}>call me</button>
                )}
              </div>
            </div>
            <div>
            {receivingCall && !callAccepted ? (
              <div className="caller">
                <h1>{name} is calling ...</h1>
                <button onClick={answerCall}>Answer</button>
              </div>
            ):null}
            </div>
          </div>
        </>
      );
    }
    
    export default Chat;
    

    在服务器上没有什么特别的错误,但是一旦它与socket.join 连接,我就会复制已经分配给套接字的房间,并且不需要这样做。所以我开始维护userIdsocket.id 的地图。在生产应用程序中,这可能会被委托给单独的存储以更好地处理流量。我的服务器现在是这样的:

    const express = require("express")
    const http = require("http")
    const app = express()
    const server = http.createServer(app)
    
    const io = require("socket.io")(server, {
      cors: {
        origin: ["http://localhost:3000", "http://localhost:3001" ],
        methods: [ "GET", "POST" ]
      }
    })
    
    let users = {}
    
    io.on("connection", (socket) => {
    
      socket.on('join', (userId) => {
        users[userId] = socket.id
      });
    
      socket.on("disconnect", () => {
        socket.broadcast.emit("callEnded")
      })
    
      socket.on("callUser", (data) => {
        io.to(users[data.userToCall]).emit("callUser", { 
          signal: data.signalData,
          from: data.from,
          name: data.name
        })
      })
    
      socket.on("answerCall", (data) => {
        io.to(users[data.to]).emit("callAccepted", data.signal)
      })
    
    })
    
    server.listen(5000, () => console.log("server is running on port 5000"))
    

    就是这样!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-10
      • 2011-12-26
      相关资源
      最近更新 更多