socket.io 的快递服务器不会发送给正确的收件人
express server with socket.io does not emit to right recipient
我正在尝试创建视频聊天并在后端使用 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
,所以视频通话无法开始。看起来很可能是服务器没有向正确的对等方发送事件,但我尝试调试无济于事。我在这里遗漏了什么吗?
看来问题出在客户端的连接上。
连接发生在组件内部,这意味着每次组件重新渲染时都会创建一个新连接。严重错误。
我在客户端的代码现在看起来像这样:
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"))
就是这样!
我正在尝试创建视频聊天并在后端使用 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
,所以视频通话无法开始。看起来很可能是服务器没有向正确的对等方发送事件,但我尝试调试无济于事。我在这里遗漏了什么吗?
看来问题出在客户端的连接上。 连接发生在组件内部,这意味着每次组件重新渲染时都会创建一个新连接。严重错误。
我在客户端的代码现在看起来像这样:
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"))
就是这样!