8

我正在使用一个名为lwip的 TCP/IP 堆栈。我在下面实现了一个发送数据包的函数,灵感来自一个类似的接收数据包的回调函数。

每次收到数据包时,我都会使用该pbuf_alloc函数创建一个缓冲区。然后,我使用udp_sendto. 最后,我使用pbuf_free. (见下面的代码。)

由于某种原因,pbuf_free没有释放缓冲区。n(我在数据包之后出现缓冲区溢出,n池大小在哪里。)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;
}
4

4 回答 4

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 版本的源文件。

于 2012-07-23T12:10:34.993 回答
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 告诉您)。

于 2012-06-18T13:40:27.633 回答
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 的值并检查它是否为非零来测试)。

于 2012-08-21T17:58:45.750 回答
0

struct pbuf 不代表连续的内存区域。它是一个内存位置链。因此,这在一般情况下不起作用:

memcpy(pkt_buf->payload, packet, bytesToSend);

您需要分散复制您的数据。代码片段中的 memcpy() 可能会溢出有效负载缓冲区并导致各种副作用,包括无法干净地释放 pbuf 链。

于 2013-07-26T09:34:46.870 回答