本文讲述Rime协议栈单跳单播的连接建立过程,即rucb_open --> runicast_open --> stunicast_open --> unicast_open --> broadcast_open --> abc_open --> channel_open。

1. 概述

1.1 讨论范围

使用Rime协议栈进行通信,需要先建立连接。Rime协议栈提供单跳单播、单跳广播、多跳三种功能,本文仅介绍单跳单播(Single-hop unicast)连接建立过程,如下图红色箭头所示:

img

图1 Rime协议栈系统框图

图1蓝色下划线sucstuc是两篇不同论文的名称,事实上是同一个意思。因源代码用的也是stuc,故在以后讨论使用stuc。关于图1各名词解释,详见博文《概述及学习资料》。

1.2 连接建立概述

Rime是层次型协议栈,整个连接过程是通过上层调用下层服务来完成的,具体如下:

rucb_open --> runicast_open --> stunicast_open --> unicast_open --> broadcast_open --> abc_open --> channel_open

1.3 几点说明

使用Rime协议栈进行通信之前,需要建立连接,也就需要保存该连接一些信息(如发送者、接收者),Rime协议栈用一系列结构体保存这些链接状态信息。Rime每一层都有相应的连接结构体(以_conn结尾),上层嵌套下层,如下:

rucb_conn --> runicast_conn --> stunicast_conn --> unicast_conn --> broadcast_conn --> abc_conn

每个连接结构体都有相应的回调结构体(以_callbacks后缀结尾),该结构体的成员变量实为发送、接收函数指针。当接收到一个数据报,会调用该结构体相应的函数。回调结构体层次如下:

rucb_callbacks --> runicast_callbacks --> stunicast_callbacks --> unicast_callbacks --> broadcast_callbacks --> abc_callbacks

综上,连接建立_open、连接结构体_conn、回调结构体_callbacks间的关系如下图:

open、coon、callbacks对应关系源文件:open、coon、callbacks对应关系.vsd

img

图2 open、coon、callbacks对应关系

为了便于叙述,将连接结构体_conn、回调结构体_callbacks分别单列成文,阅读本文后续内容,请结合如下博文:

事实上,整个连接过程,就是设置相应的连接结构体成员变量,所以建立先阅读下博文《结构体rucb_conn》。

1.4 一个实例

文件contiki/examples/rime/example-rucb.c给出一个使用rucb实例,建立连接部分源代码如下:

static struct rucb_conn rucb;
const static struct rucb_callbacks rucb_call =
{
  write_chunk,
  read_chunk,
  NULL
};
rucb_open(&rucb, 137, &rucb_call);

定义连接结构体rucb,实现回调结构体中的函数并定义回调结构体rucb_call,指定一个通道号调用rucb_open建立连接。

2. rucb_open

rucb是块传输(Bulk transfer)层[1],可以理解成传输层,rucb_open源代码如下:

void rucb_open(struct rucb_conn *c, uint16_t channel, const struct rucb_callbacks *u)
{
  rimeaddr_copy(&c->sender, &rimeaddr_null);
  runicast_open(&c->c, channel, &ruc);
  c->u = u;
  c->last_seqno = - 1;
}

rimeaddr_copy

将Rime地址rimeaddr_null拷贝到sender,即将sender设为0.0(当Rime地址长度为2)。关于Rime地址rimeaddr_t及函数rimeaddr_copy相关介绍见博文《Rime地址rimeaddr_t》。

runicast_open

见第三部分。

c->u = u

结构体rucb_callbacks有3个函数指针成员变量写数据块write_chunk、读数据块read_chunk、超时timedout,这三个函数由用户自己实现,建立连接时作为参数传给rucb_open(见1.4例子)。

c->last_seqno = -1

一次数据发送多个片段的最后一个序列号,当接收端接收到数据时,判断其序列号是否等于最后一个序列号,若等于则不接收(即接收到最后一个数据块,停止接收)。last_seqno初始化为-1。

3. runicast_open

ruc是可靠通信的另一层,该层主要实现确认和序列功能(acknowledgments and sequencing)[1],runicast_open源代码如下:

//static const struct runicast_callbacks ruc = {recv, acked, timedout};
//runicast_open(&c->c, channel, &ruc);

void runicast_open(struct runicast_conn *c, uint16_t channel, const struct runicast_callbacks *u)
{
  stunicast_open(&c->c, channel, &runicast);
  channel_set_attributes(channel, attributes);
  c->u = u;
  c->is_tx = 0;
  c->rxmit = 0;
  c->sndnxt = 0;
}

stunicast_open

见第四部分。

channel_set_attributes

设置通道channel属性,关于结构体通道channel及函数channel_set_attributes介绍见博文《通道channel》。这里的参数attributes是数组指针,具体展开比较繁琐,遂单独成文《单跳单播头部》。

c->u = u

结构体runicast_callbacks有3个函数指针成员变量接收recv、发送sent、超时timedout,分别映射到接收recv、确认acked、超时timedout函数(在contiki/core/net/rime/rucb.c实现),详情见博文《单跳单播建立连接之callbacks》。

is_tx、rxmit、max_rxmit

is_txrxmitmax_rxmit是结构体runicast_conn的成员变量,均初始化为0,关于is_txrxmitmax_rxmit介绍见博文《结构体rucb_conn》。

4. stunicast_open

stuc是可靠通信的另一层,该层在给定的时间间隔不断地重发数据包,直到上层让其停止。为了防止无限重发,需要指定最大重发次数(maximum retransmission number)[1],stunicast_open源代码如下:

//static const struct stunicast_callbacks runicast = {recv_from_stunicast,sent_by_stunicast};
//stunicast_open(&c->c, channel, &runicast);

void stunicast_open(struct stunicast_conn *c, uint16_t channel, const struct stunicast_callbacks *u)
{
  unicast_open(&c->c, channel, &stunicast);
  c->u = u;
}

unicast_open

见本文第五部分。

c->u = u

结构体stunicast_callbacks有2个函数指针成员变量接收recv、发送sent,分别映射到接收recv_from_stunicast、发送sent_by_stunicast函数(在contiki/core/net/rime/runicast.c实现),详情见博文《单跳单播建立连接之callbacks》。

5. unicast_open

uc(unicast abstraction)将上层的数据包添加一个接收者头部(adds a receiver header field)[1],unicast_open源代码如下:

//static const struct unicast_callbacks stunicast = {recv_from_uc, sent_by_uc};
//unicast_open(&c->c, channel, &stunicast);

void unicast_open(struct unicast_conn *c, uint16_t channel, const struct unicast_callbacks *u)
{
  broadcast_open(&c->c, channel, &uc);
  c->u = u;
  channel_set_attributes(channel, attributes);
}

broadcast_open

见本文第六部分。

c->u = u

结构体unicast_callbacks有2个函数指针成员变量接收recv、发送sent,分别映射到接收recv_from_uc、发送sent_by_uc函数(在contiki/core/net/rime/stunicast.c实现),详情见博文《单跳单播建立连接之callbacks》。

channel_set_attributes

设置通道channel属性,关于结构体通道channel及函数channel_set_attributes介绍见博文《通道channel》。这里的参数attributes是数组指针,具体展开比较繁琐,遂单独成文《单跳单播头部》。

6. broadcast_open

ibc(identified sender best-effort broadcast)将上层的数据包添加一个发送者身份(sender identity)头部[1],broadcast_open源代码如下:

//static const struct broadcast_callbacks uc = {recv_from_broadcast, sent_by_broadcast};
//broadcast_open(&c->c, channel, &uc);

void broadcast_open(struct broadcast_conn *c, uint16_t channel, const struct broadcast_callbacks *u)
{
  abc_open(&c->c, channel, &broadcast);
  c->u = u;
  channel_set_attributes(channel, attributes);
}

abc_open

见本文第七部分。

c->u = u

结构体broadcast_callbacks有2个函数指针成员变量接收recv、发送sent,分别映射到接收recv_from_broadcast、发送sent_by_broadcast函数(在contiki/core/net/rime/unicast.c实现),详情见博文《单跳单播建立连接之callbacks》。

channel_set_attributes

设置通道channel属性,关于结构体通道channel及函数channel_set_attributes介绍见博文《通道channel》。这里的参数attributes是数组指针,具体展开比较繁琐,遂单独成文《单跳单播头部》。

7. abc_open

abc(anonymous broadcast,匿名广播)将数据包通过无线射频驱动(radio driver)发出去,接收来自无线射频驱动所有的包并交给上层[1],abc_open源码如下:

//static const struct abc_callbacks broadcast = {recv_from_abc, sent_by_abc};
//abc_open(&c->c, channel, &broadcast);

void abc_open(struct abc_conn *c, uint16_t channelno, const struct abc_callbacks *callbacks)
{
  channel_open(&c->channel, channelno);
  c->u = callbacks;
  channel_set_attributes(channelno, attributes);
}

channel_open

见本文第八部分。

c->u = callbacks

结构体abc_callbacks有2个函数指针成员变量接收recv、发送sent,分别映射到接收recv_from_abc、发送sent_by_abc函数(在contiki/core/net/rime/broadcast.c实现),详情见博文《单跳单播建立连接之callbacks》。

channel_set_attributes

设置通道channel属性,关于结构体通道channel及函数channel_set_attributes介绍见博文《通道channel》。这里的参数attributes是数组指针,具体展开比较繁琐,遂单独成文《单跳单播头部》。

8. channel_open

channel_open用于打开一个通道,实际上是设置通道号并将通道c加到channel链表中,详情见博文《通道channel》,channel_open源代码如下:

//channel_open(&c->channel, channelno);

void channel_open(struct channel *c, uint16_t channelno)
{
  c->channelno = channelno;
  list_add(channel_list, c);
}

至此,单跳单播连接整个过程介绍完毕:-)

参考资料:

[1] 博文《概述及学习资料

[2] Adam Dunkels,Fredrik Osterlind,Zhitao He. An Adaptive Communication Architecture for Wireless Sensor Networks[J]

本文系Spark & Shine原创,转载需注明出处本文最近一次修改时间 2022-03-22 18:29

results matching ""

    No results matching ""