摘要:
本文讲述Rime协议栈单跳单播的连接建立过程,即rucb_open --> runicast_open --> stunicast_open --> unicast_open --> broadcast_open --> abc_open --> channel_open。
一、概述
1.1 讨论范围
使用Rime协议栈进行通信,需要先建立连接。Rime协议栈提供单跳单播、单跳广播、多跳三种功能,本文仅介绍单跳单播(Single-hop unicast)连接建立过程,如下图红色箭头所示:
图1 Rime协议栈系统框图
图1蓝色下划线suc、stuc是两篇不同论文的名称,事实上是同一个意思。因源代码用的也是stuc,故在以后讨论使用stuc。关于图1各名词解释,详见博文 Rime 协议栈概述及学习资料。
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对应关系.rar
图2 open、coon、callbacks对应关系
为了便于叙述,将连接结构体_conn、回调结构体_callbacks分别单列成文,阅读本文后续内容,请结合如下博文:
博文《Contiki学习笔记:Rime协议栈单跳单播建立连接之结构体rucb_conn》
博文《Contiki学习笔记:Rime协议栈单跳单播建立连接之callbacks》
事实上,整个连接过程,就是设置相应的连接结构体成员变量,所以建立先阅读下博文Rime 协议栈单跳单播建立连接之结构体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建立连接。
二、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。
三、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介绍见博文 Rime 协议栈通道 channel。这里的参数attributes是数组指针,具体展开比较繁琐,遂单独成文 Rime 协议栈单跳单播头部。
c->u = u
结构体runicast_callbacks有3个函数指针成员变量接收recv、发送sent、超时timedout,分别映射到接收recv、确认acked、超时timedout函数(在contiki/core/net/rime/rucb.c实现),详情见博文Rime 协议栈单跳单播建立连接之callbacks。
is_tx、rxmit、max_rxmit
is_tx、rxmit、max_rxmit是结构体runicast_conn的成员变量,均初始化为0,关于is_tx、rxmit、max_rxmit介绍见博文Rime 协议栈单跳单播建立连接之结构体rucb_conn。
四、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实现),详情见博文Rime 协议栈单跳单播建立连接之callbacks。
五、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实现),详情见博文Rime 协议栈单跳单播建立连接之callbacks。
channel_set_attributes
设置通道channel属性,关于结构体通道channel及函数channel_set_attributes介绍见博文Rime 协议栈通道 channel。这里的参数attributes是数组指针,具体展开比较繁琐,遂单独成文Rime 协议栈单跳单播头部。
六、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实现),详情见博文Rime 协议栈单跳单播建立连接之callbacks。
channel_set_attributes
设置通道channel属性,关于结构体通道channel及函数channel_set_attributes介绍见博文Rime 协议栈通道 channel。这里的参数attributes是数组指针,具体展开比较繁琐,遂单独成文Rime 协议栈单跳单播头部。
七、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实现),详情见博文Rime 协议栈单跳单播建立连接之callbacks。
channel_set_attributes
设置通道channel属性,关于结构体通道channel及函数channel_set_attributes介绍见博文Rime 协议栈通道 channel。这里的参数attributes是数组指针,具体展开比较繁琐,遂单独成文Rime 协议栈单跳单播头部。
八、channel_open
channel_open用于打开一个通道,实际上是设置通道号并将通道c加到channel链表中,详情见博文Rime 协议栈通道 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] 博文Rime 协议栈概述及学习资料
[2] Adam Dunkels,Fredrik Osterlind,Zhitao He. An Adaptive Communication Architecture for Wireless Sensor Networks[J]