本文讲述了单跳单播连接建立的回调结构体(即设置连接结构体的接收发送函数),包括rucb_callbacks --> runicast_callbacks --> stunicast_callbacks --> unicast_callbacks --> broadcast_callbacks --> abc_callbacks。
PS:为了叙述方便,将连接建立的回调结构体单独成文,阅读时,请结合以下博文:
1. 概述
使用Rime协议栈进行通信,需要先建立连接,也就需要设置该连接一些信息。其中,很重要的一点就是要设置接收与发送函数,即填充连接结构体的回调函数。Rime是层次型协议栈,每一层都有对应的回调函数,从上层到下层的回调函数如下:
rucb_callbacks --> runicast_callbacks --> stunicast_callbacks --> unicast_callbacks --> broadcast_callbacks --> abc_callbacks
回调结构体与连接建立、连接结构体间对应关系如下图(visio源文件:open、coon、callbacks对应关系.vsd):
图1 open、coon、callbacks对应关系
2. rucb_callbacks
结构体rucb_callbacks
有3个函数指针成员变量写数据块write_chunk
、读数据块read_chunk
、超时timedout
(注:不是timeout,是timedout,指timed out),源代码如下:
struct rucb_callbacks
{
void(*write_chunk)(struct rucb_conn *c, int offset, int flag, char *data, int len);
int(*read_chunk)(struct rucb_conn *c, int offset, char *to, int maxsize);
void(*timedout)(struct rucb_conn *c);
};
这三个函数由用户自己实现,如,文件contiki/examples/rime/example-rucb.c
实现这些函数,这样就可以定义回调结构体rucb_call
,建立连接部分源代码如下:
static struct rucb_conn rucb; //1.定义连接结构体
/*****2.定义回调结构体*****/
const static struct rucb_callbacks rucb_call =
{
write_chunk,
read_chunk,
NULL
};
rucb_open(&rucb, 137, &rucb_call);//3.建立连接
3. runicast_callbacks
结构体runicast_callbacks
有3个函数指针成员变量接收recv
、发送sent
、超时timedout
,源代码如下:
struct runicast_callbacks
{
void(*recv)(struct runicast_conn *c, const rimeaddr_t *from, uint8_t seqno);
void(*sent)(struct runicast_conn *c, const rimeaddr_t *to, uint8_t retransmissions);
void(*timedout)(struct runicast_conn *c, const rimeaddr_t *to, uint8_t retransmissions);
};
这3个函数分别映射到接收recv
、确认acked
、超时timedout
函数(在contiki/core/net/rime/rucb.c
实现),ruc
是可靠通信的另一层,该层主要实现确认和序列功能(acknowledgments and sequencing)[1]。rucb_open
调用runicast_open
,部分源码如下:
/*****1.在contiki/core/net/rime/rucb.c实现******/
static const struct runicast_callbacks ruc =
{
recv,
acked,
timedout
};
runicast_open(&c->c, channel, &ruc);//2.rucb_open调用该函数
4. stunicast_callbacks
结构体stunicast_callbacks
有2个函数指针成员变量接收recv
、发送sent
,源代码如下:
struct stunicast_callbacks
{
void(*recv)(struct stunicast_conn *c, const rimeaddr_t *from);
void(*sent)(struct stunicast_conn *c, int status, int num_tx);
};
这2个函数分别映射到接收recv_from_stunicast
、发送sent_by_stunicast
函数(在contiki/core/net/rime/runicast.c
实现),stuc
是可靠通信的另一层,该层在给定的时间间隔不断地重发数据包,直到上层让其停止。为了防止无限重发,需要指定最大重发次数(maximum retransmission number)[1]。runicast_open
调用stunicast_open
,部分源码如下:
static const struct stunicast_callbacks runicast =
{
recv_from_stunicast,
sent_by_stunicast
};
stunicast_open(&c->c, channel, &runicast);
5. unicast_callbacks
结构体unicast_callbacks
有2个函数指针成员变量接收recv
、发送sent
,源代码如下:
struct unicast_callbacks
{
void(*recv)(struct unicast_conn *c, const rimeaddr_t *from);
void(*sent)(struct unicast_conn *ptr, int status, int num_tx);
};
这2个函数分别映射到接收recv_from_uc
、发送sent_by_uc
函数(在contiki/core/net/rime/stunicast.c
实现)。uc(unicast abstraction)将上层的数据包添加一个接收者头部(adds a receiver header field)[1]。stunicast_open
调用unicast_open
,部分源码如下:
static const struct unicast_callbacks stunicast =
{
recv_from_uc,
sent_by_uc
};
unicast_open(&c->c, channel, &stunicast);
6. broadcast_callbacks
结构体broadcast_callbacks
有2个函数指针成员变量接收recv
、发送sent
,源代码如下:
struct broadcast_callbacks
{
/** Called when a packet has been received by the broadcast module. */
void(*recv)(struct broadcast_conn *ptr, const rimeaddr_t *sender);
void(*sent)(struct broadcast_conn *ptr, int status, int num_tx);
};
这2个函数分别映射到接收recv_from_broadcast
、发送sent_by_broadcast
函数(在contiki/core/net/rime/unicast.c
实现)。ibc
(identified sender best-effort broadcast)将上层的数据包添加一个发送者身份(sender identity)头部[1]。unicast_open
调用broadcast_open
,部分源码如下:
static const struct broadcast_callbacks uc =
{
recv_from_broadcast,
sent_by_broadcast
};
broadcast_open(&c->c, channel, &uc);
7. abc_callbacks
结构体abc_callbacks
有2个函数指针成员变量接收recv
、发送sent
,源代码如下:
struct abc_callbacks
{
/** Called when a packet has been received by the abc module. */
void(*recv)(struct abc_conn *ptr);
void(*sent)(struct abc_conn *ptr, int status, int num_tx);
};
这2个函数分别映射到接收recv_from_abc
、发送sent_by_abc
函数(在contiki/core/net/rime/broadcast.c
实现)。abc
(anonymous broadcast,匿名广播)将数据包通过无线射频驱动(radio driver)发出去,接收来自无线射频驱动所有的包并交给上层。broadcast_open
调用abc_open
,部分源码如下:
static const struct abc_callbacks broadcast =
{
recv_from_abc,
sent_by_abc
};
abc_open(&c->c, channel, &broadcast);
8. 总结
使用Rime协议栈进行通信,需要先建立连接,也就需要设置该连接一些信息。其中,很重要的一点就是要设置接收与发送函数,即填充连接结构体的回调函数。 每一层连接结构体的成员变量回调结构体指针是指向上一层的回调结构体,这点很好理解,即接收到数据总是提交给上一层处理。
参考资料:
[1] 博文《概述及学习资料》