我正在使用名为lwip的TCP / IP堆栈。我已经在下面实现了一个函数来发送数据包,灵感来自一个接收数据包的类似回调函数。
每次收到数据包时,我都会使用pbuf_alloc
函数创建一个缓冲区。然后,我使用udp_sendto
发送数据包。最后,我使用pbuf_free
释放缓冲区。 (参见下面的代码。)
由于某种原因,pbuf_free
没有释放缓冲区。 (n
数据包后出现缓冲区溢出,其中n
是池大小。)The lwip wiki警告:
网络驱动程序也可能不会假设pbuf内存是 当它调用pbuf_free时实际释放。
如何强制pbuf_free
释放我的缓冲区?如何避免缓冲区溢出?
(我的实现如下。)
static err_t IAP_tftp_send_data_packet(struct udp_pcb *upcb, struct ip_addr *to, int to_port, int block)
{
err_t err;
struct pbuf *pkt_buf;
char packet[TFTP_DATA_PKT_LEN_MAX];
int bytesRead;
int bytesToSend;
/* Specify that we are sending data. */
IAP_tftp_set_opcode(packet, TFTP_DATA);
/* Specify the block number that we are sending. */
IAP_tftp_set_block(packet, block);
bytesRead = IAP_tftp_set_data(packet, block);
if(bytesRead != 0) {
bytesToSend = TFTP_DATA_PKT_LEN_MAX - (512 - bytesRead + 1);
} else {
bytesToSend = TFTP_DATA_PKT_LEN_MAX - 512;
}
pkt_buf = pbuf_alloc(PBUF_TRANSPORT, bytesToSend, PBUF_POOL);
if (!pkt_buf)
{
print("(TFTP) Buffer overflow!\r\n");
}
/* Copy the file data onto pkt_buf. */
memcpy(pkt_buf->payload, packet, bytesToSend);
err = udp_sendto(upcb, pkt_buf, to, to_port);
/* free the buffer pbuf */
printf("%d\n\r", pbuf_free(pkt_buf));
return err;
}
答案 0 :(得分:7)
您使用的是什么版本的lwIP? 根据不同的版本,答案会有很大不同。
在pbuf_alloc()内部调用的memp_malloc()分配函数失败或pbufs链接失败。因此,它返回NULL。
如果传递的参数也包含NULL,则pbuf_alloc()也将返回NULL。(由于NULL参数检查)。在较新的版本中,您能否显示MEMP_OVERFLOW_CHECK宏包含的值?当宏值> = 2时,lwIP显示不同的行为。
另一个原因可能是如果你使用多线程,pbuf_alloc()内部的锁定机制会失败,可能会导致它返回NULL。
某些版本要求您在调用pbuf_alloc()之前调用pbuf_init()。
你可以试试这个:
pkt_buf = NULL;//Use NULL, just incase the NULL is not 0 as per your compiler.
pkt_buf = pbuf_alloc(PBUF_TRANSPORT, bytesToSend, PBUF_REF);
if(pkt_buf == NULL)
{
printf("pbuf_alloc failed.\n");
}
else
{
/* Do something with the allocated pbufs and free it. */
}
PBUF_REF将不为pbuf分配缓冲区内存。 pbuf应仅在单个线程中使用,如果pbuf排队,则应调用pbuf_take来复制缓冲区。
您还可以尝试PBUF_RAM,它将在RAM中分配缓冲区。
要获得更多信息,您还可以浏览您正在使用的lwIP版本的源文件。
答案 1 :(得分:6)
最简单的解决方案似乎是制作缓冲区static
,即为每次调用重复使用相同的缓冲区:
static struct pbuf *pkt_buf = NULL;
if( pkt_buf == NULL )
pkt_buf = pbuf_alloc(PBUF_TRANSPORT, bytesToSend, PBUF_POOL);
if( pkt_buf == NULL )
{
print("(TFTP) Buffer overflow!\r\n");
}
如果您的方案涉及卸载/重新加载驱动程序,则会泄漏内存。要解决这个问题,请在IAP_tftp_send_data_packet()
函数之外使缓冲区静态,并在驱动程序卸载时调用pbuf_free()
(假设lwip告诉你)。
答案 2 :(得分:0)
只是一个过去的想法,可能完全没有意义。在这段代码中:
if(bytesRead != 0) {
bytesToSend = TFTP_DATA_PKT_LEN_MAX - (512 - bytesRead + 1);
} else {
bytesToSend = TFTP_DATA_PKT_LEN_MAX - 512;
}
pkt_buf = pbuf_alloc(PBUF_TRANSPORT, bytesToSend, PBUF_POOL);
... bytesRead
是否可以假设值513 - TFTP_DATA_PKT_LEN_MAX?
如果它发生了,那么分配零字节的请求不会失败吗? (这可以通过在缓冲区溢出时打印bytesToSend的值来测试,并检查它是否为非零)。
答案 3 :(得分:0)
struct pbuf不代表连续的内存区域。它更像是一系列内存位置。因此,这在一般情况下不起作用:
memcpy(pkt_buf->payload, packet, bytesToSend);
您需要分散复制数据。代码片段中的memcpy()可能会溢出有效负载缓冲区并导致各种副作用,包括无法完全释放pbuf链。