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

Node.js向WASM传递连接失败:WASM模块加载报错求助

Node.js传递Socket到WASM时的模块导入错误修复

问题现象

运行node server.js可正常启动localhost:8080服务,但执行curl -v http://127.0.0.1:8080测试时,出现报错:

Error loading WASM module: [TypeError: WebAssembly.instantiate(): Import #0 module="go" error: module is not an object or function]

原代码

server.js

const net = require('net');
const { WASI } = require('wasi');
const path = require('path');
const fs = require('fs');

const server = net.createServer(socket => {
    console.log('Client connected');
    const wasi = new WASI({
        args: [],
        env: {},
        preopens: {
            '/dev': '/dev',
            '/tmp': '/tmp',
            '/etc': '/etc',
            '/var': '/var',
        },
        preview1: true,
        version: 'preview1',
    });

    const wasmFilePath = path.resolve(__dirname, './forwarder.wasm');
    const wasmModuleBuffer = fs.readFileSync(wasmFilePath);
    const goImports = { wasi_snapshot_preview1: wasi.wasiImport };

    WebAssembly.instantiate(wasmModuleBuffer, goImports).then(({ instance }) => {
        const { handleSocket } = instance.exports;
        handleSocket(socket._handle.fd); // Passing socket file descriptor to Go
    }).catch(err => {
        console.error('Error loading WASM module:', err);
    });
});

server.listen(8080, 'localhost', () => {
    console.log('Server listening on localhost:8080');
});

Go代码(go_module.go)

// go_module.go

package main

import (
    "fmt"
    "net"
    "os"
    "strconv"
    "syscall/js"
)

func handleSocket(this js.Value, inputs []js.Value) interface{} {
    // Convert the file descriptor to a net.File type
    fd := inputs[0].Int()
    file := os.NewFile(uintptr(fd), strconv.Itoa(fd))
    // Convert the net.File to a net.Listener
    listener, err := net.FileListener(file)
    if err != nil {
        fmt.Println("Error creating listener:", err)
        return nil
    }
    defer listener.Close()

    fmt.Println("Waiting for incoming connections...")
    for {
        conn, err := listener.Accept()
        if err != nil {
            fmt.Println("Error accepting connection:", err)
            return nil
        }
        defer conn.Close()
        
        go conn.Write([]byte(" HTTP/1.1 200 OK\r\nOK"))

        fmt.Println("Received connection from:", conn.RemoteAddr())
        // Handle the connection...
    }
}

func main() {
    c := make(chan struct{}, 0)
    js.Global().Set("handleSocket", js.FuncOf(handleSocket))
    <-c
}

核心问题分析

报错根源在于环境不兼容:

  • 你编写的Go代码使用了syscall/js,编译时默认是GOOS=js GOARCH=wasm目标,这类WASM依赖go模块提供的JS绑定运行时,但你在Node.js中仅配置了WASI导入,缺失go模块导致加载失败。
  • 同时,syscall/js是为浏览器WASM环境设计的API,无法在WASI环境中正常工作。

修复步骤

1. 修改Go代码,移除syscall/js依赖

改用WASI兼容逻辑,直接从命令行参数获取文件描述符:

// go_module.go
package main

import (
	"fmt"
	"net"
	"os"
	"strconv"
)

func main() {
	if len(os.Args) < 2 {
		fmt.Println("Missing FD argument")
		return
	}
	fdStr := os.Args[1]
	fd, err := strconv.Atoi(fdStr)
	if err != nil {
		fmt.Println("Invalid FD:", err)
		return
	}

	file := os.NewFile(uintptr(fd), "socket")
	listener, err := net.FileListener(file)
	if err != nil {
		fmt.Println("Error creating listener:", err)
		return
	}
	defer listener.Close()

	fmt.Println("Waiting for incoming connections...")
	for {
		conn, err := listener.Accept()
		if err != nil {
			fmt.Println("Error accepting connection:", err)
			return
		}
		go func(c net.Conn) {
			defer c.Close()
			c.Write([]byte("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nOK"))
			fmt.Println("Received connection from:", c.RemoteAddr())
		}(conn)
	}
}

2. 重新编译Go代码为WASI目标

需要Go 1.21及以上版本,执行编译命令:

GOOS=wasip1 GOARCH=wasm go build -o forwarder.wasm go_module.go

3. 调整Node.js代码,正确传递FD并初始化WASI

将Socket的FD作为命令行参数传递给WASI程序,同时确保导入对象仅包含WASI所需模块:

const net = require('net');
const { WASI } = require('wasi');
const path = require('path');
const fs = require('fs');

const server = net.createServer(socket => {
    console.log('Client connected');
    const wasi = new WASI({
        args: ['forwarder.wasm', socket._handle.fd.toString()],
        env: {},
        preopens: {
            '/dev': '/dev',
            '/tmp': '/tmp',
        },
        version: 'preview1',
    });

    const wasmFilePath = path.resolve(__dirname, './forwarder.wasm');
    const wasmModuleBuffer = fs.readFileSync(wasmFilePath);
    const imports = { wasi_snapshot_preview1: wasi.wasiImport };

    WebAssembly.instantiate(wasmModuleBuffer, imports).then(({ instance }) => {
        wasi.start(instance);
    }).catch(err => {
        console.error('Error loading WASM module:', err);
    });
});

server.listen(8080, 'localhost', () => {
    console.log('Server listening on localhost:8080');
});

4. 测试验证

重新运行node server.js,执行curl http://127.0.0.1:8080,应收到OK响应。

额外注意事项

  • 不要混合使用浏览器WASM(GOOS=js)和WASI(GOOS=wasip1)编译目标,两者运行时依赖完全不同。
  • 传递FD后,不要在Node.js侧关闭对应的Socket,否则WASI程序会失去对该文件描述符的控制权。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 04:36:09