好久没有来博客了。编程
心情也很差,可是毕业设计仍是要继续的。数组
跟老师商量改了毕业设计的要求,MCU换了,TCP/IP协议栈也换了,换成STM32与UIP了。换的缘由很蛋疼,以前的那个跟我用的WIN7兼容性不好,不少软件出问题,因此放弃了。服务器
先从uip看起,版本1.0,貌似好久没有更新,这是最新的版本了吧。看看uip英文资料开始学习,仍是为了防止忘记,顺便为毕业论文准备,因此记下一些学习内容。网络
——————————————————————————————————————————————————分割线,上面是废话app
下面内容都是参考英文文档tcp
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地址。