我正在尝试使用next.js和socket.io实现一个聊天应用程序。套接字io的客户端和服务器都在nextjs上(服务器使用API路由)。
我想创建一个聊天应用程序,允许用户创建“房间”,每个房间最多可以有2个连接的客户端(房间的创建者和另一个用户)。每个房间都有一个密码,用户在访问聊天框之前必须输入密码。我有一个页面pages/room/[id].tsx
,这个页面验证密码,如果密码有效,然后显示聊天框。在这个页面本身,我初始化了一个socket.io客户端,并在验证了用户密码之后发出一个joinRoom
事件。
(页数/房间/[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
页面传递过来的,socket是在该页面初始化的):
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();
}
型
这就是我想要实现的流程:密码验证->发出joinRoom
事件(从客户端发出,服务器成功接收)->发出sendMessage
事件(消息发送时,从ChatBox组件发出,服务器接收)->从sendMessage
向聊天室发出message
事件(客户端接收,客户端在聊天箱中显示接收到的消息)。
然而,问题是,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
型
(logs所附的是sendMessage调度后的事件)
我似乎找不到问题,插座已连接,我已仔细检查房间ID和插座ID是否相同。请帮助。如果您也有改进此架构的建议,请与我们分享。
谢谢
1条答案
按热度按时间zy1mlcev1#
我已经修复了它。这是因为我使用
socket.to(roomId).emit(...)
,它向所有客户端发送事件,除了发送者,这就是为什么事件没有被发送。用
io
(Server
)替换socket应该可以工作,因为io.in
/io.to
brodcast事件到所有连接的客户端,包括发送方。查看https://socket.io/docs/v4/server-api/#serverinroom了解更多信息。