【问题标题】:breaking up a char array into a structure将 char 数组分解为结构
【发布时间】:2013-09-09 02:01:15
【问题描述】:

如果以后有人遇到这个问题,我会留下这个。

*注意这种方法在从 C 客户端到 C 服务器时不起作用。这只适用于 Java 客户端到 C 服务器。所以我不得不放弃这种方法。

好吧,我已经和 C 吵了太久了。我正在使用 UDP 将一些信息从 java 客户端传递到 C 服务器。我可以在那里获取信息,但我不确定如何拆分消息以存储到这样的结构中

struct __attribute__((__packed__)) clientMessage
{
  short tml; 
  short rid;  
  char op;        
  char message[MAXBUFLEN-5];
};

我收到这样的消息 其中 test 是 char test[MAXBUFLEN-5];

if ((numbytes = recvfrom(sockfd, test, MAXBUFLEN-1, 0,
  (struct sockaddr *)&their_addr, &addr_len)) == -1) {
     perror("recvfrom");
     exit(1);}

所以,我需要获取消息“7 2 1Yo”(两个 2 字节的短裤和一个字符,后跟一个未知的消息长度)并将其存储到结构中的适当部分。消息被正确发送,我只是无法将它分解成我需要的信息。我正在尝试

memcpy(&cm.rid, &test, 2);
memcpy(&cm.tml, &test[1], 2);
memcpy(&cm.op, &test[4], 1);
memcpy(&cm.message, &test[5], MAXBUFLEN-5);

但我的结果最终是

Message: Yo
OP: 1Yo
RID: 7 1Yo
TML: 2 7 1Yo

应该是的

Message: Yo
OP: 1
RID: 2
TML: 7

我成功收到了消息,但没有别的。我对C比较陌生,所以请原谅我的无知。我猜这真的很容易,但我想。

【问题讨论】:

  • //empty,你的意思是0吗?为什么不是char test[MAXBUFLEN]?为什么test 的内容在你的结构中颠倒了rid 和tml 的顺序?您将一个指向数组的指针传递给memcpy() 作为源地址(如果test 是一个数组,但它的类型错误,您不会注意到差异)。如果您的意思是 &test[0],您可以通过删除 & 来修复。不应该是memcpy(&cm.tml, &test[2], 2);吗?
  • 您考虑过字节序问题吗?另外,如果rid 和tml 都是两个字节,你为什么要从&test[1] 中memcpy-ing 到tml?
  • 为了理智起见,我还建议您对结构成员进行排序以匹配它们在网络流中出现的顺序。
  • 您要打印什么?它不应该是 4 个十六进制数字,还是 5 个十进制数字(可能是负数)?我还是不明白空是什么意思。
  • 暂时忘记它是空的。当我打印出整个字符“test”时,我发送的消息“7 2 1Yo”打印得很好。当试图从该字符中提取我需要的信息时,我得到了奇怪的结果。

标签: c memcpy


【解决方案1】:

第一行应该是memcpy(&cm.rid, &test[0], 2);,因为地址应该是第一个字节的地址。

其余的:

memcpy(&cm.tml, &test[2], 2); // you want to get the third and forth byte, begin with index 2.
memcpy(&cm.op, &test[4], 1);  // the fifth byte, begin with index 4.
memcpy(&cm.message, &test[5], MAXBUFLEN-5); // the rest bytes.

【讨论】:

  • test 和 &test[0] 是一回事
  • @ScottyBauer 是的,我只是想按照作者的代码保持。
  • 我更新了我的问题。我清理了我的代码以更好地匹配它。我仍然遇到同样的问题。
  • @ChaunceyPhilpot 你的代码没有像你说的那样改变。 Endian 是另一个问题,需要更多细节。但我认为您的问题与字节顺序无关。
【解决方案2】:

如果您使用的是二进制协议,最好使用例如在clientMessage 的声明中使用int16_t 而不是short,因为在c 标准中没有指定short 的大小。

要访问存储在test 中的消息,您可以简单地执行以下操作:

struct clientMessage *cm = (struct clientMessage *) test;

您还应该注意到,网络协议的字节序不同于 x86 和 amd64 架构的字节序,因此您可能会收到 10752 而不是 42。 要解决此问题,您可以使用ntohs()(网络到主机短)功能访问tm 和rid。

【讨论】:

  • sizeof(short) 没有在 C 中指定,但它的 minimum 范围被指定为 least -32767 到 32767,因此 short 可以存储int16_t 可以存储的所有整数,唯一可能的例外是-32768。无论如何,我也更喜欢int16_t 而不是short。
  • @chux:关键是你不希望它变大。如果连接的另一端需要两个字节,您不希望仅仅因为连接这一端的实现超过最小值并且有 32 位短而意外发送四个字节。
  • @Paul Griffiths 我对 Ruge Bolek 帖子的担忧是“未指定短裤的大小”,尽管这是真的,但由于对短裤的限制,这是有误导性的。 OP 的问题是 Java 短片和 C something 之间的消息传递。 C 类型int16_t(如果可用)与 Java 缩写匹配,因此使其成为首选类型。如果int16_t 不可用,接收端可能需要使用更大的类型,当然在发送时应该限定其值。
【解决方案3】:

对第一部分使用 sscanf(),对文本使用 memcpy()。

const char *message = "7 2 1Yo";
struct clientMessag cM;  
int offset = 0;
int n;
n = sscanf(message, "%hd %hd %c%n", &cM.tml, &cM.rid, &cM.op, &offset);
size_t SuffixLength = strlen(&message[offset]);
if ((n != 3) || (SuffixLength >= sizeof(cM.message))) {
  exit(1);  // handle syntax error;
}
memcpy(cM.message, &message[offset], SuffixLength + 1);
// Additional field checks like IsOKOpCode(cM.op)

我避免使用%s,因为它不会将空格存储到您的消息中。
错误检查总是好事。建议进行额外的实地检查。


顺便说一句:不清楚cM.op 字段是否应该被视为文本或数字。 OP 是否需要存储 1 或“1”?以上假设文本。或者,可以使用"%hd%hd%hhd%n"。

【讨论】:

    【解决方案4】:

    由于test 包含字符串"7 2 1Yo",您必须将字符串解析为您想要的值。你可以通过sscanf() 做到这一点:

    char fmt[256];
    snprintf(fmt, sizeof(fmt), "%%hd %%hd %%c%%%ds", MAXBUFLEN-5-1);
    sscanf(test, fmt, &cm.rid, &cm.tml, &cm.op, cm.message);
    

    在上面的代码片段中,我们创建了一个格式字符串用于解析。这是必要的,以防止sscanf() 在邮件未被\0 终止的情况下扫描超过邮件的末尾。 snprintf() 调用的结果是:MAXBUFLEN 为 128:

    "%hd %hd %c%122s"
    

    它告诉sscanf() 扫描两个短十进制数字、一个char 和一个不超过 122 个字符的字符串。

    【讨论】:

      猜你喜欢
      • 2018-07-25
      • 2015-10-26
      • 2021-09-05
      • 2018-06-11
      • 2018-05-13
      • 1970-01-01
      • 2012-02-25
      • 2017-10-27
      • 2014-04-27
      相关资源
      最近更新 更多