lwip协议栈实现服务器端主动发送,API函数及编程实例《LwIP协议栈源码详解—TCP/IP协议的实现》...

函数netconn_new用来创建一个新的连接结构。连接结构的类型可以选择为TCP或UDP等。函数结构原型如下所示,参数type描述了连接的类型,可以为NETCONN_TCP或NETCONN_UDP等,这里都以TCP作为讨论的对象。

struct netconn* netconn_new(enum

netconn_type type)

该函数首先调用netconn_alloc函数分配并初始化一个netconn结构。初始化的过程包括设置netconn结构类型字段,同时为该结构的op_completed创建一个信号量、recvmbox字段创建一个接收邮箱。奇怪的是netconn_alloc函数并不是在文件api_lib.c文件中,而是在api_msg.c中,凌乱!接下来函数netconn_new会构建一个api_msg消息,该消息要求内核执行函数do_newconn,最后函数tcpip_apimsg用来将消息包装成tcpip_msg结构并发送出去。tcpip_thread函数解析该消息并调用函数do_newconn,do_newconn根据参数的类型最终调用函数tcp_new创建一个TCP控制块。tcpip_apimsg会阻塞在一个信号量上,直至do_newconn释放该信号量。

函数netconn_delete用来删除一个连接结构netconn。与前面的流程相同,它通过消息告诉内核执行do_delconn,调用tcp_close函数关闭TCP连接。而后netconn_delete调用netconn_free函数释放netconn结构的空间。注意这里的netconn_free函数netconn_alloc函数一样,也不是在文件api_lib.c文件中,而是在api_msg.c中,尽管他们都是netconn_xxx结构。

netconn_bind用于将一个IP地址及端口号与结构netconn绑定。事实上,内核是通过函数do_bind调用tcp_bind完成相应TCP控制块得绑定工作的。

netconn_connect函数一般在客户端调用,用以将服务器端的IP地址和端口号与本地的netconn结构绑定。该函数与内核tcp_connect函数对应。

netconn_listen函数一般在服务器端调用,用于将某个TCP控制块置为LISTEN状态。类似的函数do_listen会被调用,该函数有两个重要的工作:为结构netconn字段acceptmbox创建邮箱,该邮箱用于接受外部连接;向相应TCP的PCB控制块中accept字段注册一个回调函数accept_function,当该PCB上有新连接出现时,回调函数会被调用,以向上面的acceptmbox邮箱中发送消息,告诉应用程序有新的连接到来,新连接的信息以netconn结构形式被保存在了邮箱中。

netconn_accept函数在服务器上使用,用于接收远端的连接,该函数主要在阻塞在上面所述的acceptmbox邮箱上,当接收到新的连接后,在该邮箱上取下连接的netconn结构并返回。

netconn_recv函数用于接收数据,接收到得数据被封装为netbuf结构。这里内核函数tcp_recved会被协议栈调用,以通知内核数据被正常接收,内核因此调整发送窗结构,返回ACK确认等。

函数netconn_write用于向相应的TCP连接上发送数据,主要这个函数只用于发送TCP数据,用于发送UDP数据的函数叫netconn_send,这里先不讨论。netconn_write函数原型如下,它用于将dataptr指向的size个数据放到连接conn的发送队列上,apiflags用于描述

err_t netconn_write(struct netconn

*conn, const void *dataptr, int size, u8_t apiflags)

对该数据的操作,包括是否拷贝,是否立即发送两种选择。最后netconn_close函数用于主动关闭连接。

API函数就说这么一点点了。下面我们用这些API函数构造一个服务器程序。这个服务器程序很简单,它能响应一个客户端的连接和数据请求,并向客户端发送一个固定字符串。任务代码如下:

const uint8 data[]= "hello

world!"; // 待发送字符串

void mytcp_thread(void *arg)

{

struct netconn * conn,

*newconn; // API描述的连接结构

struct netbuf *

buf; // API数据缓冲

conn = netconn_new(NETCONN_TCP); //

创建新的TCP连接结构

netconn_bind(conn, NULL,

7); //

该连接与端口7绑定

netconn_listen(conn); //

将结构置为侦听状态

newconn =

netconn_accept(conn); // 接收到一个新的连接

while(1)

{

buf =

netconn_recv(newconn); // 在新连接上接收到一个数据

netbuf_delete(buf); // 删除接收到的数据

netconn_write(newconn,data,sizeof(data), NETCONN_COPY); //

将字符串发送的客户端

}

}

服务器程序之所以要这样设计是为了测试的方便,因为手上恰好有个小程序可以用来测试这个服务器程序以及我们的LwIP协议栈运转是否正常。这个小程序是当年参加中兴编程大赛的时候写的,名字叫报文监视器。它能接收某个TCP连接上的数据并能按照用户要求对这些数据进行过滤,去除用户不关心的数据。大嘴东哥和寝室的鹏鹏。。O(∩_∩)O~看到这个程序就想到了你们,大功臣啊。。。测试结果如下:

a4c26d1e5885305701be709a3d33442f.png

过滤表达式编辑框内的内容是用户输入的过滤条件,当接收的数据串满足过滤条件时,该字符串不会在接收结果中显示出来。过滤条件是一系列的引号括起来的字符串,它们可以用or,and,not,括号等连接起来,组成很复杂的过滤条件。。不讲了。

首先,将过滤条件置为空,此时显示了从服务器接收到得所有数据“hello

word!”,如图下方所示。然后将过滤条件设置为“he”or

“ww”..即字符串中含有“he”或者“ww”字样的数据串将被滤除掉不以显示。。这正如接收结果中的前两行所示。OK…测试结果一切正常,我们的LwIP稳定的跑起来了!不过,这里还可以用其他的测试方法,更常用的方法是构建一个http服务器,然后用我们的浏览器来连接服务器,这些在LwIP移植手册中有了很多例程以及详细的说明,不罗嗦了。

可见,使用LwIP

API已经可以轻松完成所有TCP通信的相关任务了。除此之外,LwIP还用自身的API函数实现了BSD Socket

API函数。因为很多的软件编写是基于BSD套接字的,BSD套接字更简单易懂,使用广泛,可见实现Socket

API还是有必要的。但是LwIP说明文档中这样写道:

这一节提供使用LwIP API对BSD Socket

API的一个简单实现。这个实现只能作为一个参考,不能用于实际编程中,因为它并不完善,比如它没有容错机制等。同时,这个简单实现也不支持BSD

Socket API中的select()与poll()函数,因为LwIP

API没有任何函数可以用于这两个函数的实现。要实现这两个函数,BSD

socket实现需要直接与LwIP协议栈通讯,而不是使用其API。

所以这里不对BSD Socket API做详细讨论了,使用LwIP

API完全可以完成相关的工作,且编程工作也很简单。

到这里,我们已经从头到尾的将LwIP协议走完了一遍,从网络接口层到ARP层,再到IP层,然后到TCP层,最后到API层。通常实际应用中,TCP数据包也是按照这个次序依次被处理的。LwIP还有很多其他内容还没有讨论到,首先是UDP,接下来是PPP,SILP,DNS,IGMP,DHCP,SNMP,IPV6等等。这些都是在某些特殊的场合才会使用到的,不具有什么共性,所以这里先不涉及这些了。

全剧终。。。

你可能感兴趣的:(lwip协议栈实现服务器端主动发送,API函数及编程实例《LwIP协议栈源码详解—TCP/IP协议的实现》...)