天天看点

LWIP使用经验

<div id="article_content" class="article_content">

<h1 align="center"><a name="t0"></a><span style="font-size:18px"><span style="font-family:Calibri">LWIP</span>使用经验</span></h1>

<h2><a name="t1"></a><span style="font-size:18px">一<span style="font-family:Cambria"> LWIP</span>内存管理</span></h2>

<p><span style="font-size:18px"><span style="font-family:Calibri">LWIP</span>的内存管理使用了<span style="font-family:Calibri">2</span>种方式:内存池<span style="font-family:Calibri">memp</span>和内存堆<span style="font-family:Calibri">mem</span>,如图<span style="font-family:Calibri">1</span>所示。</span></p>

<p><span style="font-size:18px">内存池的特点是预先开辟多组固定大小的内存块组织成链表,实现简单,分配和回收速度快,不会产生内存碎片,但是大小固定,并且需要预估算准确。</span></p>

<p><span style="font-size:18px">内存堆的本质是对一个事先定义好的内存块进行合理有效的组织和管理,主要用于任意大小的内存分配,实现较复杂,分配需要查找,回收需要合并,容易产生内存碎片,需要合理估算内存堆的总大小。</span></p>

<p></p>

<p align="center"><span style="font-size:18px">&nbsp;</span></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517103926406" alt=""></span></p>

<p align="center"><span style="font-size:18px">图<span style="font-family:Calibri">1</span>内存池与内存堆</span></p>

<h3><a name="t2"></a><span style="font-size:18px"><span style="font-family:Calibri">1.</span>&nbsp;&nbsp;&nbsp;&nbsp; 数据包管理</span></h3>

<p><span style="font-size:18px">数据包管理结构<span style="font-family:Calibri">pbuf</span>共有四种类型,它们的特点和使用场合如表<span style="font-family:Calibri">1</span>所示。</span></p>

<table cellspacing="0" cellpadding="0">

<tbody>

<tr>

<td valign="top">

<p><span style="font-size:18px">类别</span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">分配方式</span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">特点</span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">使用场合</span></p>

</td>

</tr>

<tr>

<td valign="top">

<p><span style="font-family:Calibri"><span style="font-size:18px">PBUF_RAM</span></span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">内存堆,包括<span style="font-family:Calibri">pbuf</span>和数据区</span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">长度不定,分配耗时</span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">应用程序和协议栈</span></p>

</td>

</tr>

<tr>

<td valign="top">

<p><span style="font-family:Calibri"><span style="font-size:18px">PBUF_POOL</span></span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">内存池,包括<span style="font-family:Calibri">pbuf</span>和数据区</span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">长度固定,分配快</span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">中断服务程序</span></p>

</td>

</tr>

<tr>

<td valign="top">

<p><span style="font-family:Calibri"><span style="font-size:18px">PBUF_ROM</span></span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">内存池,仅包括<span style="font-family:Calibri">pbuf</span></span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">所指数据位于<span style="font-family:Calibri">ROM</span>中</span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">应用程序引用内存区</span></p>

</td>

</tr>

<tr>

<td valign="top">

<p><span style="font-family:Calibri"><span style="font-size:18px">PBUF_REF</span></span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">内存池,仅包括<span style="font-family:Calibri">pbuf</span></span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">所指数据位于<span style="font-family:Calibri">RAM</span>中</span></p>

</td>

<td valign="top">

<p><span style="font-size:18px">应用程序引用内存区</span></p>

</td>

</tr>

</tbody>

</table>

<p align="center"><span style="font-size:18px">表<span style="font-family:Calibri">1 &nbsp;pbuf</span>类型与特点</span></p>

<p><span style="font-size:18px">每一种<span style="font-family:Calibri">pbuf</span>分配内存的方式都不一样,如图<span style="font-family:Calibri">2</span>所示。</span></p>

<p></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517103957062" alt=""></span></p>

<p align="center"><span style="font-size:18px">图<span style="font-family:Calibri">2</span>四种数据包管理结构</span></p>

<p><span style="font-size:18px">只有选择合适的<span style="font-family:Calibri">pbuf</span>类型才能发挥<span style="font-family:Calibri">LWIP</span>的最大性能,一个数据包可能是多种<span style="font-family:Calibri">pbuf</span>的组合,用链表连接起来,如图<span style="font-family:Calibri">3</span>所示。</span></p>

<p align="center"></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517104017062" alt=""></span></p>

<p align="center"><span style="font-size:18px">图<span style="font-family:Calibri">3 &nbsp;pbuf</span>链表</span></p>

<h3><a name="t3"></a><span style="font-size:18px"><span style="font-family:Calibri">2.</span>&nbsp;&nbsp;&nbsp;&nbsp; 设置内存大小</span></h3>

<p><span style="font-size:18px">为<span style="font-family:Calibri">LWIP</span>开辟一个专用的内存堆是应该的,这样一来<span style="font-family:Calibri">LWIP</span>的<span style="font-family:Calibri">mem_alloc()</span>和<span style="font-family:Calibri">mem_free()</span>都将基于该堆内存进行分配和回收,不影响其他系统内存的使用。如图<span style="font-family:Calibri">1</span>左所示,<span style="font-family:Calibri">lwipopt.h</span>文件中宏<span style="font-family:Calibri">MEM_SIZE</span>定义了堆区的大小,对于一个负荷较重的系统堆区需要分配较大。</span></p>

<p align="center"></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517104035671" alt=""></span></p>

<p align="center"><span style="font-size:18px">图<span style="font-family:Calibri">4</span>堆和<span style="font-family:Calibri">PBUF_ROM</span>内存区</span></p>

<p align="left"><span style="font-size:18px"><span style="font-family:Calibri">LWIP</span>使用<span style="font-family:Calibri">PBUF_ROM</span>类型的内存池来发送“只读”数据(处于<span style="font-family:Calibri">ROM</span>中或者其他进程中不可修改),宏<span style="font-family:Calibri">MEMP_NUM_PBUF</span>定义了该缓冲池的个数,如图<span style="font-family:Calibri">2</span>右所示。</span></p>

<p align="left"><span style="font-size:18px">在<span style="font-family:Calibri">ISR</span>(中断服务程序中)经常需要快速分配一部分内存进行数据存储,这是通过<span style="font-family:Calibri">PBUF_POOL</span>类型的缓冲区实现的。为此需要定义两个宏:<span style="font-family:Calibri">PBUF_POOL_SIZE</span>定义缓冲池的个数,<span style="font-family:Calibri">PBUF_POOL_BUFSIZE</span>定义单个缓冲区的大小,如图<span style="font-family:Calibri">5</span>所示。</span></p>

<p align="center"></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517104109093" alt=""></span></p>

<p align="center"><span style="font-size:18px">图5 <span style="font-family:Calibri">

PBUF_POOL</span>内存区</span></p>

<h3><a name="t4"></a><span style="font-size:18px"><span style="font-family:Calibri">3 </span>宏编译开关</span></h3>

<p><span style="font-size:18px">若定义<span style="font-family:Calibri">MEM_LIBC_MALLOC=1</span>,直接使用<span style="font-family:Calibri">C</span>库中的<span style="font-family:Calibri">malloc</span>、free来分配动态内存;否则使用LWIP自带的mem_malloc、mem_free等函数。</span></p>

<p><span style="font-size:18px">若定义MEMP_MEM_MALLOC=1,则memp.c中的全部内容不会被编译,用内存堆来实现内存池分配,使用这种方式得考虑是否能忍受内存堆分配带来的时间延迟。</span></p>

<p><span style="font-size:18px">若定义MEM_USE_POOLS=1,则mem.c中的全部内容不会被编译,用内存池来实现内存堆的分配,使用这种方式得考虑是否能忍受因为POOL内存固定大小而带来的内存浪费。此外用户需要定义宏MEM_USE_CUSTOM_POOLS=1,还需要额外实现一个头文件lwippools.h,并在其中开辟一些用于内存堆分配函数的内存池POOL,开辟空间的格式如下:</span></p>

<p><span style="font-size:18px">LWIP_MALLOC_MEMPOOL_START</span></p>

<p><span style="font-size:18px">LWIP_MALLOC_MEMPOOL(20, 256)</span></p>

<p><span style="font-size:18px">LWIP_MALLOC_MEMPOOL(10, 512)</span></p>

<p><span style="font-size:18px">LWIP_MALLOC_MEMPOOL_END</span></p>

<h2><a name="t5"></a><span style="font-size:18px">二<span style="font-family:Cambria"> LWIP</span>启动时序</span></h2>

<p><span style="font-size:18px">图<span style="font-family:Calibri">6</span>展示了<span style="font-family:Calibri">LWIP</span>启动时序,大部分函数都是<span style="font-family:Calibri">LWIP</span>自带的,主要的移植代码是<span style="font-family:Calibri">eth_init()</span>实现初始化以太网接口,启动程序会创建<span style="font-family:Calibri">2</span>个线程:<span style="font-family:Calibri">tcpip_thread</span>负责<span style="font-family:Calibri">LWIP</span>的绝大部分工作(主要是协议栈的解析和系统运行),<span style="font-family:Calibri">ethernetif_thread</span>负责从网口接收数据包再交付给<span style="font-family:Calibri">tcpip_thread</span>线程进行处理。</span></p>

<p align="center"></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517104136359" alt=""></span></p>

<p align="center"><span style="font-size:18px">图6<span style="font-family:Calibri"> LWIP</span>启动函数</span></p>

<h2><a name="t6"></a><span style="font-size:18px">三<span style="font-family:Cambria"> LWIP</span>运行逻辑</span></h2>

<h3><a name="t7"></a><span style="font-size:18px"><span style="font-family:Calibri">1 </span>接收数据包</span></h3>

<p align="center"></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517104209218" alt=""></span></p>

<p align="center"><span style="font-size:18px">图7<span style="font-family:Calibri"></span>接收数据包</span></p>

<p align="left"><span style="font-size:18px">当以太网口接收到一个数据包后,<span style="font-family:Calibri">EMAC_IRQ</span>中断服务程序通过信号量通知<span style="font-family:Calibri">ethernetif</span>线程,<span style="font-family:Calibri">ethernetif</span>线程调用<span style="font-family:Calibri">low_level_input()</span>接收该数据包并通过邮箱交付给<span style="font-family:Calibri">tcpip_thread</span>线程,<span style="font-family:Calibri">tcpip_thread</span>根据该数据包的类型进行相应处理。它是建立在消息传递的基础上的,如图<span style="font-family:Calibri">8</span>所示。</span></p>

<p align="left"></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517104224468" alt=""></span></p>

<p align="center"><span style="font-size:18px">图8<span style="font-family:Calibri"></span>数据包消息的产生和处理</span></p>

<h3><a name="t8"></a><span style="font-size:18px"><span style="font-family:Calibri">2&nbsp; SequentialAPI</span>函数调用</span></h3>

<p><span style="font-size:18px"><span style="font-family:Calibri">API</span>设计的核心在于让用户进程负责尽可能多的工作,例如数据的计算、拷贝等;而协议栈进程只负责简单的通信工作,这是很合理的,因为系统可能存在多个应用程序,它们都使用协议栈进程提供的通信服务,保证内核进程的高效性和实时性是提高系统性能的重要保障。进程之间通信使用IPC技术,包括邮箱、信号量和共享内存,如图9所示。</span></p>

<p align="center"></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517104250140" alt=""></span></p>

<p align="center"><span style="font-size:18px">图9<span style="font-family:Calibri"></span>协议栈<span style="font-family:Calibri">API</span>实现</span></p>

<p><span style="font-size:18px">以函数<span style="font-family:Calibri">netconn_bind()</span>为例看<span style="font-family:Calibri">API</span>是如何实现的,首先用户程序中调用函数<span style="font-family:Calibri">netconn_bind()</span>绑定一个连接,则这个函数实现时,是通过向内核进程发送一个<span style="font-family:Calibri">TCPIP_MSG_API</span>类型的消息,告诉内核进程执行<span style="font-family:Calibri">do_bind</span>函数:在消息发送后,函数阻塞在信号量上,等待内核处理该消息;内核在处理消息时,会根据消息内容调用<span style="font-family:Calibri">do_bind</span>,而<span style="font-family:Calibri">do_bind</span>会根据连接的类型调用内核函数<span style="font-family:Calibri">udp_bind</span>、tcp_bind或raw_bind;当do_bind执行完后,它会释放信号量,这使被阻塞的netconn_bind得以继续执行,整个过程如图10所示。</span></p>

<p align="center"></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517104311156" alt=""></span></p>

<p align="center"><span style="font-size:18px">图10<span style="font-family:Calibri"> API</span>函数实现</span></p>

<h2><a name="t9"></a><span style="font-size:18px">四<span style="font-family:Cambria"> TCP/IP</span>核心知识点</span></h2>

<h3><a name="t10"></a><span style="font-size:18px"><span style="font-family:Calibri">1.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 滑动窗口</span></h3>

<p></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517104347703" alt=""></span></p>

<p align="center"><span style="font-size:18px">图<span style="font-family:Calibri">11</span>滑动窗口</span></p>

<p align="left"><span style="font-size:18px">接收窗口相关的字段中,<span style="font-family:Calibri">rcv_nxt</span>是自己期望收到的下一个字节编号,<span style="font-family:Calibri">rcv_wnd</span>表示接收窗口的大小,<span style="font-family:Calibri">rcv_ann_wnd</span>表示将向对方通告的窗口大小值,这个值在报文发送时会被填在首部中的窗口大小字段,<span style="font-family:Calibri">rcv_ann_right_edge</span>记录了上一次窗口通告时窗口右边界取值。上面这四个字段都会随着数据的发送和接收动态地改变,如图<span style="font-family:Calibri">12</span>所示。</span></p>

<p align="left"></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517104453234" alt=""></span></p>

<p align="center"><span style="font-size:18px">图12<span style="font-family:Calibri"></span>接收窗口</span></p>

<p><span style="font-size:18px">发送窗口中,<span style="font-family:Calibri">lastack</span>记录了被接收方确认的最高序列号,<span style="font-family:Calibri">snd_nxt</span>表示自己将要发送的下一个数据的起始编号,<span style="font-family:Calibri">snd_wnd</span>记录了当前的发送窗口大小,它常被设置为接收方通告的接收窗口值,<span style="font-family:Calibri">snd_lbb</span>记录了下一个被应用程序缓存的数据的起始编号,如图<span style="font-family:Calibri">10</span>所示。</span></p>

<p><span style="font-size:18px">上面这四个字段的值也是动态变化的,每当收到接收方的一个有效<span style="font-family:Calibri">ACK</span>后,<span style="font-family:Calibri">lastack</span>的值就做相应的增加,指向下一个待确认数据的编号,当发送一个报文后,<span style="font-family:Calibri">snd_nxt</span>的值就做相应的增加,指向下一个待发送数据,<span style="font-family:Calibri">snd_nxt</span>和<span style="font-family:Calibri">lastack</span>之间的差值不能超过<span style="font-family:Calibri">snd_wnd</span>的大小。</span></p>

<p><span style="font-size:18px">由于实际数据发送时是按照报文段的形式组织的,因此可能存在这样的情况:即使发送窗口允许,但并不是窗口内的所有数据都能发送以填满窗口,如图<span style="font-family:Calibri">13</span>中编号为<span style="font-family:Calibri">11</span>~<span style="font-family:Calibri">13</span>的数据,可能因为它们太小不能组织成一个有效的报文段,因此不会被发送。发送方会等到新的确认到来,从而使发送窗口向右滑动,使得更多的数据被包含在窗口中,这样再启动下一个报文段的发送。</span></p>

<p align="left"></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517104411171" alt=""></span></p>

<p align="center"><span style="font-size:18px">图<span style="font-family:Calibri">13</span>发送窗口</span></p>

<h3><a name="t11"></a><span style="font-size:18px"><span style="font-family:Calibri">2.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 三次握手</span></h3>

<p align="center"></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517110403718" alt=""></span></p>

<p align="center"><span style="font-size:18px">图<span style="font-family:Calibri">14</span>连接建立过程</span></p>

<h3><a name="t12"></a><span style="font-size:18px"><span style="font-family:Calibri">3.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 断开连接</span></h3>

<p align="center"></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517110515906" alt=""></span></p>

<p align="center"><span style="font-size:18px">图<span style="font-family:Calibri">15</span>连接断开过程</span></p>

<h3><a name="t13"></a><span style="font-size:18px"><span style="font-family:Calibri">4.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-family:Calibri">TCP</span>状态转换</span></h3>

<p></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517110926515" alt=""></span></p>

<p align="center"><span style="font-size:18px">图<span style="font-family:Calibri">16&nbsp; TCP</span>状态转换图</span></p>

<h3><a name="t14"></a><span style="font-size:18px"><span style="font-family:Calibri">5.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 同时打开</span></h3>

<p align="center"></p>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517110947250" alt=""></span></p>

<p align="center"><span style="font-size:18px">图<span style="font-family:Calibri">17</span>双方同时打开</span></p>

<h3><a name="t15"></a><span style="font-size:18px"><span style="font-family:Calibri">6.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 同时关闭</span></h3>

<p align="center"><span style="font-size:18px"><img src="https://img-blog.csdn.net/20140517114518140" alt=""></span></p>

<p align="center"><span style="font-size:18px">&nbsp;</span></p>

<p align="center"><span style="font-size:18px">图<span style="font-family:Calibri">18</span>双方同时关闭</span></p>

<h2><a name="t16"></a><span style="font-size:18px">五正确使用<span style="font-family:Cambria">LWIP</span></span></h2>

<p><span style="font-size:18px">一般说来<span style="font-family:Calibri">LWIP</span>协议栈是比较稳定的,尤其像<span style="font-family:Calibri">V1.3.2</span>经历过业界广泛使用和工程应用,完全可以应用于<a href="http://lib.csdn.net/base/embeddeddevelopment" target="_blank" rel="external nofollow" class="replace_word" title="嵌入式开发知识库" target="_blank" style="color:#df3434; font-weight:bold;">嵌入式</a>产品。那为什么还是有很多人反映<span style="font-family:Calibri">LWIP</span>不稳定呢?主要是以下几个方面的原因:</span></p>

<p><span style="font-size:18px"><span style="font-family:Calibri">1.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 网络硬件驱动<span style="font-family:Calibri">&nbsp;</span>确保<span style="font-family:Calibri">EMAC</span>口接收与发送稳定可靠</span></p>

<p><span style="font-size:18px"><span style="font-family:Calibri">2.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 移植<span style="font-family:Calibri">LWIP&nbsp;</span>基于<span style="font-family:Calibri">OS</span>的移植代码正确稳定</span></p>

<p><span style="font-size:18px"><span style="font-family:Calibri">3.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 配置<span style="font-family:Calibri">LWIP&nbsp;</span>根据设备<span style="font-family:Calibri">RAM</span>尺寸进行合理配置</span></p>

<p><span style="font-size:18px"><span style="font-family:Calibri">1)</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 值<span style="font-family:Calibri">(PBUF_POOL_SIZE * PBUF_POOL_BUFSIZE)</span>必须大于<span style="font-family:Calibri">TCP_SND_BUF</span>和<span style="font-family:Calibri">TCP_WND</span>,否则容易引起错误;</span></p>

<p><span style="font-size:18px"><span style="font-family:Calibri">2)</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 当内存有限时<span style="font-family:Calibri">TCP</span>发送不能太快(具体值依赖于分配内存的大小),否则引起<span style="font-family:Calibri">tcp_enqueue()</span>逻辑错误;</span></p>

<p><span style="font-size:18px"><span style="font-family:Calibri">4.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 调用<span style="font-family:Calibri">LWIP</span>的<span style="font-family:Calibri">API</span>函数<span style="font-family:Calibri">&nbsp;</span>正确使用<span style="font-family:Calibri">API</span>函数,特别防止内存泄露。</span></p>

<p><span style="font-size:18px"><span style="font-family:Calibri">5.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 资源不足<span style="font-family:Calibri">&nbsp;</span>打开报警提醒,当资源不够时提醒设计者</span></p>

<h2><a name="t17"></a><span style="font-size:18px">六<span style="font-family:Cambria"> LWIP</span>常见问题</span></h2>

<h3><a name="t18"></a><span style="font-size:18px"><span style="font-family:Calibri">1.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 网卡驱动程序</span></h3>

<p><span style="font-size:18px">首先,必须将协议栈完全初始化才能打开网络接收功能,接收中断必须将数据封装在<span style="font-family:Calibri">PBUF</span>中,然后交会给协议栈内核处理。其次,<span style="font-family:Calibri">LWIP</span>的全局变量(<span style="font-family:Calibri">arp_table,netif_list,udp_pcbs</span>等)确保赋初值<span style="font-family:Calibri">0</span>,否则容易一运行就崩溃。</span></p>

<h3><a name="t19"></a><span style="font-size:18px"><span style="font-family:Calibri">2.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 内存泄露</span></h3>

<p><span style="font-size:18px">第一个原则(责任制):谁分配内存,谁就负责回收。</span></p>

<p><span style="font-size:18px">第二个原则(对称性):分配内存者与回收内存者一一对应构成闭环。</span></p>

<p><span style="font-size:18px">另外,需要特别注意一些系统函数的调用,它们也会带来内存泄露,如:</span></p>

<p><span style="font-size:18px">例<span style="font-family:Calibri">1</span></span></p>

<p><span style="font-family:Calibri"><span style="font-size:18px">newconn = netconn_accept(conn);</span></span></p>

<p><span style="font-family:Calibri"><span style="font-size:18px">do_something_for(newconn);</span></span></p>

<p><span style="font-family:Calibri"><span style="font-size:18px">netconn_close(newconn);</span></span></p>

<p><span style="font-size:18px"><strong><em><span style="color:red"><span style="font-family:Calibri">netconn_delete(newconn);&nbsp;&nbsp;&nbsp;</span></span></span></span></em></span></p>

<p><span style="font-family:Calibri"><span style="font-size:18px">&nbsp;</span></span></p>

<p><span style="font-size:18px">例<span style="font-family:Calibri">2</span></span></p>

<p><span style="font-family:Calibri"><span style="font-size:18px">inbuf = netconn_recv(conn);</span></span></p>

<p><span style="font-family:Calibri"><span style="font-size:18px">do_something_for(inbuf);</span></span></p>

<p><span style="font-size:18px"><strong><em><span style="color:red"><span style="font-family:Calibri">netbuf_delete(inbuf);&nbsp;&nbsp;&nbsp;</span></span></span></span></em></span></p>

<h3><a name="t20"></a><span style="font-size:18px"><span style="font-family:Calibri">3.</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span style="font-family:Calibri">PC</span>机无法与<span style="font-family:Calibri">LWIP</span>建立<span style="font-family:Calibri">TCP</span>连接</span></h3>

<p><span style="font-size:18px">问题:<span style="font-family:Calibri">PC</span>机能够与<span style="font-family:Calibri">LWIP</span>设备<span style="font-family:Calibri">PING</span>操作成功,但是无法建立<span style="font-family:Calibri">TCP</span>连接。</span></p>

<p><span style="font-size:18px">原因:通过代码跟踪,发现<span style="font-family:Calibri">LWIP</span>发出了<span style="font-family:Calibri">SYN+ACK</span>数据包,但是<span style="font-family:Calibri">PC</span>机无法接收该握手数据包,该数据包为<span style="font-family:Calibri">60</span>字节,小于以太网的最小长度(<span style="font-family:Calibri">64</span>字节),而<span style="font-family:Calibri">LWIP</span>设备的<span style="font-family:Calibri">EMAC</span>没有设置短小数据包填充功能,导致<span style="font-family:Calibri">PC</span>机无法接收该短数据包。</span></p>

<p><span style="font-size:18px">解决:使能<span style="font-family:Calibri">EMAC</span>的短小数据包填充功能。</span></p>

<p><span style="font-size:18px; font-family:Calibri">&nbsp;</span></p>

</div>

继续阅读