【问题标题】:Unable to use a structure to store and later on print udp data usin recvfrom无法使用结构来存储和稍后使用 recvfrom 打印 udp 数据
【发布时间】:2020-08-01 09:09:58
【问题描述】:

我正在尝试使用以下结构将接收到的数据放入:

typedef struct packet {
  uint8_t magic;
  uint8_t version;
  uint16_t body_length;
  char *body;
} packet;

这是打印功能:

void displayPacket (struct packet p){
  printf("Magic : %u\n",p.magic);
  printf("Version : %u\n",p.version);
  printf("Body Length: %d\n",ntohs(p.body_length));
  printf("Body : %s\n",p.body);
}

在我的主要工作中,我正在尝试将接收到的数据保存在我的结构中:

unsigned char reply[1024];
  struct packet reply_packet;
  reply_packet.body = malloc(1021);
  reply_packet.body[1020] = '\0';
  rc = recvfrom(s,&reply_packet,sizeof(reply_packet),0,NULL,NULL);
  displayPacket(reply_packet);
  free(reply_packet.body);
  close(s);

输出:

Magic : 95
Version : 1
Body Length: 1008
[1]    4741 segmentation fault  ./network

Magic、Version、Body Length 是数据包开头的预期输出。由于其他对等方遵循的协议,数据包的最大限制大小为 1024 字节。

但是,我的 displayPacket 函数引发了分段错误,更准确地说是这一行:

printf("Body : %s\n",p.body);

如果有帮助的话,这是 valgrind 的输出:

==4886== Invalid read of size 1
==4886==    at 0x4C32CF2: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4886==    by 0x4E994D2: vfprintf (vfprintf.c:1643)
==4886==    by 0x4EA0F25: printf (printf.c:33)
==4886==    by 0x108EFD: displayPacket (network.c:143)
==4886==    by 0x1090FF: main (network.c:189)
==4886==  Address 0x3030000000000000 is not stack'd, malloc'd or (recently) free'd
==4886== 
==4886== 
==4886== Process terminating with default action of signal 11 (SIGSEGV)
==4886==  General Protection Fault
==4886==    at 0x4C32CF2: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4886==    by 0x4E994D2: vfprintf (vfprintf.c:1643)
==4886==    by 0x4EA0F25: printf (printf.c:33)
==4886==    by 0x108EFD: displayPacket (network.c:143)
==4886==    by 0x1090FF: main (network.c:189)

这已经困扰了我好几个小时了,尽管它看起来很微不足道......

预期的输出将是数据包中其余内容的内容。到目前为止,我已经能够做到。

请问你有什么办法可以解决这个问题?

谢谢。

编辑 1:

通过如下修改结构解决了分段错误问题:

typedef struct packet {
  uint8_t magic;
  uint8_t version;
  uint16_t body_length;
  char body[1021];
} packet;

以及其余代码:

unsigned char reply[1024];
  struct packet reply_packet;
  reply_packet.body[1020] = '\0';
  rc = recvfrom(s,&reply_packet,sizeof(reply_packet),0,NULL,NULL);
  printf("body[0] %d\n",reply_packet.body[0]);
  displayPacket(reply_packet);
  close(s);

displayPacket 保持不变。

【问题讨论】:

  • char *body; 该成员(指针的实际值;不是它指向的缓冲区)将被 UDP 数据包中的任何内容覆盖。它将在发件人地址空间中包含一个地址,在发件人主机上的发件人进程中。简而言之,reply_packet.bodyrecvfrom 之后持有一个毫无价值的指针。在伤口上撒盐,这也会导致泄漏您之前仅分配两行的内存,因为您正在覆盖指向分配内存的唯一未完成的指针。

标签: c struct udp packet recvfrom


【解决方案1】:

packet.body 是一个指针。传递指针实际上没有意义,因为远程机器上数据的地址是无用的,而且您将始终只读取主体的 sizeof(pointer) 字节。你可以这样做:

typedef struct packet {
  uint8_t magic;
  uint8_t version;
  uint16_t body_length;
  char body[MAX_BODY_SIZE];
} packet;

您在 printf 中崩溃了,因为您取消了对错误指针的引用。即使 body 具有可变长度,您也可以将 MAX_BODY_SIZE 设置为可能的最大尺寸,然后使用 body_length 字段计算实际尺寸。

你也可以去掉reply变量,因为它不被使用。

更新:你已经接近了。您正在将正文打印为数字。既然你提到了 ASCII,我假设它是一个字符串。此外,您需要根据正文的实际长度终止 NULL。这是您需要的:

struct packet reply_packet;
  rc = recvfrom(s,&reply_packet,sizeof(reply_packet),0,NULL,NULL);
  reply_packet.body[packet.body_length] = '\0';     // null term after the body length 
  printf("body[0] %s\n",reply_packet.body);  // change to %s and remove [0], you want the pointer not a character
  displayPacket(reply_packet);

【讨论】:

  • 谢谢我在这里摆脱了段错误。我已经这样做了,在用 char 数组替换 char 指针后:memset(reply_packet.body,0,1021); reply_packet.body[1020] = '\0';,但是当我尝试打印正文时,它正在打印一个 ASCII 字符,一个灰色块,这正常吗?我能做些什么来解决这个问题?我知道这是原始问题的偏差...
  • 你不需要 memset 调用。 recvfrom 调用将填充整个 replay_packet 结构。您是否也更新了发送端的代码?
  • 删除了 memset 调用,效果一样!你是说我的发送方?如果是这样,不,我没有修改任何东西。在发送方(我从中接收数据的对等方),不,我不控制它,但我们尊重相同的协议
  • 远端是否发送指针?如果是这样,则需要有人在发送端修复它。
  • 不,远端不应该发送指针。我猜你的答案解决了段错误,但 ASCII 问题仍然存在。你对此有什么想法吗?
猜你喜欢
  • 1970-01-01
  • 2019-01-12
  • 1970-01-01
  • 1970-01-01
  • 2012-12-11
  • 2016-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多