Lwip实用总结
Posted 夏沫の浅雨
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Lwip实用总结相关的知识,希望对你有一定的参考价值。
写在前面:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
一、断言处理
1、断言:LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len", conn->write_offset < conn->current_msg->msg.w.len);
定位:
- V2.0.3 - - - [void lwip_netconn_do_write(void *m){}]
- V1.4.1 - - - [static err_t do_writemore(struct netconn *conn){}]
原因:
netif_add(&xnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);
解决:
netif_add(&xnetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, &tcpip_input);
解释:
http://savannah.nongnu.org/bugs/?func=detailitem&item_id=56531
在 os里面调用的 nosys的 ethernet_input函数
2、断言:LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL && msg->conn->write_offset == 0);
定位:
- V1.4.1 - - - [void do_write(struct api_msg_msg *msg){}]
原因:
内核 bug
解决:
- 根本问题:
在部分写入分支中的 do_writemore函数添加
#if LWIP_SO_SNDTIMEO
if ((conn->send_timeout != 0) &&
((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
write_finished = 1;
if (conn->write_offset == 0) {
/* nothing has been written */
err = ERR_WOULDBLOCK;
conn->current_msg->msg.w.len = 0;
} else {
/* partial write */
err = ERR_OK;
conn->current_msg->msg.w.len = conn->write_offset;
conn->write_offset = 0; /* <= ADDED THIS */
}
} else
#endif /* LWIP_SO_SNDTIMEO */
- 版本修复:
(STABLE-2.0.0)
++ Bugfixes:
2014-10-21: Simon Goldschmidt
* api_msg.c: fixed bug #38219 Assert on TCP netconn_write with sndtimeout set
建议:升级版本至 v2.0.0及以上
官方答复:http://savannah.nongnu.org/bugs/?47666
解释:
http://savannah.nongnu.org/bugs/?38219
二、Lwip Socket API操作
1、socket默认是状态阻塞的。这就意味着当调用一个不能立即完成其任务的 socket API时,其进程将被阻塞,等待相应操作完成。
2、可能阻塞的套接字调用可分为以下四类:
- 输入操作,包括
read
、readv
、recv
、recvfrom
和recvmsg
共 5个函数。 - 输出操作,包括
write
、writev
、send
、sendto
和sendmsg
共 5个函数。 - 发起外出连接、及用于 TCP的
connect
函数。 - 接受外来连接,即
accept
函数。
3、由于数据收发默认是阻塞模式,在阻塞模式下,最好设置超时机制,并根据相应的反馈状态作处理;否者当连接不正常,网络断开时会大概率事件永远等待(当 Lwip内核的网络检测定时器失效);
4、Lwip对数据的收 /发是全双工的,可也是线程不安全的;即它可以对同一个 Socket的收 /发分开为两个独立线程去处理(一个 Socket允许同时接收和发送),但在多线程中是不可以同时调用多个发送 /接收 Socket API函数(在对同一个 Socket上多收或多发,容易导致串包 /数据交插的问题或其他异常),最好的方式是利用消息邮箱去通知单独处理收 /发的线程;至于是否可以利用上锁来进行多线程收发,理论上是可以的,但是效果不太好。
类似问题:
https://www.zhihu.com/question/56899596、https://bbs.csdn.net/topics/110146134?page=2
5、定时处理:
-
NO_SYS模式:必需实现
sys_now();
函数处理,RAW_Lwip处理(主要是低速定时任务tcp_slowtmr();
和快速定时任务tcp_fasttmr();
)都是基于该函数所获取的时间作相应处理,还包括 lwip协议栈中超时定时器的实现等。 -
RTOS模式:在 V1.4.x版本上,如果使用发送超时机制,则需要实现
sys_now();
函数处理,否则可有可无;当在 V2.x.x版本上,则必需实现sys_now();
函数处理,因为此版本的sys_timeout();
需要sys_now();
提供计数时间,而在 V2.x.x之前的版本中,是不需要该部分的,由内部时间提供,现在为了统一处理,把 lwip协议栈中超时定时器的计数提交给外部接口sys_now();
来提供计数时间。
6、阻塞 /非阻塞读写区别:
-
假如 socket的文件描述符被设置为非阻塞方式:
A、对于read
调用,如果接收缓冲区中有 20字节,请求读 100个字节,就会立即返回 20;如果接收缓冲区的数据大于请求读取得字节数,则立即返回请求读取的字节,剩下的数据等待下次读取;当接收缓冲区为空,返回的将是小于零的状态值,具体看 C点。
B、对于write
调用,如果请求写入 100个字节,而发送缓冲区中只有 20个字节的空闲位置,那么 write会立即返回 20,即返回成功的数目;如果发送缓冲区还有足够空间容纳这个 write所指向的应用层 buffer的全部数据,那么当把这些数据从应用层的 buffer拷贝到内核的发送缓冲区后,会返回请求写入的字节;若 write所指向的应用层 buffer数据为空,返回的也将是小于零的状态值,具体看 C点。
C、无论是read /write
调用,当收发处理空闲时,它们都会立即返回,由于并没有数据收发,那么返回的将是小于零的状态值,而对应的 ERRNO码一般为 EINTR、EWOULDBLOCK或 EAGAIN;另外由于是处于非阻塞状态,所以必须自己实现延时阻塞(或者是事件通知)以待其他任务可以得到执行。 -
假如 socket的文件描述符被设置为阻塞方式:
A、对于read
调用,如果接收缓冲区中有 20字节,请求读 100个字节,就会立即返回 20;如果接收缓冲区的数据大于请求读取得字节数,则立即返回请求读取的字节,剩下的数据等待下次读取。
B、对于write
调用,如果请求写 100个字节,而发送缓冲区中只有 20个字节的空闲位置,那么 write会阻塞,然后进程挂起,直到把 100个字节全部交给发送缓冲区才返回;如果发送缓冲区还有足够空间容纳这个 write所指示的应用层 buffer的全部数据,那么把这些数据从应用层的 buffer拷贝到内核的发送缓冲区,然后返回。
C、无论是read/write
调用,当收发处理空闲时,如果没有设置超时机制,那么会一直挂起进程,直到消息通知处理才退出阻塞执行对应的收发处理。 -
补充
http://nathanchen.github.io/14554141138605.html
https://cloud.tencent.com/developer/article/1008483
https://www.cnblogs.com/kex1n/p/7461124.html
以上是关于Lwip实用总结的主要内容,如果未能解决你的问题,请参考以下文章