LwIP及其网络编程应用实例

上传人:M****1 文档编号:570088766 上传时间:2024-08-01 格式:PPT 页数:106 大小:1.69MB
返回 下载 相关 举报
LwIP及其网络编程应用实例_第1页
第1页 / 共106页
LwIP及其网络编程应用实例_第2页
第2页 / 共106页
LwIP及其网络编程应用实例_第3页
第3页 / 共106页
LwIP及其网络编程应用实例_第4页
第4页 / 共106页
LwIP及其网络编程应用实例_第5页
第5页 / 共106页
点击查看更多>>
资源描述

《LwIP及其网络编程应用实例》由会员分享,可在线阅读,更多相关《LwIP及其网络编程应用实例(106页珍藏版)》请在金锄头文库上搜索。

1、第九章第九章LwIP及其网络编程应用实例及其网络编程应用实例LwIP介绍介绍nLwIP(LightWeightInternetProtocol)是瑞典计算机科学院(SwedishInstituteofComputerScience)的AdamDunkels等人开发的一套用于嵌入式系统的开源TCP/IP协议栈。nLwIP的含义是轻型的含义是轻型IP协议,其实现的协议,其实现的重点是在保持重点是在保持TCP协议主要功能的基础协议主要功能的基础上减少对上减少对RAM的占用,这使得的占用,这使得LwIP协协议栈非常适合在小型嵌入式系统中使用。议栈非常适合在小型嵌入式系统中使用。LwIP介绍nLwIP的

2、版本较多,较新的版本通常完善或增加了的版本较多,较新的版本通常完善或增加了LwIP的功能。的功能。nLwIP有如下特点:有如下特点:nIP:支持多网络接口下的IP转发nARP:支持ARP协议nICMP:支持ICMP协议nUDP:支持UDP协议nTCP:支持TCP协议,包括拥塞控制、RTT估算和快速恢复/快速重传nRawAPI:提供专门的内部回调函数,以提高应用性能nSocketAPI:可选的Berkeley-likesocketAPInLwIP的较新版本还提供对以下功能或协议的支持:nIPfragment:IP分片nDNS:域名解析nSNMP:简单网络管理协议nDHCP:动态主机配置协议nPP

3、P:点对点协议nIPv6LwIP源码的文件组织源码的文件组织nLwIP文件目录的组织结构如图所示,其源代码全部位于目录src下。nsrc目录下一般有目录下一般有5个子目录个子目录nLwIP提供的api子目录子目录、core子目录子目录、include子目录子目录和netif子目录子目录n需用户自己创建的arch目录目录。LwIP源码的文件组织源码的文件组织n每个子目录包含的某一类相关的文件,简要说明如下:napi目录目录n应用程序接口文件。narch目录目录n与硬件和OS有关的文件,包括网络驱动、移植需要修改的文件。ncore目录目录nLwIP的核心代码,包括ICMP、IP、UDP、TCP等协

4、议的实现等。ninclude目录目录nLwIP的包含文件。nnetif目录目录nARP协议和LwIP网络设备驱动程序的模板,提供了网络接口驱动程序的基本框架。LwIP的软件体系结构的软件体系结构nLwIP的协议层次的协议层次:nLwIP也是以也是以4层层TCP/IP模型为参照来实现模型为参照来实现TCP/IP协协议族的。议族的。n每一个协议作为一个模块被实现,同时还提供了几个函数作为协议的入口点。nLwIP并没有严格地按照分层的方式实现协议族。并没有严格地按照分层的方式实现协议族。n实际上实际上LwIP使用的是一种比较松散的通讯机制,通过使用的是一种比较松散的通讯机制,通过共享内存的方式实现应

5、用层与底层协议族之间的通讯。共享内存的方式实现应用层与底层协议族之间的通讯。nLwIP拥有独特的缓冲机制独特的缓冲机制,使得各层次可以更加有效的重复使用缓冲区。nLwIP尽量避免内存复制,避免了内存复制产生的性能损失。LwIP的软件体系结构的软件体系结构n与与LwIP的协议层次相匹配,的协议层次相匹配,LwIP采用模块化设计的方法实现。采用模块化设计的方法实现。nTCP/IP协议的实现模块协议的实现模块n如ARP、IP、ICMP、UDP、TCP等n许多相关支持模块。许多相关支持模块。n这些支持模块包括操作系统模拟层、缓冲与内存管理子系统、网络接口函数等。LwIP的进程模型的进程模型nTCP/I

6、P协议族的进程模型指的是采用何种方法把系统分成不同的进程指的是采用何种方法把系统分成不同的进程。n常见的进程模型有两种:常见的进程模型有两种:n每一个协议作为一个独立的进程n协议栈作为一个内核只占据一个进程。n第一种模型第一种模型必须符合协议的每一层,协议层之间通过指定的方式进行通讯。n优点较明显,即每一种协议都可以独立参与到系统运行中,其实现的代码也比较简单,整个协议栈的层次脉络清晰,便于理解和调试。n缺点也是显而易见的,即数据跨层传递时不得不产生进程切换以及内存复制。这一缺点极大影响了系统的整体性能,尤其对于嵌入式系统来说更是不能忍受的。n第二种模型第二种模型将协议栈驻留在操作系统内核中,

7、应用程序通过系统调用与协议栈进行通讯。n这种设计可以使用交叉协议分层技术,各层协议不必严格划分。n这种进程模型的缺点是层次不清,给理解增加了难度。LwIP的进程模型的进程模型nLwIP则采用一种比较灵活的设计方法。则采用一种比较灵活的设计方法。n它可以将所有的协议驻留在一个进程,以便独立于操作系统内核之外。n应用程序既可以驻留在LwIP的进程中,也可以使用一个单独的进程。n它也可以根据协议层次结构创建多个进程,但各个进程之间只传送尽可能少的必要信息,而没有引入额外的内存复制nLwIP在协议层之间切换时,一般只传递数据缓冲区的地址,让需要处理数据的协议层自己去提取。LwIP的函数调用关系的函数调

8、用关系n为了尽量避免不必要的内存复制,为了尽量避免不必要的内存复制,LwIP更多的是采用更多的是采用一种基于回调函数的设计方法。一种基于回调函数的设计方法。n当数据需要处理或跨层传递时,通常是通过调用事先已定义好的回调函数来完成有关操作。n优点是大大提高了LwIP的整体性能;缺点是使得LwIP的整个软件体系显得略微复杂,尤其是函数之间的调用关系更为繁琐。n为了理清LwIP的函数调用关系,从两个不同的方向对这一问题进行分析:n从不同的协议层出发,横向分析各个层次内的调用关系;n从几种典型的协议模块出发,纵向分析各模块的跨层调用关系。整体调用关系整体调用关系n图给出了图给出了LwIP的整体调的整体

9、调用关系,基本上涵盖了用关系,基本上涵盖了LwIP的主要功能模块和的主要功能模块和绝大部分的函数调用。绝大部分的函数调用。n图中只标注了对图中只标注了对LwIP的的整个软件体系起着重要整个软件体系起着重要支撑作用的主干函数支撑作用的主干函数协议层内的调用协议层内的调用nTCP/IP协议栈是按功能层组织的,每一层都为上一层提供协议栈是按功能层组织的,每一层都为上一层提供服务,并使用下一层提供的服务。服务,并使用下一层提供的服务。n在在4层层TCP/IP模型中,从下至上依次是网络接口层、网际模型中,从下至上依次是网络接口层、网际层、运输层和应用层。层、运输层和应用层。n(1)网络接口层)网络接口层

10、n网络接口层是较高协议与局域网接口的地方。n当主机通过查询或者中断方式得知网络芯片接收到数据帧时,LwIP协议栈对该数据帧进行解码,并判断数据帧的协议类型:n如果是IP协议,则将该帧传递给上层(网际层)的ip_input()函数进行处理;n如果是ARP协议,则直接传给本层的arp_input()函数,该函数根据需要决定是否调用arp_replay()进行ARP应答。n当上层有数据需要通过网络接口层进行发送时,当前网络接口的输出函数netif-output()将会被调用,以完成真正的数据发送过程。协议层内的调用协议层内的调用协议层内的调用协议层内的调用n(2)网际层)网际层n网际层负责网间寻址(

11、IP地址)、数据封装、路由选择、错误处理和诊断等n典型协议有IP协议和ICMP协议。n当从下层(网络接口层)接收到当从下层(网络接口层)接收到IP数据报时,调用数据报时,调用ip_input()函数进行处理。函数进行处理。n根据IP数据报的协议字段,LwIP决定将该数据报传给上层(运输层)还是传给本层。n如果IP净荷中承载的是ICMP协议,则本层的icmp_input()函数将会调用。n当不论是上层还是本层有数据需要从网际层发送出去时nLwIP将会调用将会调用ip_output()发送数据,或者先调用发送数据,或者先调用ip_route()找到一个合适的网络接口再调用找到一个合适的网络接口再调

12、用ip_output_if()发送数据。发送数据。n实际上ip_output()也是通过先调用ip_route()再调用ip_output_if()来实现的协议层内的调用协议层内的调用协议层内的调用协议层内的调用n(3)运输层)运输层n运输层负责在网际设备之间运输数据,以可靠或不可靠运输层负责在网际设备之间运输数据,以可靠或不可靠的方式进行。的方式进行。nTCP和UDP。n当下层(网际层)有数据传给运输层时当下层(网际层)有数据传给运输层时nLwIP会根据数据类型的不同(是TCP还是UDP)调用该层的tcp_input()或者udp_input()。n经过一定处理后,LwIP将数据由tcp_r

13、eceive()或udp_input()提交给上层(应用层),一般会调用事先注册的接收函数。n当上层需要发送数据时当上层需要发送数据时nLwIP选择调用tcp_write()或者udp_send()对数据进行处理n最后通过tcp_output()或udp_send()将数据交给下层。协议层内的调用协议层内的调用协议层内的调用协议层内的调用n(4)应用层)应用层n用户的应用运行在应用层,该层使用户可以根据自己的需要对数据进行处理。n用户需要发送数据时用户需要发送数据时n由LwIP根据数据类型(TCP或UDP)调用下层(运输层)对应的发送函数。n应用层并不需要直接关注数据是怎样发送出去的。n用户接

14、收的数据一般由LwIP调用下层的接收函数送达,此后用户可以根据实际情况实现应用程序。典型模块的跨层调用典型模块的跨层调用n对于某一个协议来说对于某一个协议来说n它一般只隶属于某一个层次(ARP除外)。n但往往会有其它层次调用该协议的有关函数n而该协议一般也会主动调用其它层次的有关函数。n(1)IP模块模块nLwIP的较早期版本实现了的较早期版本实现了IP层大部分的基本功能,能够发送、层大部分的基本功能,能够发送、接收以及转发信息包。接收以及转发信息包。n接收信息包由网络设备驱动调用接收信息包由网络设备驱动调用ip_input()函数开始处理。函数开始处理。n完成对IP版本字段及包头长度的初始完

15、整性检查n同时还要计算和验证包头校验和n函数检查目的地址是否与网络接口的IP地址相符以确定信息包是否到达预定主机。n如果一个到达的信息包被发现已经到达了目的主机,则由协议字段来决定信息包应该传送到哪一个上层协议。典型模块的跨层调用典型模块的跨层调用n外发的信息包由外发的信息包由ip_output()函数处理,该函数使用函数处理,该函数使用ip_route()函数查找适当的网络接口来传送信息包。函数查找适当的网络接口来传送信息包。n当外发的网络接口确定后,信息包传给以外发网络接口为参数的ip_output_if()函数。n所有的IP包头字段被填充,并且计算IP包头校验和。nIP信息包的源及目标地

16、址作为参数被传递给ip_output_if()函数。n传输层协议UDP与TCP在计算传输层校验和的时候需要拥有目标IP地址,因此一些传输层函数可能会直接直接调用ip_route()函数确定接口。n这样这些函数在外发数据前就没有必要再对网络接口链表进行检索,而是直接调用ip_output_if()函数外发数据。典型模块的跨层调用典型模块的跨层调用n如果没有网络接口的地址与到达的信息包如果没有网络接口的地址与到达的信息包的目标地址相同,信息包应该被转发。的目标地址相同,信息包应该被转发。n由ip_forward()函数完成。nTTL字段值被减少,当减为0的时候,将会给IP信息包的最初发送者发送IC

17、MP错误信息,并抛弃该信息包。n因为IP包头被改变,因此需要调整IP包头校验和。n最后,信息包被转发到适当的网络接口。典型模块的跨层调用典型模块的跨层调用n(2)ICMP模块模块nICMP信息包由信息包由ip_input()函数收到后,转交给函数收到后,转交给icmp_input()函数对函数对ICMP包头解码,然后进行适当的动作。包头解码,然后进行适当的动作。n如果需要对回送请求进行应答,则调用ip_output()函数发送应答报文。n某些ICMP消息被传递给上层协议,由传输层的特定函数处理。nICMP目标不可到达消息可以由传输层发送,特别是目标不可到达消息可以由传输层发送,特别是UDPn如

18、udp_input()就可以调用icmp_dest_unreach()函数完成这项工作。nicmp_dest_unreach()最后也会调用ip_output()发送ICMP报文。典型模块的跨层调用典型模块的跨层调用典型模块的跨层调用典型模块的跨层调用n(3)UDP模块模块n当一个当一个UDP数据包到达时数据包到达时nIP层调用udp_input()函数将数据包移交给udp_input()。n如果需要的话,LwIP会在这里对UDP校验和进行检查。n为了找到匹配的UDPPCB,LwIP会对UDPPCB全局链表进行线性搜索。n如果当前链表中存在匹配的UDPPCB,则其recv函数会被调用。n发送数

19、据的过程发送数据的过程n由应用程序调用udp_send()函数发起。n为了计算校验和,该函数会调用ip_route()确定网络接口,因为该接口地址将在校验和的计算过程中用到。n最后,信息包被移交给ip_output_if()函数传送。典型模块的跨层调用典型模块的跨层调用典型模块的跨层调用典型模块的跨层调用n(4)TCP模块模块nTCP处理比UDP处理要复杂得多n与与TCP输入相关的函数输入相关的函数ntcp_input()ntcp_process()ntcp_receive()n与与TCP输出有关的函数输出有关的函数ntcp_write()ntcp_enqueue()ntcp_output()

20、典型模块的跨层调用典型模块的跨层调用典型模块的跨层调用典型模块的跨层调用nTCP数据的发送过程一般是由应用层发起。数据的发送过程一般是由应用层发起。n应用层调用应用层调用tcp_write(),而,而tcp_write()再调用再调用tcp_enqueue()。ntcp_enqueue()函数会在必要时将数据分割成适当大小的TCP段,然后把这些TCP段放到所属连接的传输队列中。n这时tcp_output()函数会判断接收器窗口是否拥有足够大的空间,阻塞窗口是否也足够大,如果条件满足,就就调用调用ip_route()找到一个合适的接口,再调用找到一个合适的接口,再调用ip_output_if()

21、完成发送过程。完成发送过程。n即使当时不能发送也不要紧,这是因为LwIP设置了定时器函数tcp_tmr(),该函数每隔固定时间就会被调用一次。ntcp_tmr()会对当前连接的传输队列进行分析,并根据需要调用tcp_output()执行数据发送操作。典型模块的跨层调用典型模块的跨层调用nTCP数据的接收过程由网络接口层发起。数据的接收过程由网络接口层发起。n网络接口层将数据包传递给ip_input()函数,该函数验证IP头后移交TCP段给tcp_input()函数。ntcp_input()函数主要完成两项工作完成两项工作n初始完整性检查(也就是校验和验证与TCP选项解析n判定这个TCP段属于哪

22、个TCP连接。n接着,这个TCP段到达tcp_process()函数。ntcp_process()函数实现了TCP状态机,任何必要的状态转换都在这里实现。n当该TCP所属的连接正处于接受网络数据的状态时,tcp_receive()函数将被调用。n最后,tcp_receive()函数将数据传给上层的应用程序,完成接收过程。n如果收到一个ACK应答确认数据,表明接收器同意接收更多的数据,此时tcp_output()函数将会被调用。LwIP的内存管理的内存管理nLwIP的包缓冲区的包缓冲区pbufnpbuf是LwIP信息包的内部表示。npbuf结构结构既支持动态内存分配以保存信息包内容,又支持让信息

23、包数据驻留在静态存储区。n多个pbuf可以通过一个链表结构链接成一个pbuf链,从而使一个信息包穿越多个pbuf。npbuf的内部结构定义的内部结构定义为nstructpbufnstructpbuf*next;/指向下一个pbufnvoid*payload;/指向实际的数据负载nu16_ttot_len;/pbuf链的数据负载总长度nu16_tlen;/该pbuf的数据负载长度nu16_tflags;/pbuf的类型标志nu16_tref;/pbuf被引用的次数n;LwIP的内存管理的内存管理npbuf结构包括两个指针,两个长度字段,一个标志字段和一个引用结构包括两个指针,两个长度字段,一个标

24、志字段和一个引用计数字段。计数字段。nnext指针指向pbuf链中下一个pbuf的位置;npayload指针指向pbuf中数据负载的开始位置nlen字段包含pbuf中数据内容的长度;ntot_len字段包含当前pbuf的长度与在这个pbuf链中随后的所有pbuf的len字段之和nflags字段标识字段标识pbuf的类型;的类型;nref字段指出pbuf被引用的次数。npbuf有四种类型有四种类型nPBUF_RAMnPBUF_ROMnPBUF_REFnPBUF_POOLPBUF_RAM类型的类型的pbufnPBUF_RAM在事先划分好的内存堆在事先划分好的内存堆栈中分配,用于存放应用程序动态栈中

25、分配,用于存放应用程序动态产生的数据。产生的数据。n图示的是一个PBUF_RAM类型的pbuf实例,其实际的数据负载存放在由协议栈管理的存储区中。n既然PBUF_RAM类型的pbuf用于应用程序发送的数据被动态生成的情况,那么在这种情况下pbuf系统不仅为应用数据分配内存,还应给为这些数据预置的包头分配内存。npbuf系统不可能预先知道为这些数据预置什么样的包头,因而考虑最坏的情况。PBUF_ROM/PBUF_REF类型的类型的pbufnPBUF_ROM类型的类型的pbuf的的payload指针指向指针指向不由协议栈管理的外部存储区不由协议栈管理的外部存储区n如应用程序管理的存储器为用户数据分

26、配的缓存。n由于由应用程序交付的数据不能被改动由于由应用程序交付的数据不能被改动n因此就需要动态地分配一个PBUF_RAM来装载协议的首部n然后将PBUF_RAM(首部)添加到PBUF_ROM(数据)的前面。n这样就构成了一个完整的数据分组(pbuf链)PBUF_ROM/PBUF_REF类型的类型的pbufPBUF_ROM/PBUF_REF类型的类型的pbufn图中的图中的PBUF_ROM还可以是还可以是PBUF_REF,二者的特,二者的特性非常相似,都可以实现数据的零拷贝,但是当发送性非常相似,都可以实现数据的零拷贝,但是当发送数据需要排队时就表现出数据需要排队时就表现出PBUF_REF的特

27、性了。的特性了。n例如待发送的分组需要在ARP队列中排队,假如这些分组中有PBUF_ROM类型的pbuf,则直到分组被处理之前,被引用的应用程序的这块存储区域都不能另作它用。n但如果是PBUF_REF类型的pbuf,LwIP则会在数据分组排队时为PBUF_REF类型的pbuf分配缓存(PBUF_POOL或PBUF_RAM),并将引用的应用程序的数据拷贝到分配的缓存中。n这样应用程序中被引用数据的存储区域就能被释放。PBUF_POOL类型的pbufnPBUF_POOL是具有固定容量的pbuf,其容量大小通过宏定义来指定。n在协议栈管理的内存中初始化了一个pbuf池池,具有相同尺寸的pbuf都是从

28、这个pbuf池中分配得到。n一般使用多个PBUF_POOL链接成一个链表,用于存储数据分组PBUF_POOL类型的类型的pbufPBUF_POOL类型的pbufnPBU_POOL主要用于网络设备驱动层主要用于网络设备驱动层n由于分配一个pbuf的操作可以快速完成,所以PBUF_POOL非常适合用于中断处理。n一般来说,收到的pbuf是PBUF_POOL类型,发送出的pbuf是PBUF_ROM或PBUF_RAM类型。n不同类型的不同类型的pbuf拥有各自的特点和不同的使拥有各自的特点和不同的使用目的,因此只有正确选用,才能最好地发用目的,因此只有正确选用,才能最好地发挥挥LwIP的特性。的特性。

29、LwIP的内存管理的内存管理nLwIP的内存区域主要用于装载待接收和发送的的内存区域主要用于装载待接收和发送的网络数据分组。网络数据分组。n当接收到分组或者有分组要发送时,LwIP协议栈为这些分组分配缓存;n在接收到的分组交付给应用程序或者分组己经发送完毕后,LwIP协议栈对分配的缓存进行回收利用。n协议栈分配的缓存必须能容纳各种大小的报文协议栈分配的缓存必须能容纳各种大小的报文n例如从仅仅几个字节的ICMP应答报文到几百个字节的TCP分段报文。PBUF_RAM的内存管理的内存管理nLwIP协议栈首先从系统内存中开辟一块连续协议栈首先从系统内存中开辟一块连续的静态存储区域的静态存储区域n该区域

30、的大小可以事先通过宏定义指定。该区域的大小可以事先通过宏定义指定。n协议栈将该区域作为协议栈将该区域作为PBUF_RAM的专用区域的专用区域n所有与PBUF_RAM有关的内存操作都被限制在该区域内,n从而确保了协议栈不会因非法访问系统内存的其它区域而扰乱其它程序的正常运行。PBUF_RAM的内存管理的内存管理n为了方便内存管理,协议栈定义了一个比较小为了方便内存管理,协议栈定义了一个比较小的结构体的结构体mem,并将该结构体置于内存分配,并将该结构体置于内存分配块的顶部来保存内存分配记录。块的顶部来保存内存分配记录。n该结构体拥有三个成员变量,分别为两个“指针”和一个标志,n其中next与pr

31、ev分别指向内存的下一个和上一个分配块,nused标志标示该内存块是否已被分配。nnext和prev并不是真正的指针,它们本质上是数组的下标,并没有直接指向真正的地址。PBUF_RAM的内存管理的内存管理n(1)初始化)初始化n使用PBUF_RAM内存之前,需对PBUF_RAM的专用内存区域进行初始化工作,即对该区域进行一定的格式设置。nLwIP协议栈在该区域的头部和尾部各设置了一个mem结构以协助管理内存,如图所示。n在头部的mem结构中,next指向尾部mem,prev指向该区域的起始处,used(=0)表明该mem结构后面的区域尚未使用;n在尾部的mem结构中,next和prev均指向该

32、mem自身,used(=1)表明该mem结构后面无可用的内存。n在该区域的末尾处,协议栈预留了对齐空间,其主要目的是防止操作过程中因对齐而导致的对该专用区域之外的存储空间的越界访问。PBUF_RAM的内存管理的内存管理PBUF_RAM的内存管理的内存管理n(2)分配)分配PBUF_RAM内存块内存块n分配内存时分配内存时n首先根据所申请分配的大小来搜索所有未被使用的内存分配块n搜索到的最先满足条件的内存块将分配给申请者。n第一次分配时第一次分配时n只要申请分配的大小没有超出限制,便会在PBUF_RAM专用存储区域的开头分配所需要的内存PBUF_RAM的内存管理的内存管理n经过多次的内存分配和释

33、放操作后,经过多次的内存分配和释放操作后,PBUF_RAM存存储区中会存在多个大小不一的未使用块。储区中会存在多个大小不一的未使用块。n此时如果需要分配一个新的内存块,有可能搜索到的第一个空闲内存块空间不够。n在这种情况下,内存管理机制会继续往后搜索未使用块,直到搜索到足够大的空闲内存块或搜索到存储区的末尾。PBUF_RAM的内存管理的内存管理n(3)释放)释放PBUF_RAM内存块内存块n对不再利用的内存块,需要进行回收,以便下次需要分配内存块时重新使用。n回收内存块时,管理该内存块的mem结构的used标志将被清零,以表明该内存块已不再被使用,可以重新对其进行分配PBUF_RAM的内存管理

34、的内存管理n为了防止内存碎片的产生,每回收一个内存块后,其上一个与下一个分配块的used标志将会被检查:n如果它们中的任何一个还未被使用(used=0),则这个内存块将被合并到一个更大的未使用内存块中。n只有经过了以上操作后,释放内存的工作才算是已经完成。PBUF_RAM的内存管理的内存管理n合并相邻空闲块的一个示例。合并相邻空闲块的一个示例。PBUF_RAM的内存管理的内存管理n(4)调整)调整PBUF_RAM内存块大小内存块大小n在内存的使用过程中,有时希望调整已分配的内存块的大小。n根据调整的方向(减小/增大)不同,处理的机制也不一样。n调整前先对腾出的内存块大小进行预算:n如果该值小于

35、mem结构的长度加内存块的最小长度(即无法另行分配一个最小长度的内存块),则调整不被执行。n调整时将在腾出的内存块开头置以一个新的mem结构以对其进行管理,该mem结构的used标志为0,表示可以对其进行分配使用。n同释放内存时一样,与新的内存块相邻的内存块的used标志同样会被检查,以防止碎片产生。PBUF_RAM的内存管理n给出了减小既定内存块大小的示意图。给出了减小既定内存块大小的示意图。PBUF_RAM的内存管理的内存管理n增大既定内存块的大小时所采取的机制与上述操作大为不同。n如果希望将某内存块的大小调整为newsize,则处理过程是先分配一块大小为newsize的空闲内存块,然后将

36、原内存块的内容复制到新内存块中,最后再释放原内存块。PBUF_ROM/PBUR_REF的内存管理的内存管理n对于PBUF_ROM/PBUF_REF,LwIP协议栈同样为其开辟了一块连续的存储区域。n协议栈定义了结构体协议栈定义了结构体memp以协助以协助PBUF_ROM/PBUF_RAM的内存管理。的内存管理。n该结构体只有一个成员next,为指向下一个相同结构的存储区的指针。nPBUF_ROM/PBUF_REF类型存储区域初始化后的结构示意图,多个pbuf在内存中以链表的形式存在。n该种类型pbuf的操作方法可以参考链表的一般操作方法。PBUF_POOL的内存管理的内存管理nPBUF_POO

37、L类型的pbuf同样拥有自己专用的存储区域,该区域通过预先从系统内存中分配而得。n区域大小由PBUF_POOL类型的pbuf个数和每个pbuf的缓冲区大小等参数共同决定,这些参数都可以事先通过宏定义指定。n对PBUF_POOL类型的内存管理,LwIP协议栈并没有额外引入类似PBUF_RAM的mem结构或是PBUF_ROM的memp结构,而是直接采用pbuf结构对其进行管理。PBUF_POOL的内存管理的内存管理nPBUF_POOL存储区域的结构存储区域的结构如图所示,对该类型pbuf的操作与普通链表的操作并无不同。n每个PBUF_POOL的数据缓冲区都紧跟在pbuf结构后面,并且大小相同。n这

38、点与PBUF_ROM不同,因为PBUF_ROM的数据缓冲区不在LwIP协议栈管理的区域,并且大小不尽相同。LwIP移植移植n无无RTOS时的移植时的移植nLwIP既可以在无RTOS(RealTimeOperatingSystem,实时操作系统)的环境下运行,也可以很方便地移植到RTOS之上。n移植过程中对于LwIP核心模块没必要也不建议进行修改,而真正的工作是结合实际的软硬件环境,针对与移植密切相关的相关文件与相关函数进行定制。LwIP移植移植n移植函数移植函数n为了将为了将LwIP移植到特定的开发平台上,需要完成与网络接口有关的移植到特定的开发平台上,需要完成与网络接口有关的底层函数。底层函

39、数。这些底层函数集中在srcnetifethernetif.c文件中。n建议将建议将“ethernet”替换成能更好地描述所选网络接口的词汇替换成能更好地描述所选网络接口的词汇,如华中科技大学瑞萨高级嵌入式控制器实验室自行开发的RenesasM16C/62P嵌入式开发平台采用的网络芯片是CS8900A,该文件便用cs8900if.c文件进行了替代。n文件中凡是用ethernet命名的函数,也一律用cs8900进行了替代。nLwIP提供的提供的ethernetif.c文件给出了网络接口驱动的整体框架,用文件给出了网络接口驱动的整体框架,用户需要自己完成的函数主要有户需要自己完成的函数主要有3个,

40、个,分别是n底层初始化函数low_level_init()n底层输入函数low_level_input()n底层输出函数low_level_output()。无无RTOS时的移植时的移植n(1)底层初始化函数)底层初始化函数low_level_init()n原型为staticvoidlow_level_init(structnetif*netif);n该函数用来对网络接口进行初始化,任何与初始化网络接口有关的操作都可以在该函数内实现。n如对网络接口有关参数进行配置,或是完成网络芯片硬件上所需的初始化操作等。n(2)底层输入函数)底层输入函数low_level_input()n函数原型为stat

41、icstructpbuf*low_level_input(structnetif*netif);n该函数为到达的数据包分配pbuf(通常是一个pbuf链),并将数据包从网络接口传入至pbuf链中。n数据具体接收过程的实现与网络接口硬件有关。n将数据装载至pbbuf时,需对pbuf结构的各字段进行正确填充,使其形成逻辑上的pbuf链无无RTOS时的移植时的移植n通常,为收到的数据分配的通常,为收到的数据分配的pbuf是是PBUF_POOL类型,因为分配一个类型,因为分配一个PBUF_POOL可以很快完成。可以很快完成。n(3)底层输出函数)底层输出函数low_level_output()n函数原

42、型为:staticerr_tlow_level_output(structnetif*netif,structpbuf*p);n该函数实现真正的的数据包发送过程n当需要发送数据包时,数据包装载在事先已分配好的pbuf(链)中。nLwIP将pbuf作为参数传入给该函数,由该函数负责将数据包发送至指定的网络接口中。n数据具体发送过程的实现同样与网络接口硬件有关。无无RTOS时的移植时的移植n几个定时器函数几个定时器函数n在LwIP的移植过程中,注意有几个定时器函数必须每隔固定时间就调用一次。n具体采用什么机制实现这一操作并无限制netharp_tmr()ntcp_fasttmr()ntcp_slo

43、wtmr()n这些函数的运行间隔周期可以通过宏定义指定,各函数的具体定义可在LwIP提供的源码中找到。LwIP在在uC/OS-II下的移植下的移植n为了方便为了方便LwIP在在RTOS下的移植,属于操作系下的移植,属于操作系统的函数调用及数据结构并没有在代码中直接统的函数调用及数据结构并没有在代码中直接使用,而是用操作系统模拟层来代替对这些函使用,而是用操作系统模拟层来代替对这些函数的使用。数的使用。n操作系统模拟层使用统一的接口提供定时器、进程同步及消息传递机制等诸如此类的系统服务n原则上,移植原则上,移植LwIP只需针对目标操作系统修只需针对目标操作系统修改模拟层实现即可。改模拟层实现即可

44、。LwIP在在uC/OS-II下的移植下的移植n模拟层主要实现以下模拟层主要实现以下4大功能:大功能:n定时与超时处理定时与超时处理nLwIP可以为某一线程注册若干个超时处理函数,当超时时限溢出时便会调用一个已注册的函数。n进程同步进程同步n进程同步机制为多个进程之间的同步操作提供支持,一般可以用信号量来实现。n如果选用的RTOS不支持信号量,则可以使如条件变量等其它基本的同步方式来模拟。n消息传递消息传递n消息传递机制可通过一种称作邮箱的抽象方法来实现。n邮箱有两种基本操作:n向邮箱投递(post)一则消息和n从邮箱中提取(fetch)一则消息n线程管理线程管理n对LwIP协议栈的线程进行管

45、理和维护,主要指创建线程。移植相关文件与函数移植相关文件与函数n移植过程中需要创建或修改的源文件和头文件位于目录src/arch之下n目录组织结构如图所示:目录组织结构如图所示:移植相关文件与函数移植相关文件与函数n头文件主要是一些宏定义,包括数据类型的定义和有关结构的封装等;头文件主要是一些宏定义,包括数据类型的定义和有关结构的封装等;而主要的功能函数均在而主要的功能函数均在sys_arch.c源文件中实现。源文件中实现。此外,sys.c和sys.h两个文件虽无需作任何修改,但与以上文件(尤其是sys_arch.c)关联紧密,有助于更好地理解LwIP在RTOS下的移植实现。nLwIP在设计时

46、就考虑到了将来的在设计时就考虑到了将来的RTOS移植问题。移植问题。n为了适应不同的操作系统,LwIP并没有在代码中使用针对某个特定RTOS的系统调用和数据结构,而是提供操作系统模拟层作为LwIP和RTOS的一个接口。n为了理解为了理解LwIP在在RTOS下的移植实现过程,选用嵌入式实时操作系统下的移植实现过程,选用嵌入式实时操作系统uC/OS-II为例,对为例,对LwIP在在uC/OS-II下的移植进行说明。下的移植进行说明。nuC/OS-II是专为嵌入式应用设计的实时内核,关于其详细信息可参考其官网http:/ /指向链表第一个指向链表第一个sys_timeoutn;n加上sys_time

47、outs结构后,LwIP线程的超时等待链表结构如图所示:超时处理的实现超时处理的实现超时处理的实现超时处理的实现ntimeoutlist将一个sys_timeouts结构和优先级联系在一起,这样便于根据当前优先级查找对应的sys_timeouts链表,其定义如下:nstructtimeoutlistnstructsys_timeoutstimeouts;/超时等待链表超时等待链表nINT8Uprio;/优先级优先级n;n每个线程都有一个对以的timeoutlist结构,通过该结构的timeouts元素可以定位超时等待列表的表头,从而确定该线程的所有超时处理函数,超时处理的实现超时处理的实现nL

48、wIP在RTOS上的移植过程中需要实现的与超时处理有关的函数是sys_arch_timeouts()。n(1)移植函数)移植函数sys_arch_timeouts()nsys_arch_timeouts()函数的作用是通过查询机制,获取指向当前线程的sys_timeouts结构的指针,相当于定位超时等待链表的表头。n函数的原型如下:structsys_timeouts*sys_arch_timeouts(void);n该函数表面上没有参数,但实际上调用该函数的线程有自己的优先级,因此可以利用当前线程的优先级充当函数的隐含参数。这样处理带来的限制是一个线程不能通过调用该函数来获取另一个线程的sy

49、s_timeouts结构,但这一般不会引起什么问题。超时处理的实现超时处理的实现nsys_timeouts结构和当前线程的优先级一起封装在timeoutlist结构中。n为了存储线程的为了存储线程的timeoutlist结构,在结构,在sys_arch.c文件中定义文件中定义了一个了一个timeoutlist数组:数组:nstaticstructtimeoutlisttimeoutlistLWIP_MAX_TASKS;nLWIP_MAX_TASKS是最大的LwIP线程数,可以事先进行配置。n每次调用sys_thread_new()创建一个新的线程时,都会依序取出一个数组元素,用当前线程的优先级

50、对数组元素的prio字段进行填充。nsys_arch_timeouts()函数通过线性搜索的方法对数组元素进行遍历,直到发现某个数组元素的prio字段与当前优先级相同为止,而该数组元素的timeouts字段正是我们需要的目标。超时处理的实现超时处理的实现超时处理的实现超时处理的实现n(2)相关函数)相关函数sys_timeout()nsys_timeout()函数用以向当前线程增加一个超时处理函数,其原型如下:voidsys_timeout(u32_tmsecs,sys_timeout_handlerh,void*arg);nsys_timeouts()函数首先从内存中申请一块空间,以存放一个

51、sys_timeout结构。n如申请成功则利用函数的实参对结构的各字段进行填充。n要向当前线程注册一个超时处理函数,sys_timeouts()会通过sys_arch_timeouts()函数获取当前线程的sys_timeouts结构。超时处理的实现超时处理的实现n第一次调用第一次调用sys_timeout()注册一个超时处理函注册一个超时处理函数时,直接将数时,直接将sys_timeout结构链接在当前线程的结构链接在当前线程的sys_timeouts结构即可,结构即可,如图所示:超时处理的实现超时处理的实现n应用程序可能会多次注册超时处理函数或删除超时应用程序可能会多次注册超时处理函数或删

52、除超时处理函数,这样处理后一个线程的处理函数,这样处理后一个线程的sys_timeouts链表中可能会同时存在多个链表中可能会同时存在多个sys_timeout结构。结构。n在这种情况下,向线程添加一个超时处理函数略微复杂,因为sys_timeout结构必须插入到链表的恰当位置。n实际上如果一个线程有多个超时处理函数,LwIP会按照链表的逻辑顺序依次结算。n这里所谓恰当的位置,就是比较当前链表节点的超时时限和待插入节点的超时时限,保证插入该节点后不会影响原有任一节点的超时等待属性。超时处理的实现超时处理的实现n如图所示,假设当前线程已注册如图所示,假设当前线程已注册3个超时处理函数,对应有个超

53、时处理函数,对应有3个个sys_timeout结构,其超时时限分别是结构,其超时时限分别是time1=100,time2=40,time3=80。现要注册一个超时时限为。现要注册一个超时时限为time4=160的超时处理函数。的超时处理函数。n为了确定恰当的插入位置,可以沿着链表逐次推算超时时限,分为了确定恰当的插入位置,可以沿着链表逐次推算超时时限,分析过程如下:析过程如下:n1time1time4next4在next1之后n2time1+time2time4next4在next3之前n经以上步骤,next4的位置已经确定,即位于next2和next3之间。n注意插入注意插入next4节点后

54、,节点后,next4节点及紧挨在节点及紧挨在next4后面的后面的next3节点的节点的time属性值需做对应调整,属性值需做对应调整,调整后的结果如图所示:超时处理的实现超时处理的实现超时处理的实现超时处理的实现n如果新节点next4的超时时限time4取其它值,则可能会出现一些特殊情况。ntime4time1+time2+time3,即新节点的,即新节点的time值大于现值大于现有所有节点有所有节点time之和。之和。n这种情况next4节点将插入到最后,并调整time4=time4-time1-time2-time3,next4=NULL。超时处理的实现超时处理的实现n(3)相关函数)相

55、关函数sys_untimeout()nsys_untimeout()函数与sys_timeout()函数的作用恰好相反,用以删除当前线程某一指定的超时处理函数。函数原型如下:voidsys_untimeout(sys_timeout_handlerh,void*arg);n与sys_timeout()相比,sys_untimeout()函数同样会调用sys_arch_timeouts()获取当前线程的sys_timeouts链表结构。n函数通过一种简单的线性搜索的方法,从表头开始遍历,直到找到一个超时处理函数h和参数arg均符合的sys_timeout结构。n将该结构所在的节点从链表中删除,并

56、调整紧挨其后的节点(如果有的话)的超时时限属性,最后释放该结构占用的内存。超时处理的实现超时处理的实现n假设某一线程的超时处理函数链表如图所示:假设某一线程的超时处理函数链表如图所示:超时处理的实现超时处理的实现n如果线程希望删除如果线程希望删除h2处理函数,即执行处理函数,即执行sys_untimeout(h2,arg2);nnext2节点将从原链表中删除,n同时next3节点的time3将会调整为time3=time3+time2。n调整后的状态为:超时处理的实现超时处理的实现n但如果线程不是要删除h2处理函数,而是要删除最后一个超时处理函数,即执行sys_untimeout(h3,arg

57、3);n这种情况next3节点从链表中删除后,没有后续节点需要调整超时时限属性。结果如下:超时处理的实现超时处理的实现n(4)超时处理函数的使用)超时处理函数的使用n在等待信号量或等待消息的过程中,LwIP会对超时等待链表中的超时处理函数进行处理。对一个线程来讲,要么通过调用sys_sem_wait()等待一个信号量,要么通过调用sys_mbox_fetch()等待一则消息,至少要采取一种方法阻塞当前线程,否则注册的超时处理函数将无法正常执行。鉴于邮箱结构比信号量结构占用更多的资源,因此通常通过永久等待一个信号量来实现线程阻塞。n如需每隔一定周期就执行一次某函数,则必须在超时处理函数中重新注册

58、自己。例如需要每隔250ms就执行一次tcp_tmr(),通常可以采用下面的方式实现:n/向当前线程注册一个超时处理函数nsys_timeout(u32_t)OS_TICKS_PER_SEC/4,(sys_timeout_handler)TCP_Timer,NULL);nn/如需周期执行tcp_tmr(),则需在TCP_Timer()中重新注册自己nvoidTCP_Timer(void*p_arg)nntcp_tmr();/每隔250ms执行一次nsys_timeout(u32_t)OS_TICKS_PER_SEC/4,(sys_timeout_handler)TCP_Timer,NULL);

59、n进程同步的实现进程同步的实现n进程同步机制是任务之间通信的一种重要方式,进程同步机制是任务之间通信的一种重要方式,通常可以由信号量实现。通常可以由信号量实现。nuC/OS-II对信号量有较全面的支持,因此移植过程中比较方便实现。nuC/OS-II实现了信号量和互斥型信号量,这里采用uC/OS-II的信号量实现。n由于uC/OS-II支持信号量的各种操作,并且可以满足LwIP对信号量的要求,因此只需对相关结构和函数进行重新封装即可。进程同步的实现进程同步的实现n函数函数sys_sem_wait()n该函数是由LwIP应用程序调用的用来等待一个信号量的函数,但它会在等待信号量的过程中对当前线程的

60、超时等待函数进行处理。函数原型如下:voidsys_sem_wait(sys_sem_tsem)n该函数首先调用sys_arch_timeouts()获取当前线程的超时等待函数链表,以对超时等待链表中的超时处理函数依次结算。n真正实现等待一个信号量的过程由sys_arch_sem_wait()完成。n现仍以下面的超时等待链表为例,分析sys_sem_wait()在等待信号量过程中对超时处理函数的处理。进程同步的实现进程同步的实现n1执行执行sys_arch_sem_wait(sem,time1)。n如超时溢出则表明在time1时间内一直未成功等到信号量,此时执行超时处理函数h1(arg1),同

61、时将next1节点从链表中删除,并转到步骤2。n如在time1时间内成功等到信号量,则不论实际消耗的等待时间是多少,LwIP一律认为消耗时间为1ms,并调整time1=time1-1,此时转到步骤4。n2执行执行sys_arch_sem_wait(sem,time2)。n只有在time1时限耗尽的情况下,才会执行sys_arch_sem_wait(sem,time2)。与1类似,如未等到信号量则执行h2(arg2)并转到步骤3,否则调整time2=time2-1并转到步骤4。进程同步的实现进程同步的实现n3调用调用sys_arch_sem_wait(sem,time3)。n只有在next3节点

62、前面的所有节点的超时时限均已耗尽的情况下,才会执行sys_arch_sem_wait(sem,time3)。n由于next3已经是最后一个节点,因此不论成功等到信号量与否,均会转到步骤4。n4sys_sem_wait()函数返回或作永久等待函数返回或作永久等待。n如果成功等到了信号量,sys_sem_wait()函数返回。n但如果超时等待链表中所有超时时限均已耗尽且所有超时处理函数均已执行后,仍未等到信号量,则sys_sem_wait()会调用sys_arch_sem_wait(0)一直等到信号量有效为止。进程同步的实现进程同步的实现n一种特殊情况是当前线程超时等待链表为空,也就是没一种特殊情

63、况是当前线程超时等待链表为空,也就是没有超时等待函数。有超时等待函数。nsys_sem_wait()会直接调用sys_arch_sem_wait(0)做永久等待。n另一种情况是在等待信号量的过程中发现某一节点的超另一种情况是在等待信号量的过程中发现某一节点的超时时限时时限time=0。n表明该节点的超时时限已经耗尽,需立即执行节点对应的超时处理函数h(arg)。n注意注意time=0与与sys_timeouts链表为空是截然不同的:链表为空是截然不同的:ntime=0只是表明该节点的超时时限已耗尽nsys_timeouts为空则意味着当前线程没有任何函数需要做超时等待。消息传递的实现消息传递的

64、实现n消息传递是任务之间通信的另一种重要方式,通常使用一种称为消息传递是任务之间通信的另一种重要方式,通常使用一种称为邮箱的抽象方法来实现。邮箱的抽象方法来实现。n邮箱有两种基本的操作:n邮递邮递(post)n(发送一则消息)操作不会阻塞进程n提取提取(fetch)n(等待一则消息)操作可能会阻塞进程。nuC/OS-II提供了消息邮箱和消息队列两种机制,区别是消息邮箱一次只能处理一则消息,而消息队列可以存储多则消息。n为了使LwIP更好地运作,采用uC/OS-II的消息队列实现LwIP所需的消息传递机制。线程管理的实现线程管理的实现n在线程管理方面,在线程管理方面,LwIP只提供了创建线程的操

65、作。只提供了创建线程的操作。n由于uC/OS-II没有采用“线程”这一概念,而是采用“任务”的概念,LwIP的线程管理实际上是通过uC/OS-II的任务管理机制实现的。n每个线程都有自己的超时等待属性。每个线程都有自己的超时等待属性。n为了区别不同线程的超时等待属性,在创建线程的过程中会将优先级prio填入到一个timeoutlist结构的prio成员中,如图所示:线程管理的实现线程管理的实现n由于uC/OS-II中每个任务都具有唯一的优先级,因此prio可以作为可以作为LwIP线程的线程的一个标识,以区分不同的线程。一个标识,以区分不同的线程。n实际上sys_arch_timeouts()正

66、是通过这一标识来定位当前线程的超时等待链表的。LwIP网络编程应用实例网络编程应用实例n为了对为了对LwIP的移植和应用进行测试和验的移植和应用进行测试和验证,以一个具体的应用实例来说明证,以一个具体的应用实例来说明LwIP网络编程的一般方法。网络编程的一般方法。n目的是设计和实现一个简单的嵌入式WEB服务器,该WEB服务器可以响应来自浏览器的HTTPGET请求,并在发送请求的浏览器上显示一个小型页面。实验平台准备实验平台准备n硬件平台的一个最基本要求是提供对以太网接口的支持。n选用的是华中科技大学瑞萨高级嵌入式控制选用的是华中科技大学瑞萨高级嵌入式控制器实验室自主研发制作的器实验室自主研发制

67、作的RenesasM16C/62P嵌入式开发平台。嵌入式开发平台。n该平台采用瑞萨科技(RENESAS)的M16C/62P单片机作为主控制器,通过集成一块CS8900A网络芯片来实现网络数据收发功能。实验平台准备实验平台准备n软件平台最重要的部分是开发环境。软件平台最重要的部分是开发环境。n采用采用Renesas的的High-performanceEmbeddedWorkshop进行编程开发。进行编程开发。n为了方便调试,华中科技大学瑞萨高级嵌入式控制器实验室开发了专门针对RenesasM16C/62P单片机的监控程序。n通过该监控程序,将用户应用程序下载至ROM或者RAM,可以实现调试功能嵌

68、入式嵌入式WEB服务器的设计服务器的设计n浏览器访问浏览器访问WEB服务器所使用的是服务器所使用的是HTTP协议。协议。n客户(客户(WEB服务器)通过服务器)通过HTTP协议向服务器发送请求协议向服务器发送请求n服务器根据HTTP协议对客户端发来的请求进行解码,并对其作出应答。nHTTP使用TCP作为运输层,客户在向服务器发送请求之前,要先与服务器的IP地址在端口80(HTTP的知名端口)建立一个连接。n服务器在端口80侦听进入的连接,并接受和处理客户的请求。n由于只是对由于只是对LwIP协议栈进行测试和验证,因此设计的协议栈进行测试和验证,因此设计的WEB服服务器只对客户端的务器只对客户端

69、的GET请求作出应答,而对其它请求一概不予请求作出应答,而对其它请求一概不予理会。理会。嵌入式嵌入式WEB服务器的设计服务器的设计n浏览器与WEB服务器交互的示意图。n图中浏览器运行在PC机(或支持浏览器的其它设备)上,WEB服务器运行在RenesasM16C/62P嵌入式开发平台上,二者通过网络进行连接。n所有的数据交互过程受TCP/IP协议族的制约。嵌入式嵌入式WEB服务器的实现服务器的实现n在进行了必要的初始化工作后,打开TCP的80端口并对该端口进行侦听。n一旦客户发起请求,则服务器接受并解析该请求。n如果请求正确,则服务器将相应的页面内容发送给客户端,而客户端(通常是浏览器)将该页面

70、直观地显示出来。n至此,一次完整的交互过程已完成。服务器或者继续侦听更多的请求,或者主动关闭。主流程图主流程图主要程序的实现n(1)HTTP主线程主线程nHTTP的主线程首先注册一个新的TCP连接,并将其绑定到80端口。n随后该连接进入侦听的状态,一旦接受到客户发起的连接请求,则调用process_connection()函数进行处理。n主要程序代码如下:nstaticvoidhttpd_thread(void*arg)nnstructnetconn*conn,*newconn;nn/*CreateanewTCPconnectionhandle.*/nconn=netconn_new(NETC

71、ONN_TCP);主要程序的实现主要程序的实现nn/*Bindtheconnectiontoport80onanylocalIPaddress.*/nnetconn_bind(conn,NULL,80);nn/*PuttheconnectionintoLISTENstate.*/nnetconn_listen(conn);nn/*Loopforever.*/nwhile(1)nn/*Acceptanewconnection.*/nnewconn=netconn_accept(conn);nif(newconn!=NULL)nn/*Processtheincommingconnection.*/

72、nprocess_connection(newconn);n/*Deallocateconnectionhandle.*/nnetconn_delete(newconn);nnOSTimeDly(2);nn主要程序的实现主要程序的实现n(2)处理连接的函数)处理连接的函数n实际的处理连接过程由process_connection()函数完成。n该函数对客户请求进行解码:如为GET请求,则将网页内容发送给请求客户端;否则不予理会。n网页内容如下:n/*Thisisthedatafortheactualwebpage.*/nstaticcharindexdata=nM16C-uC/OS-IIWeb

73、servertestpage.nnnWelcometothistestpage.nThiswebsiteisrunningLwIPonaM16C16-bitmicrocontroller,usinguC/OS-IInRealTimeOperatingSystem.Thecontrollerhas31KBRAMand384KBROM.nnNumberofpageviews:0nnThisservershowssomedynamiccontent.nRefreshthepageandwatchthecounterincrement!Wow!11nn;主要程序的实现nstaticcharhttp_h

74、tml_hdr=nHTTP/1.1200OKrnnContent-Type:text/htmlrnrnn;n函数中设计了一个简单的计数器,对客户请求的次数进行计数,并将计数值反馈到网页内容中。主要的程序代码如下:n/*Thisfunctionprocessesanincommingconnection.*/nstaticvoidprocess_connection(structnetconn*conn)nnstructnetbuf*inbuf;nchar*rq;nu16_tlen;ninti;nstaticintNumber=0;nnif(+Number=10)nNumber=0;nnni=0

75、;nwhile(indexdatai)nnif(indexdatai=)nni+;nindexdatai=0+Number;nni+;n主要程序的实现主要程序的实现n/*Readdatafromtheconnectionintothenetbufinbuf.nWeassumethatthefullrequestisinthenetbuf.*/nif(inbuf=netconn_recv(conn)=NULL)nnreturn;nnnetbuf_data(inbuf,(void*)&rq,&len);n/*deletedatabuffer*/nnetbuf_delete(inbuf);nn/*C

76、heckiftherequestwasanHTTPGET/rn.*/nif(rq0=G&rq1=E&nrq2=T&rq3=&nrq4=/)nn/*Sendtheheader.*/nnetconn_write(conn,(void*)http_html_hdr,nsizeof(http_html_hdr),NETCONN_NOCOPY);n/*Sendtheactualwebpage.*/nnetconn_write(conn,(void*)indexdata,sizeof(indexdata),NETCONN_COPY);nn实现结果实现结果n嵌入式WEB服务器在RenesasM16C/62P

77、上运行后,通过浏览器对其进行访问,访问结果如图所示。嵌入式嵌入式WEB服务器功能扩展服务器功能扩展n由于只是对LwIP进行简单的验证,因此设计的嵌入式WEB服务器只是简单地显示一个页面。n实际上可以结合应用需求,对嵌入式实际上可以结合应用需求,对嵌入式WEB服务器作出各种各样的服务器作出各种各样的功能扩展。功能扩展。n如可以将嵌入式如可以将嵌入式WEB服务器与移动机器人结合起来。服务器与移动机器人结合起来。n在移动机器人上搭建嵌入式WEB服务器,使得客户可以非常方便地通过WEB浏览器对移动机器人进行远程监控,如图像采集、机器人状态信息查询、机器人运动控制等。n嵌入式WEB服务器在移动机器人上搭建成功以后,原则上世界上任何一个地方都可以通过WEB浏览器获取该服务器发布的监控系统实时信息,进而进行远程控制、调节和维护等。

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 医学/心理学 > 基础医学

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号