带有 socket.io 的快递服务器不会发送给正确的收件人
Posted
技术标签:
【中文标题】带有 socket.io 的快递服务器不会发送给正确的收件人【英文标题】:express server with socket.io does not emit to right recipient 【发布时间】:2021-11-22 15:27:41 【问题描述】:我正在尝试创建视频聊天,并在后端使用 express
和 socket.io
,在前端使用 react
和 simple-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
,所以视频通话无法开始。服务器很可能没有将事件发送给正确的对等方,但我尝试调试无济于事。我在这里有什么遗漏吗?
【问题讨论】:
【参考方案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
连接,我就会复制已经分配给套接字的房间,并且不需要这样做。所以我开始维护userId
和socket.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"))
就是这样!
【讨论】:
以上是关于带有 socket.io 的快递服务器不会发送给正确的收件人的主要内容,如果未能解决你的问题,请参考以下文章
Socket.io-client 未连接到 socket.io 服务器反应和节点(快递)