这个文档描述uIP TCP/IP栈。 uIP TCP/IP栈是使用于低至8位或16位微处理器的嵌入式系统的一个可实现的极小的TCP/IP协议栈。现时,uIP代码的大小和RAM的需求比其它一般的TCP/IP栈要小。
uIP栈使用一个基于编程模块事件去减少代码的大小和RAM的使用量。基于系统的底层和uIP之间的接口的回应会在文档里描述。系统的底层和uIP之间的接口是隐蔽的。文档后面包含了一些uIP应用编程例子。
uIP 的代码和这个文档的新版本可以在uIP的主页下载 http://dunkels.com/adam/uip/。
这个文档描述了uIP的0.6版。
1 引言
新近这些年里,人们对连接一个甚至只是小装置到一个现有的IP网络例如全球因特网的兴趣增加了。为了可以通过因特网通讯,一个可实现的TCP/IP协议栈是必须的。uIP是一个可实现的TCP/IP协议组件的一个非常重要的部分。uIP的实现目标是保持代码大小和储存器使用量最小。现时,uIP代码的大小和RAM的需求比其它一般的TCP/IP栈要小。uIP使用C编程语言,它可以自用分发和使用于商业和非商业目的。
其它的TCP/IP栈,储存器经常用于数据缓存,等待一个数据已经成功送达的确应信号。 事实上,数据包丢失了,数据必须重发。有特色的是,数据是缓存在RAM里,如果需要重销并发数据,应用程序可以快速重生数据。例如,一个HTTP服务器服务的大部分是ROM里的静态和半静态页,不需要在RAM里缓存静态内容。所以,如果一个包丢失了,HTTP服务器可以容易地从ROM里重生数据。数据简单地从原先的位置读回来。uIP的优越性是允许应用程序参加数据重发。
这个文档由以下部分组成,第2节描述在系统和应用的立场上怎样使用uIP。第3节详细讨论协议实现细节。第4 节覆盖了uIP的配置,第5节描述uIP的结构部分。最后,第6节提供一些uIP的应用编程实例。
2 uIP的接口技术
uIP可以看作是一个代码库为系统提供确定的函数。图 1 展示了uIP,系统底层和应用程序之间的关系。uIP提供三个函数到系统底层, uip_init(), uip_input(),和uip_periodic()。应用程序必须提供一个回应函数给uIP。当网络或定时事件发生时,调用回应函数。 uIP提供许多函数和堆栈交互。
要注意的就是uIP提供的大部分函数是作为C的宏命令实现的,主要是为了速度,代码大小,效率和堆栈的使用。
图 1 uIP就好像一个库
2.1 uIP应用接口
BSD套节字接口使用于大部分的 *** 作系统,它不适合微系统,因为在应用设计里,它逼使一个线程基于编程模块。一个多线程环境代价重大,因为,不但在亏告迹线程管理里涉及增加代码的复杂性,而且保存每线程堆栈需要额外的储存器,还有执行任务切换的时间开销也摊派在这里。微型系统不会有足够的资源去实现一个多线程环境,因此需要这个环境的应用接口不适合uIP。
相反,uIP使用一个基于编程模块的事件,模块是实现友闹应用程序作为一个C函数被uIP调用的地方,uIP响应一定的事件。uIP调用应用在,当接收数据时,当数据成功送达另一方中止连接时,当一个新的连接建立时,或者当数据需要重发时。 应用程序也周期性地循环等待新数据。应用程序只提供一个回应函数;它提升了应用程序处理不同的网络服务的不同的端口和连接的映射
uIP与其它TCP/IP栈不同的是,当正在重发工作,它需要应用程序的帮助。其它TCP/IP栈缓存传输数据在储存器里,直到在连接的最后数据确应成功发送。如果数据需要重传,堆栈在没有通知应用程序下监视着重传工作。通过这种方法,当要等待一个确应,数据必须缓存在储存器里,如果产生一个重发,应用程序可以快速重新生成数据。为了减少储存器的使用量,uIP利用的论据是应用程序可以重新生成发送的数据和让应用程序参加重发。
2.1.1 uIP应用事件
应用程序必须作为C函数去实现,uIP在任何一个事件发生时调用UIP_APPCALL()。表 1 列出可能的事件和每个事件的对应测试函数。测试函数用于区别不同的事件。函数是作为C宏命令实现的,将会是零值或非零值。注意的是某些函数可以在互相连接时发生(也就是新数据可以在数据确应的同时到达)。
表 1: uIP应用事件和对应的测试参数
一个数据包到达,确应先前发送到数据 uip_acked()
应用程序的新数据包已经到达 uip_newdata()
一个远程主机连接到监听端口 uip_connected()
一个到达远程主机的连接成功建立 uip_connected()
计时时间满重发 uip_rexmit()
计时时间满周期性轮询 uip_poll()
远程主机关闭连接 uip_closed()
远程主机中断连接 uip_aborted()
由于太多重传,连接中断 uip_timedout()
当应用程序调用时,uIP设置全局变量uip_conn去指向当前连接的uip_conn结构 (图 5) 。这可以用来区别不同的服务。一个典型的应用是检查uip_conn->lport (当地TCP端口号)去决定那个服务连接应该提供。例如,如果值uip_conn->lport等于80,应用程序可以决定启动一个HTTP服务,值是23是启动TELNET服务。
2.1.2 接收数据
如果uIP测试函数uip_newdata()值为1,远程连接的主机有发送新数据。uip_appdata指针指向实际数据。数据的大小通过uIP函数uip_datalen()获得。在数据不是被缓冲后,应用程序必须立刻启动。
2.1.3 发送数据
应用程序通过使用uIP函数uip_send()发送数据。uip_send()函数采用两个参数;一个指针指向发送数据和数据的长度。如果应用程序为了产生要发送的实际数据需要RAM空间,包缓存(通过uip_appdata指针指向)可以用于这方面。
在一个时间里应用程序只能在连接中发送一块数据。因此不可以在每个应用程序启用中调用uip_send()超过一次;只有上一次调用的数据将会发出后才可以。注意,调用uip_send()以后会改变某些全局变量,在应用函数返回前它不能被调用。
2.1.4 重发数据
如果数据在网络中丢失,应用程序必须重发数据。无论数据收到或没有收到,uIP保持跟踪,和通知应用程序什么时候察觉出数据是丢失了。如果测试函数uip_rexmit()为真,应用程序要重发上一次发出的数据。重发就好像原来那样发送,也就是通过uip_send()。
2.1.5 关闭连接
应用程序通过调用uip_close()关闭当前连接。这会导致连接干净地关闭。为了指出致命的错误,应用程序可以中止连接和调用uip_abort()函数完成这个工作。
如果连接已经被远端关闭,测试函数uip_closed()为真。应用程序接着可以做一些必要的清理工作。
2.1.6 报告错误
有两个致命的错误可以发生在连接中,不是连接由远程主机中止,就是连接多次重发上一数据和被中止。uIP通过调用函数报告这些问题。应用程序使用两个测试函数uip_aborted()和uip _timedout() 去测试那些错误情况。
2.1.7 轮询
当连接空闲时,uIP在每一个时候周期性地轮询应用程序。应用程序使用测试函数uip_poll()去检查它是否被轮询过。
2.1.8 监听端口
uIP维持一个监听TCP端口列表。通过uip_listen()函数,一个新的监听端口打开。当一个连接请求在一个监听端口到达,uIP产生一个新的连接和调用应用程序函数。如果一个新连接产生,应用程序被调用,测试函数uip_connected()为真。
2.1.9 打开连接
作为uIP的0.6版,在uIP里面通过使用uip_connect()函数打开一个新连接。这个函数打开一个新连接到指定的IP地址和端口,返回一个新连接的指针到uip_conn结构。如果没有空余的连接槽,函数返回空值。为了方便,函数uip_ipaddr()可以用于将IP地址打包进两个单元16位数组里,通过uIP去代表IP地址。
使用两个例子,在图 2 和图 3 展示。第一个例子展示了怎样打开一个连接去远端TCP端口8080。如果没有足够的TCP连接插槽去允许一个新连接打开,uip_connect()函数返回NULL和通过uip_abort()中止当前连接。第二个例子展示怎样打开一个新连接去指定的IP地址。这例子里没有错误检查。
void connect_example1_app(void) {
if(uip_connect(uip_conn->ripaddr, 8080) == NULL) {
uip_abort()
}
}
图 2:打开一个连接去当前连接的远端的端口8080
void connect_example2(void) {
u16_t ipaddr[2]
uip_ipaddr(ipaddr, 192,168,0,1)
uip_connect(ipaddr, 8080)
}
图 3: 打开一个到主机192.168.0.1上端口8080的连接
2.1.10 数据流控制
通过函数uip_stop()和uip_restart(),uIP提供存取TCP数据流的控制途径。设想一个应用程序下载数据到一个慢速设备,例如磁盘驱动器。如果磁盘驱动器的作业队列满了,应用程序不会准备从服务器接收更多的数据,直到队列排出空位。函数uip_stop()可以用于维护流控制和停止远程主机发送数据。当应用程序准备好接收更多数据,函数uip_restart()用于告知远程终端再次发送数据。函数uip_stopped()可以用于检查当前连接是否停止。
2.2 uIP/系统接口
从系统的立场看, uIP由3个C函数 uip_init(),uip_input(), 和 uip_periodic()。uip_init()函数用于初始化uIP堆栈和在系统启动期间调用。当网络设备驱动器读一个IP包到包缓存时,调用函数uip_input()。周期性运行是调用uip_periodic(),代表的是一秒一次。调用uIP函数是系统的职责。
2.2.1 uIP/设备驱动接口
当设备驱动放一个输入包在包缓存里(uip_buf),系统应该调用uip_input()函数。函数将会处理这个包和需要时调用应用程序。当uip_input()返回,一个输出包放在包缓存里。包的大小由全局变量uip_len约束。如果uip_len是0,没有包要发送。
2.2.2 uIP/周期计时接口
周期计时是用于驱动所有uIP内部时钟事件,例如包重发。当周期计时激发,每一个TCP连接应该调用uIP函数uip_periodic()。连接编号传递是作为自变量给uip_periodic()函数的。类似于uip_input()函数,当uip_periodic()函数返回,输出的IP包要放在包缓存里。图 4 展示了调用uip_periodic()函数和监视输出包的一小段代码。在这个特别的例子,函数netdev_send()是网络驱动的部分,将uip_buf数组的目录发出到网上。
for(i = 0i <UIP_CONNS++i) {
uip_periodic(i)
if(uip_len >0)
netdev_send()
}
图 4:周期计时和uIP的接口的例子代码.
2.3 uIP 函数总结
表 2 包含了所有uIP提供的函数
表 2: uIP 函数总结
系统接口
uip_init()
uip_input()
uip_periodic() 初始化uIP
处理输入包
处理周期计时事件
应用程序接口
uip_listen()
uip_connect()
uip_send()
uip_datalen()
uip_close()
uip_abort()
uip_stop()
uip_stopped()
uip_restart() 开始监听端口
连接到远程主机
在当前连接发送数据
输入数据的大小
关闭当前连接
中止当前连接
停止当前连接
查找连接是否停止
重新启动当前连接
测试函数
uip_newdata()
uip_acked()
uip_connected()
uip_closed()
uip_aborted()
uip_timeout()
uip_rexmit
uip_poll() 远程主机已经发出数据
确应发出的数据
当前连接刚连上
当前连接刚关闭
当前连接刚中止
当前连接刚超时
数据重发
应用程序循环运行
其它
uip_mss()
uip_ipaddr()
htons(),ntohs() 获得当前连接的最大的段大小
将IP地址结构打包
在主机和网络之间转换字节次序
简单来说, TCP Receive Window是在TCP连接两端都有的缓冲区, 用于暂时保存到来的数据. 在这个缓冲区中的数据会被发送到应用程序中, 为新到来的数据腾出棚仿老圆空间. 如果这个缓冲满了, 那么数据的接收方会警告发送方在缓冲去清空之前已链含纤经不能在收取更多的数据了. 这其中涉及到一些细节, 但那都是很基本的东西. 一般, 设备会在TCP Header信息中通知对方当前它的TCPWindows的大小.欢迎分享,转载请注明来源:内存溢出
评论列表(0条)