You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NodeJS后端无法接收Flutter前端WebSocket数据问题求助

问题分析与解决建议

问题概述

开发Flutter前端+NodeJS(TypeScript)后端的移动应用,后端部署在OVHcloud的Ubuntu 20.04 VPS上,已关闭VPS防火墙。测试时:

  • 前端终端显示connected,能打印发送的消息,但后端仅显示监听端口,无法接收数据、无任何响应。
  • VPS上执行tsc --w编译时,express模块出现147个错误,但仍能正常编译并启动服务;本地运行相同代码无编译错误,但同样无法接收数据。

Flutter前端代码

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:nagahni/client.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'MyProject',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'MyProject'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;
  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String? _message;

  // This function will send the message to our backend.
  void sendMessage(msg) {
    WebSocketChannel? channel;
    try {
      channel = IOWebSocketChannel.connect('ws://I put my IP here:I put my port here');
      print('connected');
    } catch (e) {
      if (kDebugMode) {
        print(
            "Error: $e");
      }
    }
    channel?.sink.add(msg);

    channel?.stream.listen((event) {
      if (event != null) {
        if (kDebugMode) {
          print(event);
        }
      }
      channel!.sink.close();
    });
    if (kDebugMode) {
      print(msg);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          children: [
            Center(
              child: TextField(
                onChanged: (e) => _message = e,
              ),
            ),
            Center(
              child: TextButton(
                child: const Text("Send String"),
                onPressed: () {
                  // Check if the message isn't empty.
                  if (_message!.isNotEmpty) {
                    sendMessage(_message);
                  }
                },
              ),
            ),
            const SizedBox(height: 12),
            TextButton(
              onPressed: () {
                sendObject();
              },
              child: const Text('Send Object'),
            ),
          ],
        ),
      ),
    );
  }
}

NodeJS(TypeScript)后端代码

import express from 'express';
import { Server } from 'ws';
const PORT = process.env.MAIN_PORT; // This is basically the SSH port + 1 ex. if 22, then this would be 23
require('dotenv').config()

const server = express()
        .use((req, res) => res.send("Hi there"))
        .listen(PORT, () => console.log(`Listening on Port ${PORT}`))

const wss = new Server({ server });

wss.on('connection', function(ws, req) {
    ws.on('message', message => {
        var data = message.toString();
        if(data === "Hello") {
            console.log(data);
            ws.send("Hi from TypeScript NodeJS");
        } else {
            console.log(data);
            ws.send("Say hello to me!");
        }
    })
})

编译错误信息(部分)

[5:51:56 PM] Starting compilation in watch mode...

node_modules/@types/express-serve-static-core/index.d.ts:104:68 - error TS1110: Type expected.

104 type RemoveTail<S extends string, Tail extends string> = S extends `${infer P}${Tail}` ? P : S;
                                                                       ~~~

node_modules/@types/express-serve-static-core/index.d.ts:104:77 - error TS1005: '}' expected.

104 type RemoveTail<S extends string, Tail extends string> = S extends `${infer P}${Tail}` ? P : S;
                                                                                ~

... wayy more errors but i cannot paste it all ... 

[5:53:55 PM] Found 147 errors. Watching for file changes.

后端部署步骤

  • mkdir node_project
  • cd node_project
  • npm init -y
  • npm i @types/express
  • npm i express
  • npm i @types/ws
  • npm i ws

问题原因分析与解决办法

1. TypeScript版本不兼容(编译错误根源)

编译错误里的${infer P}${Tail}是**TypeScript 4.1+**才支持的模板字符串类型推断特性。VPS上全局或项目依赖的TypeScript版本过低,导致无法解析express类型定义里的语法。

解决:
升级项目的TypeScript版本:

npm install typescript@latest --save-dev

同时检查tsconfig.json的target字段,建议设置为ES2020或更高,确保兼容新语法。

2. 环境变量加载顺序错误(后端收不到连接的核心原因)

后端代码中先赋值PORT = process.env.MAIN_PORT,再调用require('dotenv').config(),此时process.env.MAIN_PORT还未被加载,导致PORT为undefined。express监听undefined时会默认用端口3000,但你前端连接的是自定义端口,所以实际后端监听的端口和前端连接的端口不匹配,自然收不到数据。

解决:
调整代码顺序,先加载dotenv再读取环境变量:

import express from 'express';
import { Server } from 'ws';
require('dotenv').config() // 移到最前面
const PORT = process.env.MAIN_PORT; 

const server = express()
        .use((req, res) => res.send("Hi there"))
        .listen(PORT, () => console.log(`Listening on Port ${PORT}`))

const wss = new Server({ server });

// ... 其余代码不变

同时确认.env文件里的MAIN_PORT设置正确,比如:

MAIN_PORT=23

3. 前端WebSocket连接后立即关闭

前端sendMessage函数中,channel?.stream.listen的回调里立刻执行channel!.sink.close(),可能导致消息还未完全发送到后端,连接就被断开。后端还没来得及处理消息,前端就已经关闭连接。

解决:
移除立即关闭的逻辑,或者在收到后端回复后延迟关闭,或者让连接保持更长时间:

channel?.stream.listen((event) {
  if (event != null) {
    if (kDebugMode) {
      print(event);
    }
  }
  // 注释掉这行,不要立即关闭
  // channel!.sink.close();
});

如果需要主动关闭,可以在发送消息后设置延迟,或者根据业务逻辑判断时机。

4. VPS云安全组端口未开放

虽然你关闭了VPS的防火墙,但OVHcloud的VPS默认有云安全组(Network Security Group),需要手动开放MAIN_PORT端口的入站规则,否则外部请求无法到达VPS的该端口。

解决:
登录OVHcloud控制台,找到对应VPS的安全组设置,添加入站规则:允许TCP协议的MAIN_PORT端口访问,来源设置为0.0.0.0/0(测试用,生产环境建议限制IP)。

5. 前端sendObject函数未实现

页面上的"Send Object"按钮调用了sendObject()函数,但代码中没有实现该函数,点击会导致报错,影响测试。建议补充实现或暂时注释该按钮。


内容的提问来源于stack exchange,提问作者exileValor

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 20:05:59