我目前正在尝试将处理数据报(udp)数据包的c程序移植到某种更高级别的语言。由于数据包的大小可以变化,因此它们以一个表示其大小的整数开头。在c中,我使用MSG_PEEK标志调用recv以仅首先接收该值,然后分配一个拟合缓冲区并读取其余的数据包。代码(简化)如下:
// Simplified message format.
struct message {
int length;
char[] text;
}
struct message *m = malloc (sizeof(int));
// Read out in just length.
recv (sock, m, sizeof(int), MSG_WAITALL | MSG_PEEK);
int txtlen = ntohl (m->length) * sizeof(char);
int msglen = sizeof(int) + txtlen;
// Read complete packet.
m = realloc (m, msglen);
read (sock, m, msglen);
m->text[txtlen] = '\0';
// Show result.
printf("%s\n", &m->text);
我想避免看似普遍的做法是分配一个巨大的缓冲区,并希望没有更大的数据包到达。那么就像在python或java等高级语言中窥视数据报或确定其完整长度一样?
答案 0 :(得分:1)
我想避免看似普遍的做法,即分配一个巨大的缓冲区,并希望数据包不会变得更大。
不确定你的意思。 UDP数据包一次到达,因此初始整数会告诉您缓冲区应该有多大;它到来后不会“增长”。
由于您附加了空字符,因此需要在长度计算中考虑该字符:
int msglen = sizeof(int) + txtlen + 1;
使用realloc()
时要小心:
m = realloc (m, msglen);
如果realloc
失败,则会将m
设置为空。这意味着你将失去对最初分配给它的内存的唯一引用,因此你永远无法free()
它。尝试这样的事情:
void *tmp = realloc(m, msglen)
if (tmp == null) {
// handle the error
}
m = tmp;
当您打印数据时,m->text
会计算出第一个字符的地址,因此您可以使用
printf("%s\n", m->text);
或者,您可以使用固定大小定义结构,如
struct message {
int length;
char *text;
}
然后您可以使用malloc()
分配(仅)您的文本缓冲区:
struct message m;
recv(sock, &m.length, sizeof(int), MSG_WAITALL | MSG_PEEK);
m.text = malloc(m.length + 1); // +1 for the null that you'll append
read(sock, m.text, m.length);
m.text(m.length) = '\0';
printf("%s\n", m.text);
free(m.text);
祝你的项目好运 - 网络编程始终是一种学习经历!
答案 1 :(得分:0)
为什么不这样做?
message = (struct message *)malloc(sizeof(struct message));
read(sock, &message->length, sizeof(int);
message->length = ntohl(message->length);
message->text = (char *)malloc(message->length + 1);
read(sock, message->text, message->length);
message->text[message->length] = 0;
答案 2 :(得分:0)
UDP datagrams限制为64K,然后ethernet frames为1500字节(除非您的网络使用jumbo frames,最多可能为9000字节)。协议设计者通常会尝试避免IP fragmentation,因此很可能您的传入数据包很小,即少于1500字节。
我将从1472的静态缓冲区开始(1500个以太网帧长度--20个字节的IP报头 - 8个字节的UDP报头)。如果你必须处理一些任意协议 - 最高可达64K。如果您负担不起 - 使用MSG_PEEK
收集实际尺寸,找到一些方便的平均值,并使用malloc(3)
设置后备计划。