Chinaunix首页 | 论坛 | 博客
  • 博客访问: 399431
  • 博文数量: 87
  • 博客积分: 1171
  • 博客等级: 少尉
  • 技术积分: 1068
  • 用 户 组: 普通用户
  • 注册时间: 2012-09-19 14:34
文章分类

全部博文(87)

文章存档

2014年(10)

2013年(24)

2012年(53)

我的朋友

分类: 嵌入式

2012-10-13 22:05:46

       1,了解uip本身。

      一 :uip协议栈实现方法简述

       uIP协议栈主要提供了三个函数供系统底层调用。即uip_init(),uip_input()和uip_periodic()。其与应用程序的主要接口是UIP_APPCALL()。uip_init()是系统初始化调用的,主要初始化协议栈的侦听端口和默认所有连接时关闭的。当网卡驱动收到一个数据包时,将放进全局缓冲区uip_buf中,包的大小由全局变量uip_len约束。同时调用uip_input()函数,这个函数将会根据包首部的协议处理这个包和需要时调用应用程序。当uip_input()返回时,一个数据包同样放在全局缓冲区uip_buf里,大小赋给uip_len。假如uip_len是0,则说明没有包要发送。否则调用系统的发送函数将包发送到网络上。uIPzh周期计时是用于驱动所有的uIP内部时钟事件。当周期计时激发,每一个TCP连接都会调用uIP函数uip_periodic(),类似于uip_input()函数,uip_periodic函数返回时,输出的IP包要放到uip_buf中,供底层系统查询uip_len的大小发送。

 

 

      UIP实现了TCP/IP协议集的四个基本协议,ARP ,IP,ICMP,TCP。

      1,实现ARP地址解析协议时为了节省存储空间,ARM应答包直接覆盖ARM请求包。

      2,实现IP网络协议时对原协议经行了极大的简化,它没有实现分片和重组。

    3,实现ICMP网络控制报文协议时,只实现echo(回响)服务。uIP在生存回响报文时并不重新分配存储空间,而是直接修改echo请求报文来生成回响报文。将ICMP类型字段从“echo”类型改变为

“echo repley”类型,重新计算校验和修改校验字段。

      4,uIP里的TCP没有实现发送和接收数据的滑动窗口。每个TCP连接的状态由uip_conn结构保存,uip_conn结构包括当地和远端的TCP端口编号,远程的TCP端口编号,远程主机的IP地址,重发时间值,上一段重发的编号,和连接的段的最大尺寸等信息。一个uip_conn结构数据组保存所有的连接,数组的大小为同时连接的最大数量。为了减少存储器的使用量,在处理重发时uip并不缓冲发送的数据包,而是由应用程序在需要重发时重新生成发送的数据。

   

   二:uIP协议栈的接口

    uip协议栈为了具有最大的通用性,在实现时将底层硬件驱动和顶层应用层之外的所有协议集“打包”在一个“库”里。协议栈通过接口与底层硬件和顶层应用“通信”,通过这种方式,uip具有极高的通用性和独立性,移植到不同的系统和实现不同的应用都和很方便,很好的体现了TCP/IP协议平台无关的特点。uIP协议栈与系统底层和应用程序之间的接口关系如图:

 

        1,uIP协议栈与系统底层的接口

           uip与系统底层的接口包括与设备驱动的接口和与系统定时器的接口两类。

        1.1 uIP与设备驱动接口

           uIP通过uip_input()和全局变量Uip_buf uip_len来实现与设备驱动的接口。uip_buf用于存放接收到和要发送的数据包,为了减少存储器的使用,接收数据包和发送数据包使用相同的缓冲区。uip_len表明接收发送缓冲区的数据长度,通过判断uip_len的值是否为0来判断是否接收到新的数据,是否有数据发送。当设备驱动接收到一个IP包并放入到输入包缓冲区uip_buf后,应该调用uip_input()函数

。uip_input()函数式uIP协议栈的底层入口,由它处理收到的IP包。当uip_input()返回时,若有数据要发送,则发送数据包放在缓冲区里。包的大小有全局变量uip_len指明。

 

        1.2 uIP与系统计时接口

        TCP/IP协议要处理许多定时事件,例如包的重发、ARP表项更新。系统计时用于为所有uIP内部时钟事件计时。当周期计时激发,每个TCP连接应该调用uIP函数uip_periodic()。TCP连接编号作为参数传递给uip_periodic()函数。uip_periodic()函数检查参数指定的连接状态,如果需要重发数据到缓冲区并修改uip_len的值。当uip_periodic()函数返回后,应该检查uip_len的值,若不为0 则将uip_buf缓冲区中的数据包发送到网络上。

     ARP协议对于构建在以太网上的TCP/IP协议时必须的,但是对于构建于其他网络接口(例如:串行链路)上的TCP/IP则不是必需的,为了结构化的目的,uIP将ARP协议作为一个可添加的模块单独实现。因此,ARP表项的定时器更新需要单独处理。系统定时器对ARP表的更新进行定时,定时时间到则调用uip_arp_timer()函数对过去表项进行删除。

        2,uIP协议栈与应用程序的接口

        应用程序作为单独的模块由用户实现,uIP协议栈提供一系列接口函数供用户程序调用,用户需要将应用层入口程序作为接口提供给uIP协议栈,定义为宏UIP_APPCALL()。uIP在接收到底层传来的数据包后,若需要送上层应用程序处理,它就调用UIP_APPCALL(),uIP提供给应用程序的接口按功能描述如下:

      2.1 接收数据接口:应用程序利用uip_newdata()函数检测是否有新数据到达。全局变量uip_appdata指针指向实际数据。数据的大小通过uip_datalen()函数获得。

      2.2 发送数据接口:应用程序通过使用uIP函数uip_send()发送数据。uip_send()函数利用两个参数;一个指针指向发送数据起始地址,另一个指明数据的长度。

      2.3重发数据接口:应用程序通过测试函数uip_rexmit()来判断是否需要重发数据,如果需要重发,则调用uip_send()函数重发数据包。

     2.4关闭连接接口:应用程序通过调用uip_close()函数关闭当前连接。

     2.5报告接口错误:uIP提供错误报告函数检查连接中出现的错误。应用程序可以使用两个测试函数uip_aborted()和uip_timedout去测试那些错误情况。

     2.6轮询接口:当连接空闲时,uIP会周期性的轮询应用程序,判断是否有数据要发送。应用程序使用测试函数uip_poll()去检查推是否被轮询过。

    2.7监听端口接口:uIP维持一个监听知名TCP端口的列表。通过uip_listen()函数,一个新的监听端口打开并添加到监听列表中,当在一个监听端口上接收到一个新的连接请求时,uIP产生一个新的连接和调用该端口对应的应用程序。

    2.8打开连接接口:在uIP里面使用uip_connect()函数打开一个新的连接。这个函数打开一个新的连接到指定的IP地址和端口,返回一个新的指针到uip_conn结构。如果没有空闲的连接槽,函数返回空值。

    2.9数据流控制接口:uIP提供函数uip_stop()和uip_restart()用于TCP连接的数据流控制。应用程序可以通过函数uip_stop()停止远程主机发送数据。当应用程序准备好接受更多数据时,调用函数uip_restart()通知远程终端再次发送数据。函数uip_stopped()可以用于检查当前连接是否停止。 

 

   3;uIP在51系列单片机上的应用

     51系列单片机具有悠久的历史和广泛的应用,许多公司推出了具有更高处理速度的51内核的8为单片机,被应用到各个领域内。因此使用uIP这种免费的TCP/IP协议栈解决由51内核的单片机构建的低端嵌入式设备的网络接入问题具有一定的代表性。下面将讨论利用uIP协议栈在51单片机上实现间的的WEB SERVER ,远端用户可以通过浏览器访问存储在单片机系统上的web页面。

     硬件平台接口如图(二)所示:其中单片机选用PHILIPS公司的p89C51RD2,64k字节的串行EEPROM可以用于存储WEB页面。采用ISA接口的以太网接口芯片RTL8019AS连接到以太网上。通过MAX232实现与PC机的串行连接,可以显示调试信息。

 

 

 

硬件平台结构

 

    

uIP协议栈是以函数库的形式提供的,本身不提供网络驱动和上层应用程序。因此为了完成指定的功能,开发者必须添加一下几个模块:底层RTL8019AS网卡芯片的驱动、应用层基于HTTP协议的WEB SERVER的实现,系统定时器。

 

      RTL8019AS的驱动主要包括三部分:init_8019as()函数完成网卡芯片的上电初始化,包括设定网卡物理地址,设定收发缓冲区位置和大小等;eth_send()函数完成数据的发送;eth——rcve()函数完成以太网数据的接收。底层网络设备驱动程序与uip协议栈通过两个全局变量进行接口:变量uip_buf为首发缓冲区首地址;uip_len为收发的数据长度。eth_send()函数将uip_buf里的uip_len长度的数据发送到以太网上。eth——rcve()函数将接收到的数据存储到uip_buf指定的缓冲区中,同时修改uip_len的值。

 

      uIP提供的源代码中包括一个基于HTTP协议的WEB SERVER示例,该WEB SERVER通过简单的文件系统在数据存储器中存储静态页面,同时具有CGI功能。用户可以参考该示例以及uIP提供给应用程序的接口函数说明实现自己的应用层功能。用户应用程序必须将UIP_APPCALL宏定义为该层的服务程序。例如:在示例程序中WEB SERVER的处理程序为httpd()函数,则要进行如下的宏定义:

 #define UIP_APPCALL httpd

 

     51系列单片机上有2到3个定时计数器,可以选择其中的一个来为TCP/IP协议中与时间有关的事件定时。需要由用户处理的定时事件包括:为uipperiodic()函数的执行提供基准,还要为ARP表项的更新定时。uip_periodic()函数每0.5秒执行一次,ARP表项每10秒更新一次。

    

     uIP的设置单独包含在一个叫uipopt.h的头文件里,都是以宏的形式定义方便于修改。用户应根据自己的应用在uipopt.h文件里设置本地的物理地址、IP地址、网卡地址、收发缓冲区的大小、支持的最大连接数、ARP表大小等等选项。

 

     添加了必须的模块,对uIP进行了正确地配置后,需要编写主程序函数。针对基于以太网的WEB SERVER应用,主程序在完成初始化后将不停的进行查询,如果有新的数据包达到则送uip_input()函数处理,如果没有新数据包到达则处理定时事件。框架代码如下所示:

     void main(void)

    {

     /* 省略部分代码*/

    /*设置TCP超时处理时间和ARP老化时间*/

     timer_set(&periodic_timer,CLOCK_CONF_SECOND/2);

     timer_set(&arp_timer,CLOCK_CONF_SECOND*10);

     

     /*定时器初始化*/

      init_Timer();

    /*协议栈初始化*/

    uip_init();

    uip_arp_init();

    /*应用层初始化*/

     exampl1_init();  //绑定端口

    /*驱动层初始化*/

    etherdev_init();

    /*IP 地址、网卡、掩码设置*/

     uip_ipaddr(ipaddr,192.168.1.9);

     uip_sethostaddr(ipaddr);

     uip_ipaddr(ipaddr,192.168.1.16);

     uip_setdraddr(ipaddr);

    uip_ipaddr(ipaddr,255,255,255,0);

     uip_setnetmask(ipaddr);

 

   /*主循环*/

 

      while(1)

      {

      /*从网卡读取数据*/

      uip_len=etherdev_read();

     /*假如存在数据则按协议处理*/

      if(uip_len>0)

     /*收到的是IP数据,调用uip_input()处理*/

      if(BUF->type=htons(UIP_ETHTYPE_IP)

    {

     uip_arp_ipin();// 不理解。

     uip_input();

    /*处理完后,假如uip_buf中有数据,则调用etherdev_send发送出去*/

    if(uip_len>0)

      {

     uip_arp_out() ;

     etherdev_send();

      }

     }

    /*收到的数据时ARP数据包,调用uip_arp_arpin()处理*/

   else if(BUF->type==htons(UIP_ETHTYPE_ARP)){

     uip_arp_arpin();

     if(uip_len>0)

     etherdev_send();

   }

   

   /*查看0.5S是否到了,到了则调用uip_periodic处理TCP超时程序*/

    else if(timer_expired(&periodic_timer))

   {

     timer_reset(&periodic_timer);

     for(i=0;i

    {

     uip_periodic(i);

     if(uip_len>0)

    {

       uip_arp_out();

     etherdev_send();

    }

   }

    if(timer_expired(&arp_timer))

    {

    timer_reset(&arp_timer);

    uip_arp_timer();

    }

   } 

  }

   return;

}

  

    通过实际的代码说明uIP协议栈的主控制循环。

 

   以上是实例在keil  c51编译器下设置大模式,进行编译,对uIP代码部分可以不做任何修改,对HTTP示例代码仅需针对类型表达进行极少量的修改即可编译通过。

阅读(5016) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~