3.2.5 Uip的主程序循环
当所有的初始化、配置等工作完成后,uip就不停地在这个主循环里一直运行了,这个主循环实际上才真正说明了uip大部分时间里做了些什么。
基本处理流程图如下:
图3-2
while(1)
{
uip_len = tapdev_read(uip_buf);//首先uip要不停的读设备,看设备里有没有新的数据出现。实际上,驱动代码也只会在这个循环里看到,uip的其他代码是与设备无关的。
if(uip_len > 0)//如果len>0,表示设备里有新数据,下面对新数据进行处理。 {
if(BUF->type == htons(UIP_ETHTYPE_IP))//表示收到的是IP数据包
{ uip_arp_ipin();//ARP对收到的IP包进行处理,如果收到的IP包中的源IP地址是本地网络上主机的IP地址,则只进行ARP缓存表的更新(更新源IP地址对应的MAC地址)或者插入(若表中没有该IP地址项,插入源IP->MAC对应关系)操作
uip_input();//调用uip_process(UIP_DATA)对数据包进行处理
if(uip_len > 0)//表示数据包处理完后,立即产生了要发送的数据 { uip_arp_out();//查看是否需要发送ARP请求,并为传出的ARP请求或IP包添加以太网头
33
tapdev_send(uip_buf,uip_len);//调用驱动程序发送缓存中的以太网帧
} }
else if(BUF->type == htons(UIP_ETHTYPE_ARP))//表示要处理的是ARP包(请求或应答)
{
uip_arp_arpin();//直接进行ARP处理。若为应答,则从包中取出需要的MAC地址加入ARP缓存表;若为请求,则将自己的MAC地址打包成一个ARP应答,发送给请求的主机。
if(uip_len > 0)//表示有ARP应答要发送 tapdev_send(uip_buf,uip_len); } {
} }
else if(timer_expired(&periodic_timer))//设备里没有新数据,检查周期性定时器是否期满,若期满进行下面的处理。 {
timer_reset(&periodic_timer);//复位定时器,将开始时间设为当前时间,使重新开始计时
for(uint32_t i = 0; i < UIP_CONNS; i++) {
uip_periodic(i);//对每一个连接进行周期性处理,这里实际上是调用uip_process(UIP_TIMER)进行处理
if(uip_len > 0)//表示缓存中有数据要发送
{
uip_arp_out();//查看是否要发送ARP请求,为ARP请求或IP数据包添加以太网帧头
tapdev_send(uip_buf,uip_len);
} }
#if UIP_UDP //如果有UDP连接,也对UDP连接进行处理
for(i = 0; i < UIP_UDP_CONNS; i++) { uip_udp_periodic(i);
if(uip_len > 0) { uip_arp_out(); tapdev_send(); } }
#endif
// Call the ARP timer function every 10 seconds.
//检查ARP缓存表中的表项是否到期,若到期则将该表项清0(表项保存时间为10秒)
if(timer_expired(&arp_timer))
34
{
timer_reset(&arp_timer); uip_arp_timer(); } }
}
由上可知,uip在不停的读设备、发数据,同时,当周期性定时器期满时,对定时器进行复位,并对连接和ARP表项进行周期性处理操作。
3.2.6 主要的处理函数uip_process()
该函数的处理过程总的来说分两种情况:有数据要处理;周期性定时器被激发。
3.2.6.1 数据处理又可细分为三种情况:
? ? ?
#define UIP_DATA 1 /* Tells uIP that there is incoming data in the uip_buf
buffer. The length of the data is stored in the global variable uip_len. */
#define UIP_POLL_REQUEST 3 /* Tells uIP that a connection should be polled. */ #define UIP_UDP_SEND_CONN 4 /* Tells uIP that a UDP datagram should be construc-
ted in the uip_buf buffer. */
3.2.6.2 定时器处理分为两种情况:
? ? {
//首先进行基础的检查,判断IP数据报首部的合法性
//如果收到的数据报长度比IP首部中记录的长度要大,说明是数据报被补0了,将正确的长度赋给len;否则,IP数据报传送过程中被破坏了,将其丢弃。
//检查分片标志,若非第一个分片需进行重组;若没有定义分片操作,而分片偏移非0,则将该IP数据报丢弃
//若主机IP地址全0,若定义了ping操作,且高层协议为ICMP,则接收到的为回送请求报文,进入icmp_input进行处理;否则,丢弃该报文
//若主机IP地址非全0 ,IP广播被支持,且目的地址为全1,则接收到的应该为UDP广播报文, 重新设定检验和,并进入udp_input处理
35
#define UIP_TIMER 2 /* Tells uIP that the periodic timer has fired. */ #define UIP_UDP_TIMER 5
? 首先分析UIP_DATA,对接收到的IP数据报的处理,对应大循环里的uip_input():
//非以上两种情况,则比较目的IP地址与本主机IP地址是否相符,并检查检验和是否合法,不符则丢弃该报文
//若该IP数据报的上层协议为TCP,则进入tcp_input进行处理
//若上层协议为UDP,则进入udp_input进行处理
//若既非ping命令,非广播,非TCP报文,也非UDP报文,则应该为ICMP报文,若上层协议不是ICMP则将该IP数据报丢弃,并更新统计信息。 }
? icmp_input:
{
//首先接收到的ICMP报文数加1
//如果报文类型不是回送请求,就将报文丢弃,相应的统计数据加1。
//将目的IP地址存入
//将报文类型改为回送回答 //重新计算检验和
//交换源和目的IP地址
//将ICMP发送报文数加1,之后转到send进行回送回答报文 }
? Send:
DEBUG_PRINTF(\
(BUF->len[0] << 8) | BUF->len[1]); //打印要发送了的IP数据报总长度UIP_STAT(++uip_stat.ip.sent);//将发送的IP数据报个数加1
uip_flags = 0; //将uip的标志位置0
return; //返回,并让被调用程序进行数据报发送(驱动程序tapdev_send())
? udp_input:
? /* 我们不对UDP/IP头做任何更改,所有复杂的事情都交给UDP应用程序来处理。如
果UDP应用程序设置了uip_slen, 表示有应用数据要发送*/ {
//如果定义了检验和,
//uip_len里存放应用数据长度
//获得应用数据的偏移地址
//判断检验和是否合法,不合法,将该数据包丢弃
//如果没有定义UDP检验和,则只取应用数据长度到uip_len //遍历UDP连接,如果本地端口号非零,且与目的端口号相等,同时远程端口号为0 或远程端口号与源端口号相等,且远程的IP地址为全0/全1或远程IP地址与源IP地址相同(有对应的UDP连接),就转到udp_found进行处 //没有找到对应的连接,则丢弃该UDP报文 }
36
相关推荐: