NodeJS 无法与我的节点服务器建立简单的websockets连接

jvidinwx  于 2023-02-12  发布在  Node.js
关注(0)|答案(1)|浏览(207)

我试图让一个非常简单的WebSocket服务器工作,但由于某种原因,我的浏览器无法建立连接。当我加载我的HTML页面时,我得到以下错误

error:index.html:9 WebSocket connection to 'ws://localhost:8080/?chatId=15432' failed:

下面是我的服务器的外观:

const http = require("http");
const WebSocketServer = require("websocket").server;
const server = http.createServer((req, res) => {});
const websocket = new WebSocketServer({
  httpServer: server,
  autoAcceptConnections: false,
});

const chats = new Map();

websocket.on("request", (request) => {
  let connection = request.accept(null, request.origin);
  console.log(connection);
  console.log(new Date() + " Connection accepted.");
  let chatId = request.resourceURL.query.chatId;

  if (!chats.has(chatId)) {
    chats.set(chatId, []);
  }

  chats.get(chatId).push(connection);

  connection.on("message", (message) => {
    console.log("Received Message: " + message.utf8Data);
    let chatConnections = chats.get(chatId);
    chatConnections.forEach((chatConnection) => {
      if (chatConnection !== connection) {
        chatConnection.sendUTF(message.utf8Data);
        console.log(connection);
      }
    });
  });
  connection.on("close", (reasonCode, description) => {
    console.log(
      new Date() + " Peer " + connection.remoteAddress + " disconnected."
    );
    let chatConnections = chats.get(chatId);
    chatConnections.splice(chatConnections.indexOf(connection), 1);
    if (chatConnections.length === 0) {
      chats.delete(chatId);
    }
  });
});
server.listen(8080, "localhost", () => {
  console.log("listening on port 8080");
});

下面是我的客户端的外观(即我在浏览器中打开的html文件):

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script>
      let webSocket = new WebSocket("ws://localhost:8080?chatId=15432", null);
      webSocket.onopen = (event) => {
        webSocket.send("Hey guys, here's my first message");
        console.log("Connection Opened Client");
      };

      webSocket.onclose = function () {
        console.log("WebSocket connection closed.");
      };
    </script>
  </head>
  <body></body>
</html>

我期望连接发生并从客户端接收消息,但是由于某种原因没有发生。我错过了一些明显的东西吗?

sr4lhrrt

sr4lhrrt1#

onRequest的用法似乎是核心问题,应该使用websocket.on('connection' ...方法:
大概是这样的

// USER CONNECTED (onload events)
wss.on("connection", async function connection(client, request) {
  console.log("user connected", Date.now());
  client.send(JSON.stringify({ ready: true }));

  ...
  
});

考虑一下我最近写的可以派生和使用的this barebones Websockets chat implementation。它使用的是React 17,但应该可以与React 18一起工作。一旦WSS准备好了,它就会让客户端知道,并开始通信-然后看到乒乓通信。组件的分数和会话命名是任意的。

  • ./包. json*
{
  "name": "simple-chat",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "dependencies": {
    "concurrently": "^7.6.0",
    "express": "^4.18.2",
    "http": "^0.0.1-security",
    "node": "^19.6.0",
    "ws": "^8.12.0"
  },
  "scripts": {
    "start": "concurrently --kill-others-on-fail \"npm run dev\" \"npm run dev_client\"",
    "dev": "node index.js",
    "dev_client": "cd ./client && npm run start",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "Wesley LeMahieu",
  "license": "ISC"
}
  • ./索引. js*
import express from "express";
import WebSocket, { WebSocketServer } from "ws";
import http from "http";

const heartbeat = (ws) => {
  ws.isAlive = true;
};

const app = express();
const server = http.createServer(app);
const wss = new WebSocketServer({ port: 4443 });

wss.on("close", function close() {
  console.log("closed");
});

wss.on("open", function connection(client) {
  console.log('sent "open" to client');
  client.send("open");
});

// USER CONNECTED (onload events)
wss.on("connection", async function connection(client, request) {
  console.log("user connected", Date.now());
  client.send(JSON.stringify({ ready: true }));

  // CLIENT ALIVE-CHECK
  client.isAlive = true;
  client.on("pong", () => heartbeat(client));

  // message all clients in the ui
  client.on("message", async function message(d, isBinary) {
    if (client.readyState === WebSocket.OPEN) {
      const data = JSON.parse(d.toString());
      console.log("client message", { data });
      client.send(
        JSON.stringify({
          score: Math.random(),
          session: Math.random(),
          ...data,
        })
      );
    }
  });
});

server.listen(process.env.port || 4444, () => {
  console.log(`*~~% wesley's server %~~*`);
});
  • ./客户端/包. json*
{
  "name": "simple-chat-client",
  "version": "1.0.0",
  "description": "A barebones Websockets chat client",
  "keywords": [
    "react",
    "starter"
  ],
  "main": "src/index.js",
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ],
  "dependencies": {
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-scripts": "^5.0.1"
  }
}
  • ./客户端/源代码/索引. js*
import { StrictMode } from "react";
import { render } from "react-dom";
import { socket, SocketContext } from "./context";

import App from "./App";

render(
  <StrictMode>
    <SocketContext.Provider value={socket}>
      <App />
    </SocketContext.Provider>
  </StrictMode>,
  document.getElementById("root")
);
  • ./客户端/源代码/上下文. js*
import React from "react";

export const socket = new WebSocket("ws://localhost:4443");
export const SocketContext = React.createContext();
  • ./客户端/源代码/应用程序. js*
import { useContext, useEffect, useState } from "react";
import { SocketContext } from "./context";
import Score from "./Score";
import Session from "./Session";

export default function App() {
  const websocket = useContext(SocketContext);
  const [message, setMessage] = useState("");
  const [isReady, setIsReady] = useState(false);
  const [sentMessages, setSentMessages] = useState([]);

  // since websocket is the same instance shared between components,
  // once websocket.onmessage is set here, it's set. subsequent
  // components will not create their own listener because the listener
  // is already set once.

  console.log("USE EFFECT APP.JS");
  useEffect(() => {
    websocket.addEventListener("message", (e) => {
      const packet = JSON.parse(e.data);
      console.log("app.js packet", packet);
      if (packet.ready) setIsReady(packet.ready);
    });
  }, [websocket]);

  const onSubmit = (e) => {
    e.preventDefault();
    websocket.send(JSON.stringify({ message }));
    setMessage("");
    setSentMessages((msgs) => [...msgs, { message }]);
  };

  const onChange = (e) => {
    setMessage(e.target.value);
  };

  return (
    <>
      <div
        style={{
          display: "flex",
          margin: "1rem",
          columnGap: 5,
        }}
      >
        <Score />
        <Session />
      </div>
      <div style={{ display: "flex", columnGap: 5, margin: "1rem" }}>
        <form onSubmit={onSubmit}>
          <input id="message" value={message} onChange={onChange} />
          <button type="submit" disabled={!isReady}>
            Send
          </button>
        </form>
        <div>
          {!isReady ? (
            <span style={{ color: "red" }}>Initializing WSS...</span>
          ) : (
            <span style={{ color: "green" }}>WSS Ready!</span>
          )}
        </div>
      </div>
      <div style={{ margin: "1rem" }}>
        {sentMessages.map((msg, key) => (
          <div key={`row-key${key}`}>{msg.message}</div>
        ))}
      </div>
    </>
  );
}
  • ./客户端/源代码/得分. js*
import { useContext, useEffect, useState } from "react";
import { SocketContext } from "./context";

const Score = () => {
  const websocket = useContext(SocketContext);
  const [score, setScore] = useState(0);

  console.log("USE EFFECT SCORE.JS");
  useEffect(() => {
    websocket.addEventListener("message", (e) => {
      const packet = JSON.parse(e.data);
      setScore(packet.score);
    });
  }, [websocket]);

  return <div>Score: {score}</div>;
};

export default Score;
  • ./客户端/源代码/会话. js*
import { useContext, useEffect, useState } from "react";
import { SocketContext } from "./context";

const Session = () => {
  const websocket = useContext(SocketContext);
  const [session, setSession] = useState(0);

  console.log("USE EFFECT SESSION.JS");
  useEffect(() => {
    websocket.addEventListener("message", (e) => {
      const packet = JSON.parse(e.data);
      setSession(packet.session);
    });
  }, [websocket]);

  return <div>Session: {session}</div>;
};

export default Session;
  • ./公共/索引. html*
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="theme-color" content="#000000">
    <!--
      manifest.json provides metadata used when your web app is added to the
      homescreen on Android. See https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/
    -->
    <link rel="manifest" href="%PUBLIC_URL%/manifest.json">
    <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
    <!--
      Notice the use of %PUBLIC_URL% in the tags above.
      It will be replaced with the URL of the `public` folder during the build.
      Only files inside the `public` folder can be referenced from the HTML.

      Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
      work correctly both with client-side routing and a non-root public URL.
      Learn how to configure a non-root public URL by running `npm run build`.
    -->
    <title>React App</title>
</head>

<body>
    <noscript>
        You need to enable JavaScript to run this app.
    </noscript>
    <div id="root"></div>
    <!--
      This HTML file is a template.
      If you open it directly in the browser, you will see an empty page.

      You can add webfonts, meta tags, or analytics to this file.
      The build step will place the bundled scripts into the <body> tag.

      To begin the development, run `npm start` or `yarn start`.
      To create a production bundle, use `npm run build` or `yarn build`.
    -->
</body>

</html>

相关问题