提问人:FC5570 提问时间:11/16/2023 更新时间:11/16/2023 访问量:29
客户端未收到从服务器发出的事件(使用 socket.io 和 next.js)
Client not receiving event emitted from server (using socket.io and next.js)
问:
我正在尝试使用 next.js 和 socket.io 实现聊天应用程序。套接字 io 的客户端和服务器都在 nextjs 上(服务器使用 api 路由)。
我想创建一个允许用户创建“房间”的聊天应用程序,每个房间最多可以有 2 个连接的客户端(房间的创建者和另一个用户)。每个房间都有一个密码,用户在访问聊天框之前必须输入该密码。
我有一个页面,此页面验证密码,如果密码有效,则显示聊天框。在此页面中,我初始化了一个 socket.io 客户端,并在验证了用户的密码后发出了一个事件。pages/room/[id].tsx
joinRoom
(pages/room/[id].tsx):
export default function ChatRoomID({
id
}: {
id: string
}) {
const [socket, setSocket] = useState<Socket | null>(null);
useEffect(() => {
const socket = io();
setSocket(socket);
return () => {
socket.disconnect();
};
}, []);
function handleSubmit(password: string) {
// ...verify password
if (socket) {
socket.emit(
'joinRoom',
id,
(error: string | string, message: string) => {
if (error) {
toast({
title: 'Error',
description: error,
});
}
}
);
}
}
return (
<Chatbox id={id} socket={socket} />
)
}
这是聊天框组件(该组件的 props 中的 和 是从初始化套接字的页面传递的):id
socket
room/[id].tsx
export default function ChatBox({ socket }: { socket: Socket | null }) {
useEffect(() => {
if (!socket) return;
console.log(socket.connected) // true
socket.on('message', (data: { message: string; roomId: string }) => { // this event does not emit
const { message, roomId } = data;
console.log('Received message:', message); // does not log
setMessages((prevMessages) => [...prevMessages, message]);
});
return () => {
socket.off('message');
};
}, [socket]);
// the function below is called when a user clicks the send message button
const handleMessageSend = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (socket) {
const { message } = Object.fromEntries(new FormData(e.currentTarget));
socket.emit('sendMessage', id, message); // this event emits properly
e.currentTarget.reset();
return;
}
};
}
最后,这是我的 api 路由器:
export default function handler(req: NextApiRequest, res: CustomApiResponse) {
if (!res.socket.server.io) {
const io = new Server(res.socket.server);
res.socket.server.io = io;
io.on('connection', (socket: Socket) => {
console.log('io connection received');
socket.on(
'joinRoom',
async (roomId: string, callback: Function) => {
const roomDetails = await kv.get(roomId);
if (!roomDetails) {
return callback('Room not found', null);
}
const roomClients = io.sockets.adapter.rooms.get(roomId);
if (roomClients && roomClients.size >= 2) {
return callback('Room is full', null);
}
socket.join(roomId);
callback(null, 'Successfully joined the room');
}
);
socket.on('sendMessage', (roomId: string, message: string) => {
console.log(`send message emitted`, roomId, message) // this is logged to console
socket.to(roomId).emit('message', { message, roomId });
});
});
}
res.end();
}
这是我想要实现的流程: 密码验证 -> 个事件发出(从客户端,服务器成功接收) -> 发出(发送消息时,从 ChatBox 组件,由服务器接收) -> 个事件发出到房间(由客户端接收,在聊天框中显示收到的消息)。joinRoom
sendMessage
message
sendMessage
但是,问题是,不会发出该事件。我检查了套接字是否仍然连接。这些是调试日志:message
socket.io:socket emitting event ["sendMessage","3aa166ee-9c8c-419d-b213-6413bfef5e6f","a"] +1ms
socket.io:socket dispatching an event ["sendMessage","3aa166ee-9c8c-419d-b213-6413bfef5e6f","a"] +1ms
Send message emitted 3aa166ee-9c8c-419d-b213-6413bfef5e6f a
socket.io-parser encoding packet {"type":2,"data":["message",{"message":"a","roomId":"3aa166ee-9c8c-419d-b213-6413bfef5e6f"}],"nsp":"/"} +4ms
socket.io-parser encoded {"type":2,"data":["message",{"message":"a","roomId":"3aa166ee-9c8c-419d-b213-6413bfef5e6f"}],"nsp":"/"} as 2["message",{"message":"a","roomId":"3aa166ee-9c8c-419d-b213-6413bfef5e6f"}] +1ms
engine:transport readyState updated from closing to closed (polling) +30s
engine:socket writing ping packet - expecting pong within 20000ms +659ms
engine:socket sending packet "ping" (undefined) +0ms
engine:socket flushing buffer to transport +1ms
engine:ws writing "2" +662ms
engine:ws received "3" +2ms
engine:socket received packet pong +3ms
engine:socket got pong +1ms
(附加的日志是调度 sendMessage 后的事件日志)
我似乎找不到问题,套接字已连接,我仔细检查了房间 ID 和套接字 ID 是否相同。请帮忙。如果您也有改进此架构的建议,请分享。
谢谢。
答:
我已经修好了。这是因为我使用的是将事件发送到除发送方之外的所有客户端,这就是事件不发出的原因。socket.to(roomId).emit(...)
用 () 替换套接字应该可以工作,因为 / 将事件广播到所有连接的客户端,包括发送方。io
Server
io.in
io.to
查看 https://socket.io/docs/v4/server-api/#serverinroom 了解更多信息。
评论