recvfrom用零填充缓冲区
我正在尝试通过udp实现距离向量路由。我有以下结构: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
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*
并通过