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

如何将C程序发送的UDP数据报解析为Dart类?

在Dart中解析C结构体格式的UDP数据包

Dart没有C语言中memcpy那样直接将字节块映射为结构体的能力——毕竟Dart是类型安全的托管语言,不会直接暴露底层内存操作。但你可以通过两种可靠的方案实现C结构体的解析:手动逐字段解析,或者使用第三方库简化流程。

核心前提:确认类型映射与内存布局

在解析前必须明确两个关键信息:

  • 类型字节对应:C类型与Dart的字节读取方法对应关系:
    • uint16_t → Dart用ByteData.getUint16()读取,结果存为int(Dart的int是任意精度,完全兼容2字节无符号值)
    • float → ByteData.getFloat32()
    • int(通常是4字节)→ ByteData.getInt32()
    • double → ByteData.getFloat64()
  • 字节序与对齐填充:
    1. 字节序:确认C程序发送时使用的是大端还是小端(多数系统默认小端),Dart解析时必须对应。
    2. 内存对齐:C编译器会自动给结构体字段补填充字节(比如为了对齐到8字节边界),你需要先在C中用offsetof宏确认每个字段的实际内存偏移,避免解析错位。

示例:在C中确认结构体内存布局

#include <stddef.h>
#include <stdio.h>

struct mainInput{
   float p1;
   int p2;
   double p3;
};

struct mainOutput{
   float p1;
   int p2;
   double p3;
};

struct DataLoggerMessage
{
   uint16_t loggerFlag;
   uint16_t eventOnOff;
   uint16_t generatedEventNumber;
   uint16_t systemPausedFlag;
   struct mainInput input;
   struct mainOutput output;
};

int main() {
  printf("loggerFlag offset: %zu\n", offsetof(struct DataLoggerMessage, loggerFlag));
  printf("eventOnOff offset: %zu\n", offsetof(struct DataLoggerMessage, eventOnOff));
  printf("input.p1 offset: %zu\n", offsetof(struct DataLoggerMessage, input.p1));
  printf("total struct size: %zu\n", sizeof(struct DataLoggerMessage));
  // 打印所有字段偏移确认布局
  return 0;
}

方案1:手动逐字段解析

用Dart内置的ByteData和Uint8List直接操作字节流,适合需要精细控制的场景:

1. 定义Dart实体类

class MainInput {
  final double p1; // Dart中float用double存储
  final int p2;
  final double p3;

  MainInput({required this.p1, required this.p2, required this.p3});
}

class MainOutput {
  final double p1;
  final int p2;
  final double p3;

  MainOutput({required this.p1, required this.p2, required this.p3});
}

class DataLoggerMessage {
  final int loggerFlag;
  final int eventOnOff;
  final int generatedEventNumber;
  final int systemPausedFlag;
  final MainInput input;
  final MainOutput output;

  DataLoggerMessage({
    required this.loggerFlag,
    required this.eventOnOff,
    required this.generatedEventNumber,
    required this.systemPausedFlag,
    required this.input,
    required this.output,
  });
}

2. 实现解析函数

DataLoggerMessage parseDataLoggerMessage(Uint8List rawUdpData) {
  final byteData = ByteData.sublistView(rawUdpData);
  var offset = 0;
  const endian = Endian.little; // 对应C的小端字节序,根据实际调整

  // 解析uint16_t字段
  final loggerFlag = byteData.getUint16(offset, endian);
  offset += 2;

  final eventOnOff = byteData.getUint16(offset, endian);
  offset += 2;

  final generatedEventNumber = byteData.getUint16(offset, endian);
  offset += 2;

  final systemPausedFlag = byteData.getUint16(offset, endian);
  offset += 2;

  // 解析MainInput
  final inputP1 = byteData.getFloat32(offset, endian);
  offset += 4;

  final inputP2 = byteData.getInt32(offset, endian);
  offset += 4;

  // 注意:如果C结构体有对齐填充,这里可能需要跳过填充字节,比如offset += 4(假设填充4字节)
  final inputP3 = byteData.getFloat64(offset, endian);
  offset += 8;

  final input = MainInput(p1: inputP1, p2: inputP2, p3: inputP3);

  // 解析MainOutput
  final outputP1 = byteData.getFloat32(offset, endian);
  offset += 4;

  final outputP2 = byteData.getInt32(offset, endian);
  offset += 4;

  final outputP3 = byteData.getFloat64(offset, endian);
  offset += 8;

  final output = MainOutput(p1: outputP1, p2: outputP2, p3: outputP3);

  return DataLoggerMessage(
    loggerFlag: loggerFlag,
    eventOnOff: eventOnOff,
    generatedEventNumber: generatedEventNumber,
    systemPausedFlag: systemPausedFlag,
    input: input,
    output: output,
  );
}

方案2:使用第三方库简化解析

如果结构体复杂,手动解析容易出错,可以用struct这类第三方库(在pubspec.yaml中添加依赖),它允许你像定义C结构体一样声明格式,自动处理解析:

示例代码

import 'package:struct/struct.dart';

// 定义子结构体格式
final mainInputStruct = Struct()
  .addFloat32('p1')
  .addInt32('p2')
  .addFloat64('p3');

final mainOutputStruct = Struct()
  .addFloat32('p1')
  .addInt32('p2')
  .addFloat64('p3');

// 定义主结构体格式
final dataLoggerStruct = Struct()
  .addUint16('loggerFlag')
  .addUint16('eventOnOff')
  .addUint16('generatedEventNumber')
  .addUint16('systemPausedFlag')
  .addStruct('input', mainInputStruct)
  .addStruct('output', mainOutputStruct);

// 解析函数
DataLoggerMessage parseWithStructLib(Uint8List rawData) {
  final parsedMap = dataLoggerStruct.unpack(rawData, Endian.little);
  
  return DataLoggerMessage(
    loggerFlag: parsedMap['loggerFlag'] as int,
    eventOnOff: parsedMap['eventOnOff'] as int,
    generatedEventNumber: parsedMap['generatedEventNumber'] as int,
    systemPausedFlag: parsedMap['systemPausedFlag'] as int,
    input: MainInput(
      p1: parsedMap['input']['p1'] as double,
      p2: parsedMap['input']['p2'] as int,
      p3: parsedMap['input']['p3'] as double,
    ),
    output: MainOutput(
      p1: parsedMap['output']['p1'] as double,
      p2: parsedMap['output']['p2'] as int,
      p3: parsedMap['output']['p3'] as double,
    ),
  );
}

关键注意事项

  1. 必须严格匹配C结构体的字节序,如果发送端是大端,解析时要改成Endian.big。
  2. 一定要处理内存对齐填充:如果C结构体的实际大小大于字段字节总和,说明存在填充字节,解析时需要手动跳过对应偏移量。
  3. Dart的float类型实际上是double,所以解析C的float后直接存为double即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 13:45:53