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
相关产品推荐
相关产品推荐

