🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
LwIP使用api\_msg结构体描述一个API消息的内容,具体见代码清单 9‑11。 ``` 1 struct api_msg 2 { 3 struct netconn *conn; //当前连接 4 err_t err; //执行结果 5 6 union 7 { 8 struct netbuf *b; //执行lwip_netconn_do_send需要的参数,待发送数据 9 10 struct 11 { 12 u8_t proto; //执行lwip_netconn_do_newconn需要的参数,连接类型 13 } n; 14 15 //执行lwip_netconn_do_bind 和 lwip_netconn_do_connect需要的参数 16 struct 17 { 18 API_MSG_M_DEF_C(ip_addr_t, ipaddr); //ip地址 19 u16_t port; //端口号 20 u8_t if_idx; 21 } bc; 22 23 //执行lwip_netconn_do_getaddr需要的参数 24 struct 25 { 26 ip_addr_t API_MSG_M_DEF(ipaddr);//ip地址 27 u16_t API_MSG_M_DEF(port); //端口号 28 u8_t local; 29 } ad; 30 31 //执行lwip_netconn_do_write需要的参数 32 struct 33 { 34 const struct netvector *vector; //要写入的当前向量 35 u16_t vector_cnt; //未写入的向量的数量 36 size_t vector_off; //偏移到当前向量 37 size_t len; //总长度 38 size_t offset; //偏移量 39 u8_t apiflags; 40 } w; 41 42 //执行lwip_netconn_do_write需要的参数 43 struct 44 { 45 size_t len; //长度 46 } r; 47 } msg; 48 }; ``` api\_msg只包含3个字段,描述连接信息的conn、内核返回的执行结果err、还有msg,msg是一个共用体,根据不一样 的API接口使用不一样的数据结构。在conn中,它保存了当前连接的重要信息,如信号量、邮箱等,lwip\_netconn\_do\_xxx(xxx表示不一样的NETCONN API接口)类型的函数执行需要用这些信息来完成与应用线程的通信与同步;内核执行lwip\_netconn\_do\_xxx类型的函数返回结果会被记录在err中;msg的各个产业记录各个函数执行时需要的详细参数。 我们了解底层的数据包消息,那么同理对于上层的API函数,想要与内核进行数据交互,也是通过LwIP的消息机制,API消息由用户线程发出,与内核进行交互,因为用户的应用程序并不是与内核处于同一线程中,简单来说就是用户使用NETCONN API接口的时候,LwIP会将对应API函数与参数构造成消息传递到tcpip\_thread线程中,然后根据对应的API函数执行对应的操作,LwIP这样子处理是为了简单用户的编程,这样子就不要求用户对内核很熟悉,与数据包消息类似,也是有独立的API消息投递函数去处理,那就是netconn\_apimsg()函数,在NETCONN API中构造完成数据包,就会调用netconn\_apimsg()函数进行投递消息,具体见代码清单 9‑12。 ``` 1 err_t 2 netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port) 3 { 4 API_MSG_VAR_DECLARE(msg); 5 err_t err; 6 7 if (addr == NULL) 8 { 9 addr = IP4_ADDR_ANY; 10 } 11 12 API_MSG_VAR_ALLOC(msg); 13 API_MSG_VAR_REF(msg).conn = conn; 14 API_MSG_VAR_REF(msg).msg.bc.ipaddr = API_MSG_VAR_REF(addr); 15 API_MSG_VAR_REF(msg).msg.bc.port = port; (1) 16 err = netconn_apimsg(lwip_netconn_do_bind, &API_MSG_VAR_REF(msg)); (2) 17 API_MSG_VAR_FREE(msg); 18 19 return err; 20 } 21 22 static err_t 23 netconn_apimsg(tcpip_callback_fn fn, struct api_msg *apimsg) 24 { 25 err_t err; 26 27 err = tcpip_send_msg_wait_sem(fn, apimsg, LWIP_API_MSG_SEM(apimsg)); 28 if (err == ERR_OK) 29 { 30 return apimsg->err; 31 } 32 return err; 33 } 34 35 err_t 36 tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t *sem) 37 { 38 TCPIP_MSG_VAR_DECLARE(msg); 39 40 TCPIP_MSG_VAR_ALLOC(msg); 41 TCPIP_MSG_VAR_REF(msg).type = TCPIP_MSG_API; 42 TCPIP_MSG_VAR_REF(msg).msg.api_msg.function = fn; 43 TCPIP_MSG_VAR_REF(msg).msg.api_msg.msg = apimsg; (3) 44 sys_mbox_post(&tcpip_mbox, &TCPIP_MSG_VAR_REF(msg)); (4) 45 sys_arch_sem_wait(sem, 0); (5) 46 TCPIP_MSG_VAR_FREE(msg); 47 return ERR_OK; 48 } ``` (1):根据netconn_bind()传递的参数初始化api_msg结构体。 (2):调用netconn_apimsg()函数投递这个api_msg结构体,这个函数实际上是调用tcpip_send_msg_wait_sem()函数投递API消息的,并且需要等待tcpip_thread线程的回应。 (3):构造API消息,类型为TCPIP_MSG_API,函数为API对应的函数lwip_netconn_do_bind,将msg 的指针指向api_msg结构体。 (4):调用sys_mbox_post()函数向内核进行投递消息。 (5):同时调用sys_arch_sem_wait()函数等待消息处理完毕 总的来说,用户的应用线程与内核也是相互独立的,依赖操作系统的ICP通信机制进行数据交互与同步(邮箱、信号量等),LwIP提供上层NETCONN API接口,会自动帮我们处理这些事情,只需要我们根据API接口传递正确的参数接口,当然,NETCONN API的使用我们会在后面的章节具体介绍,此处仅做了解一下即可,只是为了让大家对LwIP整个内核的运作有个详细的了解,其运作示意图具体见图 9 4。 ![](https://box.kancloud.cn/e16b0f52c8e68af1787e867dfd80aa40_769x457.png)