客户端未收到从服务器发出的事件(使用 socket.io 和 next.js)

Client not receiving event emitted from server (using socket.io and next.js)

提问人:FC5570 提问时间:11/16/2023 更新时间:11/16/2023 访问量:29

问:

我正在尝试使用 next.js 和 socket.io 实现聊天应用程序。套接字 io 的客户端和服务器都在 nextjs 上(服务器使用 api 路由)。

我想创建一个允许用户创建“房间”的聊天应用程序,每个房间最多可以有 2 个连接的客户端(房间的创建者和另一个用户)。每个房间都有一个密码,用户在访问聊天框之前必须输入该密码。 我有一个页面,此页面验证密码,如果密码有效,则显示聊天框。在此页面中,我初始化了一个 socket.io 客户端,并在验证了用户的密码后发出了一个事件。pages/room/[id].tsxjoinRoom

(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 中的 和 是从初始化套接字的页面传递的):idsocketroom/[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 组件,由服务器接收) -> 个事件发出到房间(由客户端接收,在聊天框中显示收到的消息)。joinRoomsendMessagemessagesendMessage

但是,问题是,不会发出该事件。我检查了套接字是否仍然连接。这些是调试日志: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 是否相同。请帮忙。如果您也有改进此架构的建议,请分享。

谢谢。

下一篇.js socket.io socket.io-client

评论


答:

0赞 FC5570 11/16/2023 #1

我已经修好了。这是因为我使用的是将事件发送到除发送方之外的所有客户端,这就是事件不发出的原因。socket.to(roomId).emit(...)

用 () 替换套接字应该可以工作,因为 / 将事件广播到所有连接的客户端,包括发送方。ioServerio.inio.to

查看 https://socket.io/docs/v4/server-api/#serverinroom 了解更多信息。