应用程序编程接口(API)定义了应用程序和TCP/IP 协议栈的交互方式,IP协议栈最常见的API是BSD套接字API,BSD套接字API用于大多数的UNIX系统,而且对微软WinSock API具有重要影响。然而,套接字API采用了一种多线程编程模型,这种模型会引起较大的内存开销,对智能物体和它们有限的内存而言,并不理想。
uIP 协议提供了一种事件驱动,API 代替了多线程套接字API,core/net/ip/下的程序文件tcpip.c 和头文件tcpip.h 定义了uIP 协议事件驱动API。对于智能物体环境来说,事件驱动API 有几个优点:第一,事件驱动机制比多线程机制占用的内存更低;第二,事件驱动API不需要在uIP 协议栈和应用程序之间使用额外的缓存,但是传统的BSD 套接字API 需要这种缓存,这一特点进一步降低了内存要求;第三,事件驱动API比多线程API的执行效率更高,有益于解决智能物体处理速度低的问题。由于应用程序能够立即在接收到的数据和连接请求上执行,即使是在低端系统中也可以降低响应时间。事件驱动API既可用于TCP连接,也可用于基于UDP的应用。
尽管事件驱动API适用于许多应用程序,但有些应用程序可以从顺序API中受益。因此,uIP协议栈也提供了基于原线程protothreads的顺序BSD套接字API。顺序API称为protosockets,允许程序以自上而下的方式编写。protosockets的API 还为重传提供缓存,减轻了程序员以更高内存需求为代价重新生成数据的潜在负担。Contiki 操作系统定义了protosockets 库头文件psock.h 和该库程序文件psock.c,这两个文件保存在core/net/ip文件夹下。
1.应用程序事件
应用程序必须以任何时候事件出现时调用的C 函数UIP_APPCALL()实现,可能的事件以及每个事件相应的测试函数如表10-1 所示。测试函数用于区分不同的事件,这些函数以C 宏实现,返回结果为零或非零。要注意的是,某些事件可能彼此结合发生,例如,新数据可能和确认数据同时到达。
表10-1 事件及事件测试函数
调用应用程序时,uIP 将全局变量uip_conn 设置为指向当前连接的uip_conn 结构,这可以用于区分不同的服务,典型的用途是检查uip_conn->lport(本地TCP端口号)以决定连接应提供哪种服务,例如,如果uip_conn->lport 的值等于80,则应用程序可能决定充当HTTP 服务器,如果值为23,则应用程序可能决定充当TELNET服务器。
2.接收和发送数据
如果uIP 测试函数uip_newdata()非零,则连接的远程主机已发送新数据,uip_appdata 指针指向实际数据,数据大小通过uIP 函数uip_datalen()获得。数据不是由uIP缓存的,但是在应用程序函数返回后将被覆盖,因此应用程序必须直接对输入数据进行操作,或者将输入数据复制到缓存区中以供以后处理。
发送数据时,uIP根据可用缓存区空间和接收方发布的当前TCP 窗口调整应用程序发送的数据长度,缓存区空间大小由内存配置决定。因此,从应用程序发送的所有数据都有可能不会到达接收方,并且应用程序可能会使用uip_mss()函数来查看协议栈实际发送的数据量。
应用程序使用uIP 函数uip_send()发送数据,uip_send()函数有两个参数:指向要发送数据的指针和数据的长度。如果应用程序需要RAM 空间来生成可能发送的实际数据,则数据分组缓存区(由uip_appdata 指针指向)可用于此目的。应用程序一次只能在连接上发送一个数据块,并且每个应用程序不可能多次调用uip_send(),只会发送上次调用的数据。
3.数据重传
周期性TCP 定时器驱动数据重传,每调用一次周期性定时器,每个连接的重传定时器递减,如果定时器值变为零,应该执行重传。因为uIP协议模块没有记录设备驱动程序发送的分组内容,因此uIP协议模块要求应用程序参与重传任务。当uIP 协议模块决定重传一个分片时,uip_rexmit()调用应用程序函数被设置重传标识以表示需要重传。
应用程序检查重传uip_rexmit()标识,并且生成与之前发送的数据相同的数据。从应用程序的角度看,执行重传与原来发送数据没有什么不同。因此,这部分应用程序发送和重传代码的写法相同,尽管实际的重传操作是应用程序执行的,但知道重传应该执行的时间是协议栈的责任,这样不会因执行重传增加应用程序的复杂性。
4.关闭连接(www.xing528.com)
应用程序活动期间,调用uIP TCP 关闭函数uip_close()关闭当前连接,这会导致连接被彻底关闭。为了表明致命错误,应用程序可能会想要取消连接,并通过调用uIP TCP取消函数uip_abort()取消连接。
如果远程终端已经关闭了连接,即测试函数uip_closed()为真,用具有关闭连接标记的设置激活应用程序,应用程序可能做一些必要的善后处理工作,例如释放分配的用于TCP连接的内存。
5.报告误差
在连接中可能发生两种致命的错误:远程主机取消连接;连接上重传多次最后的数据,并且已取消该连接。uIP协议用设置了取消标记或超时标记的应用程序函数报告这两种错误,应用程序检查这些设置的标记查找是否有被取消或超时的连接。应用程序可以使用测试函数uip_aborted()和uip_timedout()测试错误条件。
6.侦听端口
uIP 协议维护一张侦听TCP 端口的清单,使用uip_listen()函数打开一个新端口进行监听,当连接请求到达侦听端口时,uIP协议会创建一个新连接并调用该应用程序函数。如果因为创建了新连接而调用了应用程序,则测试函数uip_connected()返回真值。应用程序会检查uip.h中定义的数据结构uip_conn中的lport字段,以检查新连接所连接的端口。
7.打开连接
可以通过uip_connect()函数在uIP 协议内打开新连接,此函数分配新连接并在连接状态中设置标记,以便在下次uIP 协议轮询该连接时打开指定IP 地址和端口的TCP 连接,uip_connect()函数返回指向新连接的uip_conn 结构的指针。如果没有空闲连接,该函数返回NULL值。函数uip_ipaddr()可能用于将IP地址打包成uIP协议表示IP地址的双元素16位数组中。
两个使用示例如下所示,第一个示例显示如何打开与当前连接的远程终端TCP 端口8080 的连接。如果没有足够的TCP 连接时隙,允许打开新连接,则uip_connect()函数返回NULL值,并且当前连接被uip_abort()函数中止。
8.轮询
当连接空闲时,uIP协议会在每次周期性定时器触发时轮询应用程序,应用程序使用测试函数uip_poll()来检查uIP 是否正在轮询。轮询事件有两个目的:第一是让应用程序定期知道连接是空闲的,这允许应用程序关闭已经空闲太久的连接;第二是让应用程序发送已生成的新数据。应用程序只能在由uIP调用时发送数据,因此轮询事件是在空闲连接上发送数据的唯一方法。下面是使用uIP轮询的实例。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。