我正在try 进行基本的WebSocket设置:

  • 客户端:Next.js 14
  • 服务器:Express
  • WebSockets:socket-io

以下是最重要的文件:

服务器

我的Express服务器只有一个文件,看起来像这样:

index.ts

import * as socket from "socket.io";
import express from "express";
import http from "http";
import cors from "cors";

const app = express();
app.use(cors());

const server = http.create服务器(app);

const io = new socket.服务器(server, {
  cors: {
    origin: true,
  },
});

io.on("connection", async (socket) => {
  console.log(`Connection established`, socket.id);

  socket.on("disconnect", () => {
    console.log("user disconnected", socket.id);
  });

  socket.on("chat-message", (text) => {
    console.log(text);
    socket.broadcast.emit("chat-message", text);
  });
});

server.listen(4000, () => {
  console.log("服务器 running on http://localhost:4000");
});

客户端

在客户端上,我有两个相关文件:

app/lib/socket.ts

问题是:当应用程序第一次启动时,该代码会执行两次!我在服务器上(即在我运行Next.js应用程序的我的终端中)看到initialize socket日志(log),在客户端(即在浏览器控制台中)看到and.

import { io } from 'socket.io-client';

const url = 'http://localhost:4000';
console.log(`initialize socket`); 
export const socket = io(url);

app/page.tsx

"use client";

import { useState, useEffect } from "react";
import { socket } from "./lib/socket";

interface Message {
  text: string;
}

export default function Home() {
  const [message, setMessage] = useState("");
  const [messageHistory, setMessageHistory] = useState<Message[]>([
    {
      text: "hello",
    },
    {
      text: "world",
    },
  ]);

  const newMessageReceived = (e) => {
    console.log(`received message: `, e);
    setMessageHistory((oldMessageHistory) => [...oldMessageHistory, { text: e }]);
  };

  useEffect(() => {
    socket.on("chat-message", newMessageReceived);

    return () => {
      console.log(`return from useEffect`);
      socket.off("chat-message", newMessageReceived);
    };
  }, []);

  const sendMessage = async (e: any) => {
    e.preventDefault();
    const newMessage = message;
    setMessage("");
    console.log(`sendMessage`, newMessage);
    socket.emit("chat-message", newMessage);
    setMessageHistory((oldMessageHistory) => [...oldMessageHistory, { text: newMessage }]);
  };

  return (
    <main className="flex min-h-screen flex-col items-center justify-between p-24">
      <div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
        <p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl lg:static lg:w-auto  lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
          <code className="font-mono font-bold">CoolChat 😎</code>
        </p>
      </div>
      <div className="">
        {messageHistory.map((message, i) => {
          return <div key={i}>{message.text}</div>;
        })}
      </div>

      <form
        id="text-input-container"
        className="bg-gray-300 py-4 px-2 w-full flex items-center justify-center"
        onSubmit={sendMessage}
      >
        <div className="text-center bg-white w-full md:w-1/3 px-3 py-2 flex gap-3 rounded-xl drop-shadow-2xl">
          <input
            name="message"
            value={message}
            onChange={(e) => setMessage(e.target.value)}
            id="message"
            className="focus:outline-none px-2 flex-1 rounded-xl"
            type="text"
            placeholder="What do you want to say?"
          />
          <button type="submit" className="rounded-xl px-3 py-2 bg-gray-600 text-gray-100 text-sm">
            Send
          </button>
        </div>
      </form>
    </main>
  );
}

下面是我运行代码时发生的情况:

  1. npm run dev启动服务器
  2. 服务器打印服务器 running on http://localhost:4000
  3. npm run dev启动客户端
  4. 打开新的浏览器窗口并转到localhost:3000
  5. 客户端(终端)打印:initialize socket
  6. 客户端(浏览器)打印:initialize socket
  7. 服务器打印Connection established wQGEI_1USi_4T9n-AAAB
  8. 服务器打印Connection established 1SPz0765uUEvMQgBAAAD
  9. (有时是第三次):服务器打印Connection established BOAeHMUMeJ5zb2EJAAAF
  10. 打开第二个浏览器窗口并转到localhost:3000
  11. 客户端(浏览器)打印:initialize socket
  12. 服务器打印Connection established um8qdP-yV9btkqVAAAAH
  13. 打开第三个浏览器窗口并转到localhost:3000
  14. 客户端(浏览器)打印:initialize socket
  15. 服务器打印Connection established um8qdP-yV9btkqVAAAAH

因此,正如您所看到的,当我第一次连接到localhost:3000时,套接字被初始化了两次-一次是服务器,一次是客户端.

在接下来的所有时间里,随着新浏览器窗口导航到localhost:3000,套接字只初始化一次(在浏览器中,而不是在服务器上),正如我所预期的那样.

  • 谁能解释一下为什么代码要执行两次,一次是在客户端(我希望它发生的地方),另一次是在服务器(我不希望它发生的地方)?这可能是显而易见的,但我是Next.js新手,所以如果有任何解释(也许还有一些其他资源的链接),我将不胜感激.
  • 有人能解释一下如何防止这种情况,只在客户机上运行代码以防止"双重连接"吗?

推荐答案

正如您所提到的,您正在使用Next.js 14,并且在您的本地环境中面临这个问题.Reaction很可能会两次呈现您的组件,这会导致建立多个连接.

我建议您在next.config.js文件中禁用reactStrictMode.它应该会为您解决问题.

// next.config.js
module.exports = {
  reactStrictMode: false,
}

Node.js相关问答推荐

仅当所需文档是最后一个文档时才更新MongoDB,否则插入

如何解决TypeError:requ.isAuthenticated不是函数错误?

如何将我的Redis客户端配置为在禁用群集模式的情况下使用读取副本?

为什么即使数据库确实更新了,此 POST 也无法解析并返回 200 代码?

将 POST 的 json 变量格式化为 lambda

当API返回400状态代码时,使用Reactjs fetch获取错误消息

NPM如何管理node_modules传递依赖?

为什么要加密 CSRF 令牌?

为什么后端开发需要单独的服务器?

为什么 $or 在带有正则表达式的mongoose 中不能正常工作

FirebaseCloudMessaging : PlatformException (PlatformException(null-error, Host platform returned null value for non-null return value., null, null))

NestJS TypeORM 可选查询不起作用

带权限的机密 Rest-Api - 总是 403 - 我做错了什么?

如何在 Nest.js 中使用查询参数?

如何监控 node.js 上的网络,类似于 chrome/firefox 开发者工具?

npm install 给出警告,npm audit fix 不起作用

Node.js 17.0.1 Gatsby 错误-数字信封 routine ::不支持 ... ERR_OSSL_EVP_UNSUPPORTED

nodeJS - 如何使用 express 创建和读取会话

node.js 服务器和 HTTP/2 (2.0) 与 express.js

node.js 异步库