本文讲述Rime协议栈单跳单播的连接建立过程,即rucb_open --> runicast_open --> stunicast_open --> unicast_open --> broadcast_open --> abc_open --> channel_open。
1. 概述
1.1 讨论范围
使用Rime协议栈进行通信,需要先建立连接。Rime协议栈提供单跳单播、单跳广播、多跳三种功能,本文仅介绍单跳单播(Single-hop unicast)连接建立过程,如下图红色箭头所示:
图1 Rime协议栈系统框图
图1蓝色下划线suc
、stuc
是两篇不同论文的名称,事实上是同一个意思。因源代码用的也是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。
图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_tx
、rxmit
、max_rxmit
是结构体runicast_conn
的成员变量,均初始化为0,关于is_tx
、rxmit
、max_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]