contiki -2.6中UDP数据包的发送流程

原创 2013年12月01日 23:02:56

contiki 中数据包的发送流程

—  2013/11/20/星期三

学习进度:

(1)      已经阅读完RFC6282,6LowPan适配层IPHC,NHC协议设计,及其代码实现。

(2)      已经阅读完RFC4861 邻居发现协议及其代码实现。

(3)      本文档说明contiki 系统中UDP报文的发送流程,分析函数调用关系。

 

 

 

待完成任务:

(1)      Contiki中数据包的接收流程,分析函数调用关系。

(2)      再详细阅读RFC4944RFC6282,分析其实现方式的区别和特点。

(3)      阅读Rime协议栈,了解Mesh-under 路由原理和代码实现。

(4)      学习TCP/IP详解卷,补充相关知识。


 

 

 

 

 


一、网络层UDP传输


下面讨论发送数据包的流程。发数据包的过程相对复杂。用到了回调机制。下面分别说明。

doc/example-program.c 为例,该函数执行的事情是:周期性的广播发送hello

PROCESS_THREAD(example_program_process,ev, data)

static structuip_udp_conn *c;

    PROCESS_BEGIN();

  c = udp_broadcast_new(UIP_HTONS(4321), NULL);

while(1)

{

    etimer_set(&timer,CLOCK_SECOND);

   PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer));

    tcpip_poll_udp(c);

    PROCESS_WAIT_EVENT_UNTIL(ev ==tcpip_event);

    uip_send("Hello", 5);

}

        PROCESS_END();

}

首先解释:c =udp_broadcast_new(UIP_HTONS(4321), NULL);先创建UDP端口号是port=4321。









接下来从tcpip_poll_udp(c) 开始

void

tcpip_poll_udp(structuip_udp_conn *conn)

{

 process_post(&tcpip_process, UDP_POLL, conn);                     // tcpip_process 传递消息 UDP_POLL

}

tcpip_process 收到消息后,进行的处理如下:

static voideventhandler(process_event_t ev, process_data_t data)   //core/net/tcpip.c

{

 switch(ev) {

   case PROCESS_EVENT_EXITED:

   case PROCESS_EVENT_TIMER:

   case TCP_POLL:

   case UDP_POLL:

     if(data != NULL) {

        uip_udp_periodic_conn(data);  // 产生数据

    tcpip_ipv6_output(); //  IPv6  发送也许有报头压缩

     }

     break;

  };

}

其中uip_udp_periodic_conn 用于产生数据包,tcpip_ipv6_output()用于发送。

先看uip_udp_periodic_conn:

#define uip_udp_periodic_conn(conn) do { uip_udp_conn =conn;   \

uip_process(UIP_UDP_TIMER); } while(0)

转到uip_process() 这是uip的核心处理函数。

void uip_process(u8_t flag)     \core\net\uip6.c

 if(flag == UIP_UDP_TIMER) {

   if(uip_udp_conn->lport != 0) {

     uip_conn = NULL;

     uip_sappdata = uip_appdata = &uip_buf[UIP_LLH_LEN + UIP_IPUDPH_LEN];

     uip_len = uip_slen = 0;

     uip_flags = UIP_POLL;

     UIP_UDP_APPCALL();   /* 产生应用层数据 */

      goto udp_send;

   } else {

     goto drop;

}

udp_send:

/* 填充udp的包头*/

  }

下面看 UIP_UDP_APPCALL 是如何产生数据的:

#defineUIP_UDP_APPCALL   tcpip_uipcall

Void tcpip_uipcall(void)            \core\net\uip6.c

{

 register uip_udp_appstate_t *ts;

  ts =&uip_udp_conn->appstate;

 if(ts->p != NULL) {

    process_post_synch(ts->p,tcpip_event, ts->state);       \core\net\tcpip.c

  }

}

通过process_post_synch(ts->p, tcpip_event, ts->state) 调回到udp连接相关联的进程,即我们最初的进程:example_program_process,向这个进程发送消息: tcpip_event,因此可以执行uip_send("Hello", 5);

PROCESS_THREAD(example_program_process,ev, data)

static structuip_udp_conn *c;

    PROCESS_BEGIN();

  c = udp_broadcast_new(UIP_HTONS(4321), NULL);

while(1)

{

    etimer_set(&timer,CLOCK_SECOND);

   PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer));

    tcpip_poll_udp(c);

    PROCESS_WAIT_EVENT_UNTIL(ev ==tcpip_event);

    uip_send("Hello", 5);

}

        PROCESS_END();

}

这时,example_program_process 将继续执行,调用 uip_send()

Voiduip_send(const void *data, int len)     \core\net\uip.c

{

   memcpy(uip_sappdata, (data), uip_slen);

}

Uip_send 执行的功能就是把应用层的数据拷贝到 数据包的对应位置,然后返回。

至此在eventhandler()函数的   uip_udp_periodic_conn(data);执行完毕,进入tcpip_ipv6_output()处理。

static voideventhandler(process_event_t ev, process_data_t data)   //core/net/tcpip.c

switch(ev) {

   case UDP_POLL:

     if(data != NULL) {

        uip_udp_periodic_conn(data);  // 产生数据

    tcpip_ipv6_output(); //  IPv6  发送也许有报头压缩

     }

 

void tcpip_ipv6_output(void)    \core\net\tcpip.c

{

#ifUIP_CONF_IPV6_QUEUE_PKT

 

      if(uip_packetqueue_buflen(&nbr->packethandle)!= 0) {

     。。。。。。

        tcpip_output(&nbr->lladdr);

      }

#endif/*UIP_CONF_IPV6_QUEUE_PKT*/

}

 

至此,网络层的数据包就已经完全生成了。

其流程图如下:


 

 

 

二、IP层传输


下面看发包函数tcpip_output /core/net/tcpip.c

u8_ttcpip_output(void)

{

 if(outputfunc != NULL) {

   return    outputfunc();

  }

  return0;

}

这里需要说明下,在main函数中启动了进程 uip_fw_procssoutputfunc = uip_fw_output

PROCESS_THREAD(uip_fw_process,ev, data)   //  /core/net/uip_fw_drv.c

{

 PROCESS_BEGIN();

  tcpip_set_outputfunc(uip_fw_output);    outputfunc =uip_fw_output

 PROCESS_WAIT_UNTIL(ev == PROCESS_EVENT_EXIT);

 PROCESS_END();

}

 

 

下面进入 uip_fw_output

u8_tuip_fw_output(void)      \core\net\uip-fw.c

{

  structuip_fw_netif *netif;

 if(uip_len == 0) {

   return UIP_FW_ZEROLEN;

 } 

  netif= find_netif();

 if(netif == NULL) {

   return UIP_FW_NOROUTE;

  }

  returnnetif->output();

}

该函数中先调用find_netif() 通过查路由表找到合适的网卡,然后调用该网卡的output函数。这就是IP层数据传输。

 


三、Mesh-under路由


我们的网卡在我们的网卡在最开始的main函数中就定义过:SmeshLink\Platform\Mx2xxcc\contiki-mx2xxcc-main.c

static struct uip_fw_netif meshif ={UIP_FW_NETIF(172,16,0,0, 255,255,0,0, uip_over_mesh_send)};

所以,假设找到的网卡就是meshif,那么接下来就要调用uip_over_mesh_send函数。

 

uint8_t uip_over_mesh_send(void)   \core\net\Uip-over-mesh.c

{

  // uip_len = hc_compress(&uip_buf[UIP_LLH_LEN], uip_len);

 //看不懂用了什么压缩方式。这里不是用来报头压缩,别被忽悠了,其作用待研究。

 

rt =route_lookup(&receiver);

  if(rt == NULL) {

。。。。。。

}else {

    route_decay(rt);

    send_data(&rt->nexthop);     //发数据到下一跳。

  }

 

接着进入send_data(&rt->nexthop)函数,看看6Lowpan是如何实现打包的。

static void  send_data(rimeaddr_t *next)     \core\net\Uip-over-mesh.c

{

  PRINTF("uip-over-mesh: %d.%d: send_datawith len %d\n",rimeaddr_node_addr.u8[0], rimeaddr_node_addr.u8[1],packetbuf_totlen());

  unicast_send(&dataconn, next);  //单播

}

 

再进入unicast_send(&dataconn, next)       

Int unicast_send(structunicast_conn *c, const rimeaddr_t *receiver)  \core\net\rime Unicast.c

{

  return broadcast_send(&c->c);

}

 

Int broadcast_send(structbroadcast_conn *c)         \core\net\rime Brocast.c

{

  packetbuf_set_addr(PACKETBUF_ADDR_SENDER,&rimeaddr_node_addr);

  return abc_send(&c->c);

}

Int  abc_send(struct abc_conn *c)           \core\net\rime  Abc.c

{

  return rime_output(&c->channel);

}

 

Int   rime_output(struct channel *c)    \core\net\rime  Rime.c

{

RIMESTATS_ADD(tx);

  if(chameleon_create(c)) {

    packetbuf_compact();

  NETSTACK_MAC.send(packet_sent,c);

// NETSTACK_MAC =nullmac_driver     core\net\mac\Nullmac.c

//进入packet_sent函数有三种选择,有一种在\core\net\Sicslowpan.c中,该函数实现了报头压缩。

    return 1;

  }  }

 

 

四、6LowPan适配层



static uint8_t  output(uip_lladdr_t *localdest)  //报头压缩函数调用实体 \core\net\Sicslowpan.c

{

。。。。。。//报头压缩  支持分片。

send_packet(&dest);     //发送压缩的报文。

}

 

static void   send_packet(rimeaddr_t *dest)  \core\net\Sicslowpan.c

{

NETSTACK_MAC.send(&packet_sent,NULL);   //据此进入Nullmac.C发送数据包

}

 

static void  packet_sent(void *ptr, int status,int transmissions)  \core\net\Sicslowpan.c

{

#ifSICSLOWPAN_CONF_NEIGHBOR_INFO

  neighbor_info_packet_sent(status,transmissions);

#endif /*SICSLOWPAN_CONF_NEIGHBOR_INFO */

  if(callback != NULL) {

    callback->output_callback(status); //callback  回调函数输出数据

  }

  last_tx_status = status;

}

以上三个函数是6Lowpan适配层的接口函数。根据static void   send_packet(rimeaddr_t *dest) 函数的NETSTACK_MAC.send(&packet_sent, NULL);  已知NETSTACK_MAC=Nullmac  因此进入Nullmac.c文件的packet_sent函数,也就是进入了802.15.4 MAC层了。

 

 

 

五:IEEE 802.15.4  MAC PHY



进入nullmac 的发送函数

static void  send_packet(mac_callback_t sent, void *ptr)   \core\net\Mac\nullmac.c

 

{

NETSTACK_RDC.send(sent,ptr);  // NETSTACK_RDC sicslowmac_driver \platform\mxusbstick\Contiki-conf.h

}

 

 

static void   send_packet(mac_callback_t sent, void *ptr)

{

if(packetbuf_hdralloc(len))

{

frame802154_create(&params,packetbuf_hdrptr(), len);

ret = NETSTACK_RADIO.send(packetbuf_hdrptr(),packetbuf_totlen()); // NETSTACK_RADIO  rf2xx_driver

}

NETSTACK_RADIO 网络协议的射频层使用rf2xx_driver 参见\platform\mxusbstick\Contiki-conf.h

至此进入NETSTACK_RADIO所指带的rf2xx_driver ,在该射频芯片下查找send 函数。

 

\cpu\AVR\Radio\rf230bb\radio.c 里面包含了rf2xx_driver 所有函数。

static int

rf230_send(const void *payload, unsigned short payload_len)

{

       ret =rf230_transmit(payload_len);

}

 

至此物理层数据发送完毕。


参考文献:http://blog.chinaunix.net/uid-23953763-id-3043683.html (contiki 中数据包的发送流程 )


 

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

contiki学习笔记-UDP-Client原厂代码分析

接触contiki一段时间了,但是苦于资料比较少,而且基本上都是入门级别的资料,所以xue

contiki学习笔记-udp-server.c文件详细的解析

上文中已经将大部分的复杂的用到的函数给解释了一下。刚刚看了一下

uIP的udp实现

#ifndef __UDPDEMO_H__ #define __UDPDEMO_H__ //定义应用程序回调函数 #ifndef UIP_APPCALL #define UIP_AP...

调试cc2530dk/example/udp-ipv6

调试cc2530dk/example/udp-ipv6; 环境:IAR 硬件:cc2530   1,先打开uipopt.h中的关于IPv6的宏定义; #ifndef UIP_CONF_IPV...

TCP以及UDP数据包发送程序

  • 2011-08-16 12:33
  • 111KB
  • 下载

Linux C raw socket 发送ipv4下的简单udp数据包

问题描述: 在linux下使用C语言和raw socket来简单实现ipv4下的udp数据包的发送 参考资源: http://www.pdbuchan.com/rawsock/rawsock.h...

服务器端发送UDP数据包客户端未收到问题解决

编程不仅是个技术活,也是个细心活,编程过程中一定要仔细
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)