// 新坑–contiki
rime简介
rime类似于TCP/IP协议栈,属于分层结构。图片来自Adam Dunkels本人的rime论文介绍。
-
abc为anonymous broadcast,匿名广播。即将数据包通过无线射频驱动(radio driver)发出去,接收来自无线射频驱动所有的包并交给上层。
-
ibc为identified sender best-effort broadcast,将下层传来的数据包添加一个发送者身份头部——使用数组形式。
-
uc为unicast abstraction,将上层的数据包添加一个接收者头部。
-
suc为Stubborn transmission。可靠通信就由这两层实现:Stubborn transmission、Reliable transmission。
Stubborn transmission层在给定的时间间隔、最大重发次数后不断地重发数据包。
Reliable communication层主要实现确认和序列功能,确认来自Stubborn transmission层的数据。 -
Bulk transfer为块传输层。该层有实现了单跳单播、单跳广播、多跳的包。
两个节点之间的通信图:
数据包格式
数据包格式如下:
数据包头部header
rime协议为了解决不同网络协议头部不兼容的问题,设计出了无头部数据包。
见packetbuf.h,该文件定义了数据包头部的基本格式。
struct packetbuf_attrlist
{
uint8_t type;
uint8_t len;
};
数据包数据data
Rime缓冲区存放数据包属性(header)和应用数据(data)。大小是这两部分的和,Rime缓冲区由静态全局变量packetbuf定义。
static uint16_t packetbuf_aligned[(PACKETBUF_SIZE + PACKETBUF_HDR_SIZE)/2+1]; // PACKETBUF_SIZE默认大小是128字节
static uint8_t *packetbuf = (uint8_t*)packetbuf_aligned; // 确保了即使在16位边界 (如MSP430) 也能对齐
数据包操作API
源自packetbuf.h,用于操作数据缓存
// Clear and reset the packetbuf
void packetbuf_clear(void);
// Get a pointer to the data in the packetbuf
void *packetbuf_dataptr(void);
// Get a pointer to the header in the packetbuf, for outbound packets
void *packetbuf_hdrptr(void);
// Get the length of the header in the packetbuf
uint8_t packetbuf_hdrlen(void);
// Get the length of the data in the packetbuf
uint16_t packetbuf_datalen(void);
// Get the total length of the header and data in the packetbuf
uint16_t packetbuf_totlen(void);
// Get the total length of the remaining space in the packetbuf
uint16_t packetbuf_remaininglen(void);
// Set the length of the data in the packetbuf
void packetbuf_set_datalen(uint16_t len);
// Compact the packetbuf
void packetbuf_compact(void);
// Copy from external data into the packetbuf
int packetbuf_copyfrom(const void *from, uint16_t len);
// Copy the entire packetbuf to an external buffer
int packetbuf_copyto(void *to);
// Extend the header of the packetbuf, for outbound packets
int packetbuf_hdralloc(int size);
// Reduce the header in the packetbuf, for incoming packets
int packetbuf_hdrreduce(int size);
rucb解析
此章仅单独研究rubc单跳单播协议。
建立连接
依次调用rucb_open() --> runicast_open() --> stunicast_open() --> unicast_open() --> broadcast_open() --> abc_open() --> channel_open()
发送数据包
struct rucb_conn { // rucb_conn为rubc协议的存储连接信息的结构体
struct runicast_conn c; // 链式结构
const struct rucb_callbacks *u; // 回调函数,每一层的连接信息结构体都有对应的callback
linkaddr_t receiver, sender; // 接收器、发送器
uint16_t chunk; // 通道序号,只有0和1两个值。当前为0,next为1
uint8_t last_seqno;
int last_size;
};
int rucb_send(struct rucb_conn *c, const linkaddr_t *receiver)
{
c->chunk = 0; // chunk置为0,表示当前正在运行
read_data(c); // 调用read_chunk()
linkaddr_copy(&c->receiver, receiver); // 调用memcpy(dest, src),完善c属性
linkaddr_copy(&c->sender, &linkaddr_node_addr);
runicast_send(&c->c, receiver, MAX_TRANSMISSIONS); // 调用runicast的send方法
return 0;
}
int runicast_send(struct runicast_conn *c, const linkaddr_t *receiver, uint8_t max_retransmissions)
{
...
packetbuf_set_attr(PACKETBUF_ATTR_RELIABLE, 1); // packetbuf_attrs[PACKETBUF_ATTR_RELIABLE].val = 1;
packetbuf_set_attr(PACKETBUF_ATTR_PACKET_TYPE, PACKETBUF_ATTR_PACKET_TYPE_DATA);
packetbuf_set_attr(PACKETBUF_ATTR_PACKET_ID, c->sndnxt);
packetbuf_set_attr(PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS, 3);
...
stunicast_send_stubborn(&c->c, receiver, REXMIT_TIME);
...
}
后面同理,一边向下层传递,一边设置头数组。最后由链路层调用send()方法发送
stunicast_send_stubborn() --> unicast_send() --> broadcast_send() --> abc_send() --> rime_output() --> NETSTACK_LLSEC.send()
最后完整的数据包头数组格式如下:
头数组type类型为enum:
enum
{
PACKETBUF_ATTR_NONE,
/* Scope 0 attributes: used only on the local node. */
PACKETBUF_ATTR_CHANNEL,
PACKETBUF_ATTR_NETWORK_ID,
PACKETBUF_ATTR_LINK_QUALITY,
PACKETBUF_ATTR_RSSI,
PACKETBUF_ATTR_TIMESTAMP,
PACKETBUF_ATTR_RADIO_TXPOWER,
PACKETBUF_ATTR_LISTEN_TIME,
PACKETBUF_ATTR_TRANSMIT_TIME,
PACKETBUF_ATTR_MAX_MAC_TRANSMISSIONS,
PACKETBUF_ATTR_MAC_SEQNO,
PACKETBUF_ATTR_MAC_ACK,
/* Scope 1 attributes: used between two neighbors only. */
PACKETBUF_ATTR_RELIABLE,
PACKETBUF_ATTR_PACKET_ID,
PACKETBUF_ATTR_PACKET_TYPE,
PACKETBUF_ATTR_REXMIT,
PACKETBUF_ATTR_MAX_REXMIT,
PACKETBUF_ATTR_NUM_REXMIT,
PACKETBUF_ATTR_PENDING,
/* Scope 2 attributes: used between end-to-end nodes. */
PACKETBUF_ATTR_HOPS,
PACKETBUF_ATTR_TTL,
PACKETBUF_ATTR_EPACKET_ID,
PACKETBUF_ATTR_EPACKET_TYPE,
PACKETBUF_ATTR_ERELIABLE,
/* These must be last */
PACKETBUF_ADDR_SENDER,
PACKETBUF_ADDR_RECEIVER,
PACKETBUF_ADDR_ESENDER,
PACKETBUF_ADDR_ERECEIVER,
PACKETBUF_ATTR_MAX
};
接收数据包
相同方法,不做分析
callback
就像在发送数据中说的,callback作为每层连接结构体conn必有的成员变量回调结构体指向上一层的回调结构体,即接收到数据总是提交给上一层处理。
例如,在runicast_callbacks结构体中,分别映射到接收recv、确认acked、超时timedout函数(位于rubc.c)
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);
};