uIP介绍[通俗易懂]下面内容都是参考英文文档uip是一个开源的微型协议栈,主要用于8位,16位MCU,占用内存少,并且代码少,容易移植。它既可以用于多任务的操作系统中,如ucos。也能单独存在,传说中的裸奔。uip的主循环uip主循环中重复做着两件事情。查看是否收到数据包查看周期性超时是否发生如果有数据包到达,则会在主循环中调用输入处理函数,uip_input(),
大家好,又见面了,我是你们的朋友全栈君。
下面内容都是参考英文文档
uip是一个开源的微型协议栈,主要用于8位,16位MCU,占用内存少,并且代码少,容易移植。
它既可以用于多任务的操作系统中,如ucos。也能单独存在,传说中的裸奔。
uip的主循环
uip主循环中重复做着两件事情。
如果有数据包到达,则会在主循环中调用输入处理函数,uip_input(),这个函数不会发生阻塞,而是立刻返回。它返回时,相应接收这个数据包的应用程序或协议栈会产生一个或多个将要被发送的回应数据包。如果是这样的话,底层的网络设备驱动会被调用去发送这些数据包。
周期性超时是用于驱动依靠定时器的TCP机制,比如延时确认,重发,估算往返时间。如果主循环中周期性定时发生,uip就会调用定时处理函数uip_periodic().
uip与具体平台有关的函数
uip有几个函数是跟具体平台有关实现有关的。一个是校验和计算,一个是32位I附加值运算。
校验和计算
在接收和发送数据包过程,校验和计算都是很重要的,且每次发送和接收时都要计算校验和,所以校验和函数必须很有效率。这在很多情况下就意味着校验和计算必须针对于运行uip的不同平台而做出一些手动调整。
虽然uip包含了通用的校验和计算,但是也留了两个函数来针对特定的平台,这两个函数是uip_ipchksum()和uip_tcpchksum(),在这两个校验和函数中,可以使用汇编语言高度优化来超过C语言的效率。
32位附加值运算
TCP中的32位运算也并不是在所有平台上都有效,所以有一个针对特定平台的关于32位附加值实现的函数uip_add32().
uip的内存管理
uip不使用动态分配内存。而是用一个单独的全局缓冲区储存数据包,还有一个固定的数组来储存连接状态。这个全局缓冲区足够大可以储存一个包的最大大小。当有个数据包被接收到,设备驱动就会把这个包放在这个全局缓冲区里然后调用TCP/IP栈。如果这个包包含数据的话,那么TCP/IP协议栈就会通知相应的应用程序。因为这个在这个缓冲区的数据会被接下来到达的数据覆盖,所以应用程序不得不立即处理这些数据或者把这些数据放到第二缓冲区以便接下来处理。在应用程序处理这些数据之前这个全局缓冲区不会被新来的数据包覆盖。在处理当前数据包时新来的数据包必须排队。许多单片以太网控制器都有足够大的片内缓冲区来包含至少4个最大大小的以太网帧。如果缓冲区满了,接下来的数据就会被丢弃。这会导致性能降低,但只是发生在多重连接的情况下。这是因为uip建议一个非常小的接收窗口,这意味着每次连接时仅仅只有一个TCP段在网络中。
在uip中,用于接收数据包的全局缓冲区也用于TCP/IP头部的传出数据。如果应用程序发送动态数据,它会使用全局缓冲区的部分来作为临时缓冲区。为了传送这些数据,应用程序会传递一个指针和数据的长度到栈中。TCP/IP的头部被写入全局缓冲区,并且一旦产生头部数据,设备驱动就会发送头部信息和应用程序数据到网络中。这个数据不是排队需要重发的数据。而是应用程序重新产生数据如果需要重发的话。
uip需要的总的内存量是根据特定的运行情况确定的。内存的配置确定了系统处理的流量数和最大连接数。
uip的API接口
因为与uip协议栈的原因,它不使用传统的BSD套接字API,它有两种API用于应用编程,一种类似BSD套接字API,还有一种是基于事件的API,这种需要内存比前者更少。
基于事件的API的意思是有一个应用程序运行在uip之上,当处理发生的特定事件时,由uip调用处理相关事件。这些事件包括接收到或发送包,建立连接时,当数据需要重发时等等。
另外uip不同于其他TCP/IP协议栈的地方是需要手动处理重发数据,也就是要自己在应用程序里编写代码处理要重发的数据,其他协议栈都是自动处理。这样做的理由也是为了节约内存。
应用程序事件
处理应用程序事件的函数是UIP_APPCALL(),当发生任何事件时都会调用此函数。每个事件都有特定的测试函数来区分是哪种事件,这个函数是一个宏实现,需要注意的是,事件可以同时发生。
连接指示器
当uip调用一个应用程序,全局变量uip_conn被设置成一个指向uip_conn结构体的指针,这个变量被称作当前连接。唉这个结构体中有些变量是有用的,比如用来区分要连接的是何种服务或者是连接对方的IP地址。一个典型的应用就是通过结构体中的变量uip_conn->lport看连接端口来确定是何种服务,比如如果是80端口,就是HTTP服务器应用程序。
数据接收
如果测试函数uip_newdata()非零,那么说明接收到新数据。接收到数据的长度可以通过uip_datalen()函数获得。数据不会被uip缓冲,但是当函数返回时数据会被覆盖。所以应用程序必须及时处理该数据或者将数据放入另一个缓冲区中。
发送数据
发送数据时,uip通过接收者的TCP窗口大小和有效的缓冲区空间来调整发送数据的长度。缓冲空间的大小由内存配置决定,因此有可能不是所有发送的数据都会到达接收者一方。所以可以调用uip_mss()看实际到底有多少数据发送出去了。
发送数据的函数是uip_send(),这个函数需要两个参数,一个是指向发送数据的指针和发送数据的长度。如果应用程序需要RAM空间来发送数据的话,那么包缓冲区(就是由uip_appdata指向的缓冲区)可以用于此目的。
在一个连接的同一时间只能有一块数据被发送,在一次应用程序里调用多次uip_send()是不可能的,它只会把最后调用的数据包发出去。
数据重发
重发是由周期TCP定时器驱动的,每次超时定时器调用时,每个连接的重发定时器就会减少,如果重发定时器减少到0,那么重发就要重发数据。因为uip在发送数据包后不会保存数据,所以需要手动处理重发数据。当uip确定有一段需要重发时,应用程序调用uip_rexmit()设置标志,表明有重发要求。
应用程序会检查重发标志然后产生重发数据,从应用程序角度来看,重发的数据和原来的数据没有什么不同,所以这两段代码是一样的。
关闭连接
应用程序通过调用uip_close()来关闭连接。这种关闭是正常的关闭连接。如果为了表示是严重错误而导致的关闭,那么应用程序应该调用uip_abort()来终止连接。
如果连接被关闭的话,那么uip_closed()会返回真,接着应用程序就可以继续做必要的清理工作了。
报告错误
在一个连接中有两种严重的错误会发生,一种是连接被异常终止或者数据多次重发无效而终止。uip会分别通过调用测试函数uip_aborted()和uip_timeout()来报告这些错误信息。
轮询
当连接空闲时,uip就会在每次超时时间到达时进行轮询。轮询的函数是uip_poll()。
轮询的目的有两个,第一个目的是让应用程序知道有空闲的连接,并让空闲太久的连接关闭。第二个目的是让应用程序发送新产生的数据。发送数据只能由uip来调用。因此轮询是在空闲连接时发送数据的唯一方式。
监听端口
监听端口的函数是uip_listen()。当连接需要和端口绑定时,uip就会创建一个连接并调用此函数。如果应用程序调用此函数的话,uip_connected()就会返回真。
开始连接
打开一个新的连接的函数是uip_connect(),这个函数会返回一个指针指向uip_conn()结构体。如果没有多余的空闲槽,那么函数返回NULL。
函数uip_ipaddr()用于将IP地址放入两个16位的数组,用来表示IP地址。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/125215.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...