出处:https://blog.csdn.net/zbychhaozeng/article/details/6561490
标签:
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://bluefish.blog.51cto.com/214870/158416
既然定了这么个标题,当然是要从socket的recv来讲了。这里主要涉及到lwip_recvfrom这个函数。它的大致过程是,先通过netconn_recv(sock->conn);从netconn的recvmbox中收取数据,在这里有个do_recv的调用,而do_recv又调用了tcp_recved,关于这个函数的注释如下:
* This function should be called by the application when it has
* processed the data. The purpose is to advertise a larger window
* when the data has been processed.
知道了,这里的do_recv只是起到一个知会的作用,可能的话会对接收条件做一些调整。
回到主题,lwip_recvfrom从netconn接收完数据就是要copy the contents of the received buffer into the supplied memory pointer mem,这一步是通过netbuf_copy_partial函数来完成的。
接着往下走,我们发现,数据的来源是在netconn的recvmbox,刚才提到过的。好了,那么是在什么地方,什么时候这个recvmbox被填充的呢?
在工程中搜索recvmbox,发现在recv_tcp函数有这样一句:
sys_mbox_trypost(conn->recvmbox, p)
ok,就是它了。看看函数的原型:
* Receive callback function for TCP netconns.
* Posts the packet to conn->recvmbox, but doesn't delete it on errors.
static err_t recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
好的,p是个输入参数,并且这是个静态函数,是作为tcp的接收回调用的。
嗯接着看recv_tcp的caller:
* Setup a tcp_pcb with the correct callback function pointers
* and their arguments.
* @param conn the TCP netconn to setup
static void setup_tcp(struct netconn *conn)
{
struct tcp_pcb *pcb;
pcb = conn->pcb.tcp;
tcp_arg(pcb, conn);
tcp_recv(pcb, recv_tcp);
tcp_sent(pcb, sent_tcp);
tcp_poll(pcb, poll_tcp, 4);
tcp_err(pcb, err_tcp);
}
哈哈,可谓是一网打尽啊。对于这个函数中的几个调用,我们看一个就好了,别的实现也差不多,就是个赋值的过程
Void tcp_recv(struct tcp_pcb *pcb,
err_t (* recv)(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err))
{
pcb->recv = recv;
}
setup_tcp上面也有讲过,是在newconn的时候被调用的,创建完pcb,就是调用的这个函数,以配置连接的各个回调函数。
然而到这里似乎走了一个死胡同里了,貌似没有什么地方对pcb->recv有调用的,而唯一有的就是接收TCP事件TCP_EVENT_RECV的宏定义中。同时,其他的几个函数也是类似的情况,例如send_tcp函数。
真是“山穷水尽疑无路,柳暗花明又一村”啊。原来上面的也不只是死胡同。这里就要从tcp的三大接收处理函数说起了。
最底层的(在tcp层)就是tcp_input,它是直接被ip层调用的,该函数的定义注释是这么写的:
* The initial input processing of TCP. It verifies the TCP header, demultiplexes
* the segment between the PCBs and passes it on to tcp_process(), which implements
* the TCP finite state machine. This function is called by the IP layer (in ip_input()).
Tcp_input又调用了tcp_process函数做进一步的处理,它的定义注释如下:
* Implements the TCP state machine. Called by tcp_input. In some
* states tcp_receive() is called to receive data. The tcp_seg
* argument will be freed by the caller (tcp_input()) unless the
* recv_data pointer in the pcb is set.
是的,下面是tcp_receive函数,被tcp_process调用
* Called by tcp_process. Checks if the given segment is an ACK for outstanding
* data, and if so frees the memory of the buffered data. Next, is places the
* segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
* is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
* i it has been removed from the buffer.
然而光有这些调用顺序是不行的,最重要的是下面两个变量【都在tcp_in.c中】
static u8_t recv_flags;
static struct pbuf *recv_data;
在tcp_receive中有以下主要几句
if (inseg.p->tot_len > 0)
{
recv_data = inseg.p;
}
if (cseg->p->tot_len > 0)
{
/* Chain this pbuf onto the pbuf that we will pass to
the application. */
if (recv_data)
{
pbuf_cat(recv_data, cseg->p);
}
else
{
recv_data = cseg->p;
}
cseg->p = NULL;
}
下面的这个是tcp_input中的,是在tcp_process处理完之后的
if (recv_data != NULL)
{
if(flags & TCP_PSH)
{
recv_data->flags |= PBUF_FLAG_PUSH;
}
/* Notify application that data has been received. */
TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
}
看最后一条语句就好了,很熟悉是吧,对了,就是上面传说中的死胡同,到此也解开了。
本文出自 “bluefish” 博客,请务必保留此出处http://bluefish.blog.51cto.com/214870/158416
一 绑定端口后,开启监听,为何监听还要返回一个新的连接?:监听状态的连接只需要很小的内存,于是tcp_listen()就会收回原始连接的
内存,而重新分配一个较小内存块供处于监听状态的连接使用。
二 tcp_accepted()通知LWIP一个新的连接已被接收,从而让LWIP去执行一些内务;tcp_accept()当指定的监听连接接通后调用回调函数
三 err_t tcp_connect():客户端请求连接远端主机,并发送最初的SYN端,connect调用后不用等待正确连接建立,是否建立看其返回值。
四 tcp_write:TCP先调用tcp_sndbuf()获得队列中的可用字节大小,为0时先等待然后调用write将其放到发送队列中; tcp_sent:远程主机收到客户端的收据后的回传回调函数; tcp_recv(再调用tcp_recved):新收据接收时调用recv回调函数如果没有错误发生,则回调函数返回ERR_OK,并且
必须释放掉pbuf结构。否则,如果函数的调用中发生错误,那么千万不要释放该结构,以便LwIP内核可以保存该结构,从而等待以后处理。
五 tcp_poll():指定轮训的时间间隔和回调函数;tcp_close:关闭连接并释放内存;tcp_abort:中止,通常会有错误发生tcp_err被调用。
tcp.c文件中,tcp_poll()回调函数被周期性调用,每interval秒调用两次。
在Xilinx SDK TCP server demo中 interval最小值是1秒,即poll_callbak将被500ms调用一次。
poll函数在tcp.c文件被函数tcp_slowtmr()周期性调用,调用周期取决于协议栈定时器中断频率的设置。
定时器中断发生会调用platform_zynqmp.c中的timer_callbak回调函数,该函数会给调用标志位(TcpFasttmrFlag和TcpSlowtmrFlag)赋值,从而控制tcp_slowtmr()函数的调用周期。
如果需要更改poll回调函数或是LWIP协议栈周期性处理时间的频率可通过更改platform_zynqmp.c中PLATFORM_TIMER_INTR_RATE_HZ的宏定义值来调整。
对于一般以太网协议,LWIP通过tcp_write()函数发送数据,其中参数len的值应小于等于1460.
---------------------
作者:yobuwen
来源:CSDN
原文:https://blog.csdn.net/yobuwen/article/details/84791756
版权声明:本文为博主原创文章,转载请附上博文链接!
最近在弄STM32+LWIP协议,在网络拔掉网线情况下,无法重新连接。网上找了好多方法都没有实现,着实郁闷!后来无意间看到了暂时解决这一问题的方法,虽然不是那么完美,但最算能解决问题。分享给大家。
问题:LWIP建立一个TCP服务器,收发数据也正常。但是客户端非正常断线-如客户端掉电,突然拔掉客户端的网线,然后重新连接服务器,就连接不上了。
原因:LWIP_CLOSE(),在非正常断线情况下使用不知道有什么还占着内存释放不了,没法正常释放资源。有高手知道怎么释放,可以指点一下。
我的方法是:使用协议栈自带的keep alive功能,自动检测网络是否掉线,来释放资源。利用lwip的回调机制,每次建立连接成功的时候回调用acept函数,在里面加一个flag来判断就好。
在main()函数中添加
//断网检测
if(((ETH_ReadPHYRegister(PHY_ADDRESS, PHY_BSR) & PHY_Linked_Status) == 0x00) || (RJ45_COUNTER))
{
RJ45_COUNTER = 1; //掉线标志位
}
在
static err_t Tcpsvrpoll(void *arg, struct tcp_pcb *pcb)
{
//添加
if(RJ45_COUNTER)
{
tcp_keepalive(pcb);
}
}
---------------------
作者:大漠鸿图
来源:CSDN
原文:https://blog.csdn.net/sunymen/article/details/37909597
版权声明:本文为博主原创文章,转载请附上博文链接!
lwip 1.3.0版本学习笔记
出处:http://www.51hei.com/bbs/dpj-39307-1.html
ETH_BSP_Config(); //硬件外设初始化
LwIP_Init();//ip地址 网关等初始化
udp_test_init();//使用udp协议时的相关初始化
void udp_test_init(void)
{
struct udp_pcb *pcb;//定义udp协议指针
pcb = udp_new();//新建一个 并开辟空间内存
udp_bind(pcb,IP_ADDR_ANY,2000);//分配IP等
udp_recv(pcb,Udp_Test_Receive,NULL);//将回调函数赋给指针 这个函数中 将回调函数Udp_Test_Receive作为参数传给udp_recv,实际表达的作用就是 这样
// pcb->recv = Udp_Test_Receive
// pcb->recv_arg = 空;
}
Udp_pcb是一个自带链表的的结构体,
struct udp_pcb {
/* Common members of all PCB types */
IP_PCB;
/* Protocol specific PCB members */
struct udp_pcb *next;
u8_t flags;
/** ports are in host byte order */
u16_t local_port, remote_port;
#if LWIP_IGMP
/** outgoing network interface for multicast packets */
ip_addr_t multicast_ip;
#endif /* LWIP_IGMP */
#if LWIP_UDPLITE
/** used for UDP_LITE only */
u16_t chksum_len_rx, chksum_len_tx;
#endif /* LWIP_UDPLITE */
/** receive callback function */
udp_recv_fn recv;
/** user-supplied argument for the recv callback */
void *recv_arg;
};
有*next,是为了可以和多个客户端链接,每个链表都有ip,可以储存不同端口。
仔细分析一下回调函数的作用
void Udp_Test_Receive(void *arg,struct udp_pcb *pcb,struct pbuf *p,struct ip_addr *addr,u16_t port)
{
//struct ip_addr destAddr = *addr;
//unsigned char g_tData[256];
struct pbuf *p1;
unsigned char *nptr; //指向UDP接收缓存区的指针
short len; //接收到的UDP数据包长度
unsigned short LenUpdata = 0; //升级数据包的有效数据长度
unsigned char ret,saddr;
if(p != NULL)
{
len = p->len;
len = len;
nptr = (unsigned char*)p->payload;
/////////////////////////////////////////////////////////////////
Add your code here
////////////////////////////////////////////////////////////////
pbuf_free(p);//释放
}
使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数。而那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。
回调函数只初始化一次就表明以后来数据就只调用该一个回调函数。
其它函数解释
struct tcp_pcb *tcp_new(void)
创建一个新的连接标识符(PCB)。如果没有有效的存储空间创建这个新的pcb,返回NULL。
译注:这个函数创建一个TCP协议控制块,但并不把它放到任何TCP PCB列表,直到使用tcp_bind()函数绑定。Tcp_new()函数会调用tcp_alloc函数来动态申请一块内存并初始化它,之后将这块内存的首地址返回给tcp_new()函数,如果动态内存不成功的话返回NULL。
err_t tcp_bind(struct tcp_pcb *pcb, struct ip_addr *ipaddr,
u16_t port)
给pcb绑定一个本地IP地址和端口号。如果参数"ipaddr"为IP_ADDR_ANY,则为这个pcb绑定任意本地IP地址。
译注:这个函数的大部分代码用于检验给出的IP地址和端口号是否合适,如果合适则将给出的IP地址和端口号赋给当前PCB,更新已绑定tcp_pcb列表并返回ERR_OK.如果给出的参数不合适,则返回ERR_USE(表示端口已被使用)。
参数ipaddr如果为IP_ADDR_ANY,表示绑定到任意本地地址,那么IP_ADDR_ANY是什么呢?在lwip-1.3.0\src\include\ipv4\lwip\ip_addr.h中定义了:
#define IP_ADDR_ANY ((struct ip_addr *)&ip_addr_any)
ip_addr_any是一个ip_addr型变量,在lwip-1.3.0\src\core\ipv4\ip_addr.c中有如下声明:
#define IP_ADDR_ANY_VALUE 0x00000000UL
const struct ip_addr ip_addr_any = { IP_ADDR_ANY_VALUE };
所以, IP_ADDR_ANY是等于0x00000000UL的. 在IP地址上规定 0.0.0.0为广播地址,也就是任意地址的意思。
- struct tcp_pcb *tcp_listen(struct tcp_pcb *pcb)
指定一个PCB进入监听状态。当一个远端连接访问时,函数 tcp_accept()指定的回调函数将被调用。在调用这个函数之前一定要使用tcp_bind()函数绑定一个本地IP和端口号。
tcp_listen() 函数返回一个新的连接标识符,原始的pcb会被释放,这是为了节省内存,使之更适合小内存系统。
如果监听连接的内存无效,tcp_listen()函数返回NULL,如果这样的话,传入的PCB参数将不会被释放。
这个函数从原理上看也比较简单,首先是做一些必要的检查,判断原始pcb是否已经处于连接状态,如果没有则申请一块tcp_pcb类型的内存,将原始的必要的pcb内容复制到新的pcb中,设置新的pcb状态为LISTEN,释放原始的pcb,并将新pcb连接放入已监听队列。
- struct tcp_pcb *tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
这个函数和tcp_listen()函数相同,只是限制了TCP监听队列连接个数,这个个数由backlog参数指定。为了使用它,你必须在你的lwipopt.h中设置TCP_LISTEN_BACKLOG=1。
- void tcp_accepted(struct tcp_pcb *pcb)
通知lwIP一个传入的连接已经被接受。通常这个函数在“accept()”函数的回调函数中被调用。这允许lwIP处理自身内部的任务。比如,允许更多传入的连接进入监听队列。
- void tcp_accept(struct tcp_pcb *pcb,
err_t (* accept)(void *arg, struct tcp_pcb *newpcb,
err_t err))
指定应在侦听连接上的一个新的连接到达时调用的回调函数。
- err_t tcp_connect(struct tcp_pcb *pcb, struct ip_addr *ipaddr,
u16_t port, err_t (* connected)(void *arg,
struct tcp_pcb *tpcb,
err_t err));
设置打开连接的pcb连接到远程主机并发送初始的SYN段。
函数tcp_connect() 会立即返回;它并不等待这个连接是否被正确设置。相反的,当连接正确建立后它将调用第四个参数("connected"参数)指定的函数。如果这个连接不能正确的建立,可能是主机拒绝这个连接或者主机没有响应,"connected"函数将被调用并设置一个相应的参数。
当入队的SYN段内存不可用时,tcp_connect()函数能返回ERR_MEM,表示连接没有正确建立。如果SYN成功入队,tcp_connect()函数返回ERR_OK。
---TCP数据发送函数
lwIP会调用tcp_write()函数来发送队列中的数据。当数据成功的发送到远程主机,会调用一个指定的回调函数来通知应用程序。
- err_t tcp_write(struct tcp_pcb *pcb, void *dataptr, u16_t len,
u8_t copy)
参数"dataptr"指向数据队列;参数"len"传递数据的长度;参数"copy"的值为0或者1,表明是否需要申请新的内存用于数据的拷贝。如果这个参数为0,则不需要申请新的内存,此时数据只能使用指针来引用。
如果数据长度超过当前发送缓存字节数或者要发送的段队列长度超过lwipopts.h中定义的上限值,tcp_write()函数执行失败并返回ERR_MEN。可以使用tcp_sndbuf()函数来返回输出队列有效的字节数。
使用这个函数的正确方法是根据tcp_sndbuf() 函数返回的字节数来发送数据。如果函数返回ERR_MEM,应用程序应该等待直到当前队列数据成功的被远程主机收到然后尝试重新发送一次。
- void tcp_sent(struct tcp_pcb *pcb,
err_t (* sent)(void *arg, struct tcp_pcb *tpcb,
u16_t len))
当远程主机成功接收(也就是应答信号)到数据时,该函数指定的回调函数被调用。传送给回调函数的"len"参数给出了上一次已经被确认的发送的最大字节数。
--TCP数据接收函数
TCP数据接收是基于回调函数的---当一个新的数据接收到时,应用程序指定的回调函数被调用。当应用程序接收到数据后,它必须调用tcp_recved()函数来指示接收数据的大小。
- void tcp_recv(struct tcp_pcb *pcb,
err_t (* recv)(void *arg, struct tcp_pcb *tpcb,
struct pbuf *p, err_t err))
当接收到数据时,本函数设置的回调函数将被调用。如果传递给回调函数一个NULL pbuf则说明远程主机关闭了这个连接。如果函数正常运行并且回调函数返回ERR_OK,则必须释放这个pbuf,如果其它情况,必须保存这个pbuf,这样才能让lwIP内核保存它以供应用程序检查并恢复错误。
- void tcp_recved(struct tcp_pcb *pcb, u16_t len)
当应用程序接收到数据后必须调用这个函数。参数"len"表明接收到的数据的长度。
--- 应用程序轮询函数
当一个连接空的时候(也就是说,既没有数据接收也没有数据发送),lwIP会通过调用一个指定的回调函数来重复轮询应用程序。这可以用作一个看门狗定时器,用来终止空闲时间太长的连接;或者用作等待内存有效的一种方法。举例来说,如果调用tcp_write()函数时因为内存无效而失败,应用程序可以使用轮询功能在连接空闲的时候再次调用tcp_write()。
- void tcp_poll(struct tcp_pcb *pcb, u8_t interval,
err_t (* poll)(void *arg, struct tcp_pcb *tpcb))
指定轮询间隔和应用程序轮询时调用的回调函数。这个间隔是以TCP粗粒度定时器为单位的,即500毫秒一次。如果参数"interval"的值为10,则意味着每5秒轮询一次应用程序。
---关闭和终止连接函数
- err_t tcp_close(struct tcp_pcb *pcb)
关闭连接。如果关闭的连接内存无效,函数返回ERR_MEM,如果是这样的话,应用程序应该等待并通过使用acknowledgment回调函数或者轮询功能重新关闭连接。如果连接关闭成功,函数返回WRR_OK。
TCP内核调用tcp_close()后,参数"pcb"指定的连接被解除。
- void tcp_abort(struct tcp_pcb *pcb)
通过向远程主机发送一个RST(复位)段来终止连接。这个函数从不会失败。
如果这个连接因为一个错误而被终止,则应用程序可以通过err回调函数灵活的处理这个事件。通常一个连接因错误而终止的原因是内存不足。这时使用tcp_err()函数设置的回调函数被调用。
- void tcp_err(struct tcp_pcb *pcb, void (* err)(void *arg,
err_t err))
指定一个处理错误的回调函数,该回调函数不能得到本函数的"pcb"作为它的参数,因为这个pcb可能已经被解除。
--- 低层次TCP接口
在系统的较低层,TCP提供一个简单的接口。在系统初始化的时候,任何其他TCP函数被调用之前必须先调用tcp_init()函数。当系统已经运行,两个定时器函数tcp_fasttmr() 和tcp_slowtmr()必须定期被调用。tcp_fasttmr()函数必须每隔TCP_FAST_INTERVAL(定义在tcp.h中)个毫秒被调用一次,tcp_slowtmr() 函数必须每隔TCP_SLOW_INTERVAL个毫秒被调用一次。
--- UDP 接口
相比之下,UDP接口要比TCP接口类似,但UDP在低层次的复杂程度上明显比TCP简单。
- struct udp_pcb *udp_new(void)ige
创建一个用于UDP通讯的UDP pcb。这个pcb直到绑定本地地址或者连接到远程地址后才被激活。
- void udp_remove(struct udp_pcb *pcb)
删除一个指定的连接。
-err_t udp_bind(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port)
为pcb绑定一个本地地址。参数"ipaddr"为IP_ADDR_ANY时,指定可以监听任何本地IP地址。这个函数一般都会返回ERR_OK。
- err_t udp_connect(struct udp_pcb *pcb, struct ip_addr *ipaddr, u16_t port)
设置pcb连接到远程主机。这个函数不产生任何流量,仅设置pcb的远程地址。
- err_t udp_disconnect(struct udp_pcb *pcb)
删除远程端的pcb。这个函数不产生任何流量,近视删除pcb的远程地址。
- err_t udp_send(struct udp_pcb *pcb, struct pbuf *p)
发送pbuf结构指针p指向的数据。这个pbuf不会被释放。
- void udp_recv(struct udp_pcb *pcb,
void (* recv)(void *arg, struct udp_pcb *upcb,
struct pbuf *p, struct
ip_addr *addr,
u16_t port),
void *recv_arg)
当接收到一个数据包后,该函数指定的回调函数将被调用。
---系统初始化
一个完整通用的lwIP初始化步骤是不可能实现的,因为它还取决于配置文件(lwipopts.h)的编写以及初始化额外运行时的环境(例如硬件定时器)。
当你使用RAW API时,我们可以给你一些建议。
我们假设你使用一个单一的以太网netif和UDP、TCP传输层、IPv4和DHCP客户端。
安以下顺序调用这些函数:
- stats_init()
清楚运行时被收集的统计结构。
- sys_init()
没有多大用处,因为我们在lwipopts.h中设置NO_SYS 1
Not of much use since we set the NO_SYS 1 option in lwipopts.h, to be called for easy
configuration changes.
- mem_init()
通过定义MEM_SIZE初始化动态存储堆
- memp_init()
通过定义MEMP_NUM_x初始化内存池。
- pbuf_init()
通过定义PBUF_POOL_SIZE初始化pbuf内存池。
- etharp_init()
初始化ARP表和队列。
注:在这个初始化之后你必须每隔 ARP_TMR_INTERVAL(5秒)个周期间隔调用etharp_tmr 函数。
- ip_init()
不常用,处理将要放生的改变时被调用。
- udp_init()
清除UDP PCB列表。
- tcp_init()
清除TCP PCB列表并清除一些内部定时器。
注:在这个初始化函数之后,你必须按预先确定的每个周期内调用tcp_fasttmr() 和 tcp_slowtmr()函数。
- netif_add(struct netif *netif, struct ip_addr *ipaddr,
struct ip_addr *netmask, struct ip_addr *gw,
void *state, err_t (* init)(struct netif *netif),
err_t (* input)(struct pbuf *p, struct netif *netif))
向netif_list列表中增加你的网络接口。分配一个netif结构体并传递一个指向这个结构体的指针作为第一个参数。当使用DHCP时给定的ip_addr结构体会被清除,或者用其它数据填充它们。"state"指针可能为NULL。
函数指针"init"必须指向你的以太网netif接口初始化函数,下面举例说用该函数的应用。
err_t netif_if_init(struct netif *netif)
{
u8_t i;
for(i = 0; i < ETHARP_HWADDR_LEN; i++) netif->hwaddr[i] = some_eth_addr[i];
init_my_eth_device();
return ERR_OK;
}
为使用以太网驱动器(For ethernet drivers),函数指针"input"必须指向"netif/etharp.h"中声明的ethernet_input() 函数。其它驱动器(Other drivers)必须使用"lwip/ip.h"中声明的ip_input()函数。
- netif_set_default(struct netif *netif)
注册默认网络接口
- netif_set_up(struct netif *netif)
当netif完全配置后,这个函数必须被调用。
- dhcp_start(struct netif *netif)
在第一次调用时为这个接口创建一个新的DHCP客户端。
注:启动这个客户端后你必须按照预先设定的间隔周期性的调用dhcp_fine_tmr() 和dhcp_coarse_tmr()函数。
你可以通过结构体netif->dhcp查看真实的DHCP状态。
--- 优化提示
首先要做的是优化src/core/inet.c中的lwip_standard_checksum()程序。你可以使用
#define LWIP_CHKSUM
来重写这个标准函数。
inet.c中使用C语言编写的例子,你也可以使用汇编语言编写。
RFC1071是这个主题的很好的介绍。
如果你使用小端处理器,另一个有效的改善是用汇编语言或者内联函数代替htons() 和 htonl()函数。
#define LWIP_PLATFORM_BYTESWAP 1
#define LWIP_PLATFORM_HTONS(x)
#define LWIP_PLATFORM_HTONL(x)
如果你的网络读到的速度比最大线速还要大,检查你的网络接口。如果硬件不能提供良好的服务,会经常快速的发生缓冲区溢出现象。举例来说,当使用cs8900处理器时,调用cs8900if_service(ethif)函数可能很频繁出现上述现象。当使用的RTOS允许cs8900使用中断唤醒一个服务于一个你的使用一个二进制信号量或事件标志的驱动程序的高优先级任务。
当产品发布时,建议设置LWIP_STATS为0。
实际当中,太多的函数会造成难以维护,所以会适量精简。