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

Node FFI中结构体指针用法技术咨询

Node FFI 结构体指针用法详解

我懂你找资料时的头疼——Node FFI的结构体指针文档确实不算太直观,结合你的代码,我一步步给你讲清楚具体用法:

1. 先修正结构体定义(关键细节)

你的结构体里有个小问题:C标准的struct sockaddr_in里对应IP地址的字段是sin_addr,不是in_addr;另外sin_zero是8字节的char数组,不是单个char。先修正你的定义(需要依赖ref和ref-struct包):

const ref = require('ref');
const Struct = require('ref-struct');

// 定义 in_addr 结构体
const struct_in_addr = Struct({
  's_addr': 'ulong' // 对应C里的 in_addr_t,通常是unsigned long
});

// 定义8字节char数组类型,用于sin_zero
const sin_zero_array = ref.arrayType('char', 8);

// 修正后的 sockaddr_in 结构体
const struct_sockaddr_in = Struct({
  'sin_family': 'short',   // AF_INET 对应数值2
  'sin_port': 'ushort',    // 端口号,需要转网络字节序
  'sin_addr': struct_in_addr,
  'sin_zero': sin_zero_array // 填充用的8字节数组
});

2. 定义结构体指针类型

要传递结构体指针给C函数,首先得用ref.refType()生成对应的指针类型:

// 生成 struct sockaddr_in* 类型
const struct_sockaddr_in_ptr = ref.refType(struct_sockaddr_in);

3. 创建结构体实例并获取指针

先实例化结构体,给字段赋值(注意网络字节序转换),再通过.ref()方法获取指针:

// 创建结构体实例
const sockaddr_in = new struct_sockaddr_in();

// 赋值:AF_INET
sockaddr_in.sin_family = 2;
// 端口号8080,转网络字节序(大端)
sockaddr_in.sin_port = ref.htons(8080);
// IP地址0.0.0.0,转网络字节序
sockaddr_in.sin_addr.s_addr = ref.htonl('0.0.0.0');
// 填充sin_zero为0(可选,C里通常会 memset 为0)
sockaddr_in.sin_zero.fill(0);

// 获取结构体指针(struct sockaddr_in*)
const sockaddr_ptr = sockaddr_in.ref();

4. 在FFI函数声明中使用指针类型

假设你的libredir里有个函数需要接收struct sockaddr_in*,比如setup_redir(struct sockaddr_in* addr, int len),那函数声明要这样写:

const ffi = require('ffi');

var redir = ffi.Library('./libredir', {
  // 第一个参数是struct sockaddr_in*,第二个是结构体长度
  'setup_redir': ['int', [struct_sockaddr_in_ptr, 'int']]
});

5. 调用函数传递指针

直接把刚才生成的指针传进去,同时传入结构体的大小(C函数通常需要知道结构体长度):

const result = redir.setup_redir(sockaddr_ptr, ref.sizeof(struct_sockaddr_in));
if (result !== 0) {
  console.error('Redirection setup failed');
}

6. 处理C函数返回的结构体指针

如果你的C函数会返回一个分配好的结构体指针(比如struct sockaddr_in* create_addr(char* ip, ushort port)),处理方式如下:

// 先声明函数返回类型为结构体指针
var redir = ffi.Library('./libredir', {
  'create_addr': [struct_sockaddr_in_ptr, ['char*', 'ushort']],
  'free_addr': ['void', [struct_sockaddr_in_ptr]] // C里必须提供释放内存的函数
});

// 调用函数获取指针
const addr_ptr = redir.create_addr('127.0.0.1', ref.htons(3000));
// 解引用指针得到结构体实例
const addr = addr_ptr.deref();

// 使用结构体数据
console.log(`IP: ${ref.ntohl(addr.sin_addr.s_addr)}, Port: ${ref.ntohs(addr.sin_port)}`);

// 必须手动调用C的free函数释放内存,否则会泄漏
redir.free_addr(addr_ptr);

关键注意事项

  • 字节序转换:网络协议用大端字节序,Node的字节序取决于系统,所以必须用ref.htons()(端口)、ref.htonl()(IP)转成网络字节序;读取时用ref.ntohs()、ref.ntohl()转回来。
  • 内存管理:Node创建的结构体实例内存由V8垃圾回收管理,但C函数分配的内存必须手动调用对应的free函数释放。
  • 字段类型匹配:确保Node里的类型和C里的类型严格对应,比如short对应C的short,ulong对应C的unsigned long,避免内存对齐问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:37:10