Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
recvfrom用零填充缓冲区_C_Sockets_Udp_Recvfrom - Fatal编程技术网

recvfrom用零填充缓冲区

recvfrom用零填充缓冲区,c,sockets,udp,recvfrom,C,Sockets,Udp,Recvfrom,我正在尝试通过udp实现距离向量路由。我有以下结构: struct cost_info { uint8_t id; uint8_t pad; uint16_t cost; }__attribute__((__packed__)); struct advertisement_packet { uint8_t type; uint8_t version; uint16_t num_updates; struct cost_info d

我正在尝试通过udp实现距离向量路由。我有以下结构:

struct cost_info { 
    uint8_t id; 
    uint8_t pad;
    uint16_t cost; 
}__attribute__((__packed__));

struct advertisement_packet { 
    uint8_t type;
    uint8_t version;
    uint16_t num_updates;
    struct cost_info data[];
}__attribute__((__packed__));
因此,数据包是1字节类型、1字节版本、2字节更新计数,然后是4*更新计数字节的数据。我使用灵活的数组成员,这样我就可以

sendto(fd, pkt, sizeof(struct advertisement_packet) + pkt->num_updates * sizeof(struct cost_info), 0, addr, sizeof(struct sockaddr_in));
把所有的东西放在一个包里寄出去。需要发送一个数据包。 我能够使用wireshark验证数据是否正确发送。在接收端,我做两次接收。第一个获取前4个字节,这样我就可以知道需要多少更新。然后,我相应地调整接收器缓冲区的大小,并接收4*num_更新字节。问题是,这第二次接收用零填充我的缓冲区!如果我期望12字节的数据,那么我得到12字节的零。下一次我从这个套接字读取数据时,我得到了预期的下一个数据包的开头。为什么会发生这种情况

这是我的接收代码,如果需要的话

recvfrom(i, pkt, sizeof(struct advertisement_packet),0,NULL,0);
//reallocate more space if the incoming data will need it
if (pkt->num_updates > pkt_cost_slots) {
  pkt_cost_slots *= 2;
  pkt = realloc(pkt,sizeof(struct advertisement_packet) + sizeof(struct cost_info) * pkt_cost_slots);
  assert(pkt);
}
//receive data
recvfrom(i,pkt->data,pkt->num_updates * sizeof(struct cost_info),0,NULL,0);

UDP是一种数据报协议。整个数据包被传递到第一个
recvfrom
调用中。如果缓冲区大小不足,则数据字节将丢失。它们不会传递给后续的
recvfrom

因此,解决方案是只传入一个足以容纳任何UDP数据报的缓冲区,然后将其转换为您的数据类型

unsigned char buffer[65535];
struct advertisement_packet *pkt = (advertisement_packet *)buffer;
recvfrom(i, buffer, sizeof(buffer), (sockaddr*)&addr, &addrlen);
这里有一个更好的解决方案,它还执行常规的安全检查,这样您就不会被恶意攻击者欺骗到缓冲区溢出中

int result = 0;
unsigned char buffer[65535];
struct advertisement_packet *pkt = (advertisement_packet *)buffer;
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int accepted = false;

result = recvfrom(i, buffer, sizeof(buffer), (sockaddr*)&addr, &addrlen);

if (result > 0)
{
    size_t expected_size = sizeof(struct advertisement_packet) + pkt->num_updates * sizeof(struct cost_info);

    if (result >= expected_size)
    {
        accepted = true;
    }
    else
    {
        // packet is malformed or undersized, so let's avoid a buffer overrun
        result = -1;
    }
}

if (accepted)
{
     // got packet and it's valid
     // continue parsing it, or make a copy of it.
}

UDP是一种数据报协议。整个数据包被传递到第一个
recvfrom
调用中。如果缓冲区大小不足,则数据字节将丢失。它们不会传递给后续的
recvfrom

因此,解决方案是只传入一个足以容纳任何UDP数据报的缓冲区,然后将其转换为您的数据类型

unsigned char buffer[65535];
struct advertisement_packet *pkt = (advertisement_packet *)buffer;
recvfrom(i, buffer, sizeof(buffer), (sockaddr*)&addr, &addrlen);
这里有一个更好的解决方案,它还执行常规的安全检查,这样您就不会被恶意攻击者欺骗到缓冲区溢出中

int result = 0;
unsigned char buffer[65535];
struct advertisement_packet *pkt = (advertisement_packet *)buffer;
sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int accepted = false;

result = recvfrom(i, buffer, sizeof(buffer), (sockaddr*)&addr, &addrlen);

if (result > 0)
{
    size_t expected_size = sizeof(struct advertisement_packet) + pkt->num_updates * sizeof(struct cost_info);

    if (result >= expected_size)
    {
        accepted = true;
    }
    else
    {
        // packet is malformed or undersized, so let's avoid a buffer overrun
        result = -1;
    }
}

if (accepted)
{
     // got packet and it's valid
     // continue parsing it, or make a copy of it.
}

不能将一个数据报的接收拆分为两个
recv()
/
recvfrom()
调用。这是面向数据报的套接字的基本特性,它以数据报为单位进行发送和接收操作。但是,您可以读取部分或全部数据报,而无需将其从接收队列中移除(即“偷看”)。这样说:

对于基于消息的套接字,例如
SOCK\u RAW
SOCK_DGRAM
SOCK_SEQPACKET
,整个消息应 可以在一次操作中读取。如果消息太长,无法放入 提供的缓冲区,并且未在flags参数中设置
MSG_PEEK
,则 多余的字节将被丢弃


因此,您可以在第一次调用
recvfrom()
时使用
MSG_PEEK
来实现您的目标,但请注意,然后您需要在第二次调用
recvfrom()
时接收整个数据报,而不仅仅是第一次未读取的部分。

您不能将一个数据报的接收拆分为两个
recv()
/
recvfrom()
调用。这是面向数据报的套接字的基本特性,它以数据报为单位进行发送和接收操作。但是,您可以读取部分或全部数据报,而无需将其从接收队列中移除(即“偷看”)。这样说:

对于基于消息的套接字,例如
SOCK\u RAW
SOCK_DGRAM
SOCK_SEQPACKET
,整个消息应 可以在一次操作中读取。如果消息太长,无法放入 提供的缓冲区,并且未在flags参数中设置
MSG_PEEK
,则 多余的字节将被丢弃


因此,您可以在第一次调用
recvfrom()
时使用
MSG_PEEK
来实现您的目标,但是请注意,在第二次调用
recvfrom()
时,您需要接收整个数据报,而不仅仅是第一次没有读取的部分。

这是一个好主意,因为不必重新分配内存。然而,我只是尝试了一下,并没有解决问题。它为我的测试接收了预期的16个字节,第一个字节仍然正确,其余的数据仍然归零。这是一个好主意,因为不必重新分配内存。然而,我只是尝试了一下,并没有解决问题。它为我的测试接收了预期的16个字节,第一个字节仍然正确,其余数据仍然归零。请尝试此操作。我能够得到尺寸,然后第二次读取完整的数量。不幸的是,即使第二次读取时说它得到了全部16个字节,数据还是被清零了。@Sandles,如果您可以在接收端(例如通过wireshark)验证传入数据的结构是否符合预期,那么我最好的猜测是接收者对
struct advertization\u packet
和/或
struct cost\u info
的布局的想法与发送者不同。这可能使接收到的数据看起来都是零。我建议通过将
pkt
转换为
unsigned char*
来检查接收到的实际数据,并逐字节进行检查。我铸造了它,它肯定是在获取数据。我仍然需要弄清楚为什么struct access返回0,但我正在获取数据,这才是重要的。非常感谢。试过这个。我能够得到尺寸,然后第二次读取完整的数量。不幸的是,即使第二次读取时说它得到了全部16个字节,数据还是被清零了。@Sandles,如果您可以在接收端(例如通过wireshark)验证传入数据的结构是否符合预期,那么我最好的猜测是接收者对
struct advertization\u packet
和/或
struct cost\u info
的布局的想法与发送者不同。这可能使接收到的数据看起来都是零。我建议通过将
pkt
转换为
unsigned char*
并通过