Chinaunix首页 | 论坛 | 博客
  • 博客访问: 70847
  • 博文数量: 11
  • 博客积分: 210
  • 博客等级: 入伍新兵
  • 技术积分: 94
  • 用 户 组: 普通用户
  • 注册时间: 2010-06-19 18:13
文章分类
文章存档

2013年(4)

2012年(2)

2011年(5)

分类: 嵌入式

2011-12-15 19:54:11

二、 发包流程

下面讨论发送数据包的流程。发数据包的过程相对复杂。用到了回调机制。其过程可以分为:产生完整的数据包 发送数据包 两部分。下面分别说明。

doc/example-program.c 为例

PROCESS_THREAD(example_program_process, ev, data)

{

  static struct uip_udp_conn *c;

  PROCESS_BEGIN();

  c = udp_broadcast_new(UIP_HTONS(4321), NULL);  ------建立一个udp连接

  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();

}

该函数执行的事情是:周期性的广播发送hello

下面从tcpip_poll_udp(c) 开始

Void tcpip_poll_udp(struct uip_udp_conn *conn)

{

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

}

前面在收包时,我们已经看到过tcpip_process。当tcpip_process 收到消息后,进行的处理如下:

static void eventhandler(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);   // 产生数据

        if(uip_len > 0) {

          tcpip_output();            // 发送

        }

      }

      break;

  };

}

其中uip_udp_periodic_conn 用于产生数据包,tcpip_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)

  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的包头*/

goto ip_send_nolen;

ip_send_nolen:

/* 填充ip层包头 , 校验和等*/

Return;

  }

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

#define UIP_UDP_APPCALL   tcpip_uipcall

 

Void tcpip_uipcall(void)

{

  register uip_udp_appstate_t *ts;

  ts = &uip_udp_conn->appstate;

  if(ts->p != NULL) {

    process_post_synch(ts->p, tcpip_event, ts->state);

  }

}

通过process_post_synch(ts->p, tcpip_event, ts->state) 调回到udp连接相关联的进程,即我们最初的进程:example_program_process,向这个进程发送消息: tcpip_event

这是我们最初的进程,

PROCESS_THREAD(example_program_process, ev, data)

{

  static struct uip_udp_conn *c;

  PROCESS_BEGIN();

  c = udp_broadcast_new(UIP_HTONS(4321), NULL);  ------建立一个udp连接

  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()

Void uip_send(const void *data, int len)

{

    memcpy(uip_sappdata, (data), uip_slen);

}

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

至此,网络层的数据包就已经完全生成了。流程如下:


 

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

u8_t tcpip_output(void)

{

  if(outputfunc != NULL) {

    return outputfunc();

  }

  UIP_LOG("tcpip_output: Use tcpip_set_outputfunc() to set an output function");

  return 0;

}

下面转到outputfunc()。在main函数中启动了进程  uip_fw_procss

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

{

  PROCESS_BEGIN();

  tcpip_set_outputfunc(uip_fw_output);

  PROCESS_WAIT_UNTIL(ev == PROCESS_EVENT_EXIT);

  PROCESS_END();

}

outputfunc = uip_fw_output

下面进入 uip_fw_output

u8_t uip_fw_output(void)

{

  struct uip_fw_netif *netif;

  if(uip_len == 0) {

    return UIP_FW_ZEROLEN;

  } 

  netif = find_netif();

  if(netif == NULL) {

    return UIP_FW_NOROUTE;

  }

  return netif->output();

}

该函数中先调用find_netif() 通过查路由表找到合适的网卡,然后调用该网卡的output函数。

前面提到的,我们的网卡在最开始就定义过:

static struct uip_fw_netif  tr1001if =

  {UIP_FW_NETIF(0,0,0,0, 0,0,0,0, uip_driver_send)};

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


 

uint8_t uip_driver_send(void)

{

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

  packetbuf_copyfrom(&uip_buf[UIP_LLH_LEN], uip_len);

  NETSTACK_MAC.send(NULL, NULL);

}

进入nullmac 的发送函数

static void send_packet(mac_callback_t sent, void *ptr)

{

  NETSTACK_RDC.send(sent, ptr);

}

进入nullrdc的发送函数

static void send_packet(mac_callback_t sent, void *ptr)

{

  int ret;

  packetbuf_set_addr(PACKETBUF_ADDR_SENDER, &rimeaddr_node_addr);

   switch(NETSTACK_RADIO.send(packetbuf_hdrptr(), packetbuf_totlen())) {

    case RADIO_TX_OK:

      ret = MAC_TX_OK;

      break;

    case RADIO_TX_COLLISION:

      ret = MAC_TX_COLLISION;

      break;

    default:

      ret = MAC_TX_ERR;

      break;

    }

  }

  mac_call_sent_callback(sent, ptr, ret, 1);

}

进入tr1001_driver的发送函数

Int tr1001_send(const void *packet, unsigned short len)

{

….(复杂,需要认真研究)

}

至此,数据的发送过程结束。

(问题:MAC层头部是在哪里填充的?)


 

总结以上tcpip_output的流程如下:

 

 

 

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

a_jige2012-04-06 08:49:21

差不多。你看以顺着ipv6的看一下

orangewu2012-03-29 11:20:41

楼主,您好
你所述的发送流程指的是IPv4的吧
如果是IPv6的有什么不同吗