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命名空间,按以下规则修改结构体定义和序列化方法:
- 将
DESCR_DATA中对应C端long的addr、value字段从C#long改为int(匹配32位VxWorks下4字节的long长度;如果你的VxWorks是64位LP64模型,改回long即可,90%以上嵌入式VxWorks场景为32位)。 - 为所有结构体添加
[StructLayout]特性,指定顺序布局、4字节对齐、ANSI字符集,匹配C端编译器的默认规则。 - 为
name字段添加[MarshalAs]特性,明确封送为100字节定长内嵌字符数组,和C端char name[100]完全对应。 - 明确枚举底层类型为
int,避免不同环境下枚举封送长度偏差。 - 修正序列化方法,添加非托管内存释放逻辑。
修正后的代码如下:
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 接收端修正
- 修正打印double值的字段引用错误,同时增加接收长度校验,排除粘包、半包问题。
- 注意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
相关产品推荐
相关产品推荐

