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

C#经Socket向C(vxWorks)发送含double/long结构体数值读取异常

故障根因

你的问题是四个点共同导致的:

  • 数据类型长度不匹配:绝大多数VxWorks为32位ILP32模型,C代码中long类型占4字节,但C#中long是固定8字节的64位整数,类型长度偏差直接导致后续所有字段的内存偏移完全错位。
  • C端打印逻辑错误:你贴的接收代码中,打印double值的语句读取的是vardata.data.value(long类型成员),并非存储double值的dvalue成员,哪怕传输完全正确,这里也不可能输出正确的double结果。
  • 结构体内存布局未对齐:C#结构体默认没有指定封送布局、对齐规则,和C端编译器生成的结构体排布不匹配。尤其是name字段,C端是内嵌100字节定长char数组,C#中直接声明为string且不指定封送规则时,默认会封送成指针或带长度前缀的字符串,而非内嵌数组,直接打乱整个结构体的内存排布。
  • 非托管内存泄漏:你写的Struct2Bytes方法申请了非托管堆内存但没有释放,长期运行会导致内存泄漏,极端情况下也会导致封送数据异常。
修复方案

C# 发送端修正

首先引入System.Runtime.InteropServices命名空间,按以下规则修改结构体定义和序列化方法:

  1. 将DESCR_DATA中对应C端long的addr、value字段从C# long改为int(匹配32位VxWorks下4字节的long长度;如果你的VxWorks是64位LP64模型,改回long即可,90%以上嵌入式VxWorks场景为32位)。
  2. 为所有结构体添加[StructLayout]特性,指定顺序布局、4字节对齐、ANSI字符集,匹配C端编译器的默认规则。
  3. 为name字段添加[MarshalAs]特性,明确封送为100字节定长内嵌字符数组,和C端char name[100]完全对应。
  4. 明确枚举底层类型为int,避免不同环境下枚举封送长度偏差。
  5. 修正序列化方法,添加非托管内存释放逻辑。

修正后的代码如下:

using System.Runtime.InteropServices;

public enum TYPE_OP : int
{
    START_INITIALIZE = 0,
    STOP_INITIALIZE = 1,
    IDENTIFY = 2,
    GET_VALUE = 3,
    SET_VALUE = 4,
    SET_RSE_MODE = 5,
    GET_VALUE_DOUBLE = 6,
    SET_VALUE_DOUBLE = 7,
    SET_STRING = 8,
    GET_VALUE_FLOAT = 9,
    SET_VALUE_FLOAT = 10,
    SET_TARGET = 11,
    GET_TARGET = 12,
    GET_TASK_STATUS = 13
}

[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct DESCR_DATA
{
    public int addr;
    public int value;
    public double dvalue;
}

[StructLayout(LayoutKind.Sequential, Pack = 4, CharSet = CharSet.Ansi)]
public struct VAR_DATA
{
    public int len;
    public TYPE_OP type;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 100)]
    public string name;
    public DESCR_DATA data;
}

public byte[] Struct2Bytes<T>(T obj) where T : struct
{
    int structSize = Marshal.SizeOf<T>();
    byte[] result = new byte[structSize];
    IntPtr unmanagedPtr = Marshal.AllocHGlobal(structSize);
    try
    {
        Marshal.StructureToPtr(obj, unmanagedPtr, false);
        Marshal.Copy(unmanagedPtr, result, 0, structSize);
    }
    finally
    {
        Marshal.FreeHGlobal(unmanagedPtr);
    }
    return result;
}

C 接收端修正

  1. 修正打印double值的字段引用错误,同时增加接收长度校验,排除粘包、半包问题。
  2. 注意long类型的打印格式符匹配。

修正后的接收逻辑代码:

bytes_read = read(client_sock, &vardata, sizeof(vardata));
if (bytes_read != sizeof(vardata))
{
    printf("Receive error: expect %zu bytes, got %zd\n", sizeof(vardata), bytes_read);
    // 增加错误处理逻辑
    close(client_sock);
    return -1;
}
printf("ENUM VALUE = %d\n", vardata.type);
printf("LONG VALUE = %ld\n", vardata.data.value);
printf("DOUBLE VALUE = %.2f\n", vardata.data.dvalue);
校验注意事项
  • 改完后先分别在两端打印结构体大小:C#端打印Marshal.SizeOf<VAR_DATA>(),C端打印sizeof(struct VAR_DATA),两个值完全相等(按上述修改应为124字节)才说明内存布局匹配。
  • 如果你的VxWorks运行在PowerPC等大端架构上,Windows为小端序,需要在发送/接收时对int、long、double类型的多字节数据做大小端转换;x86架构的VxWorks和Windows同为小端序,无需转换。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 09:12:16