摘要:
Rime 协议栈所有通信都是通过通道channel标识的,本文介绍通道channel结构体及相关函数,包括channel_init、channel_open、channel_close、channel_lookup、channel_set_attributes。
PS:channel有通道、信道、渠道、频道等含义,本文将其翻译为通道,理由是Rime中channel是逻辑上的,并不是真实的信道(信道指信号的传输媒质)。
一、概述
Rime协议栈所有通信都是通过通道channel标识的,即两个应用进程通信需要相同的channel。想想套接字编程,两个进程通信具有相同的端口号。
channel由两字节标识,小于128保留给系统使用(如shell、系统应用程序)。系统所有channel连成一个链表,channel结构体源码如下:
-
struct channel
-
{
-
struct channel *next;
-
uint16_t channelno;
-
const struct packetbuf_attrlist *attrlist;
-
uint8_t hdrsize;
-
};
-
-
struct packetbuf_attrlist
-
{
-
uint8_t type;
-
uint8_t len;
-
};
其示意图如下(值得注意的是attrlist指向的是packetbuf_attrlist数组):
channel链表示意图源文件 Rime协议栈通道channel.rar
图1 channel链表示意图
结构体channel各成员变量含义如下:
next
指向下一个channel,最后一个channel的next指向空。
channelno
用两字节标识不同的channel。
attrlist
指向由类型type和长度len两个成员变量组成的结构体数组packetbuf_attrlist[]。
hdrsize
hdrsize是指数据报的头部大小。即为packetbuf_attrlist类型的数组attrlist[]中所有结构体中成员变量的长度len的总和。详情见2.7。
二、相关操作
2.1 声明链表channel_list
宏LIST(name)用来声明一个结构体类型的链表,并且该结构体的第一个成员变量必须是指针,宏LIST用该指针形成链表。除此之外,该链表被声明为静态变量(不用导出符号,从而方便其他模块使用)。部分源码如下:
-
//LIST(channel_list);
-
#define LIST(name) \
-
static void *LIST_CONCAT(name,_list) = NULL; \
-
static list_t name = (list_t)&LIST_CONCAT(name,_list)
-
-
typedef void ** list_t;
LIST_CONCAT宏用于连结两个字符串,源码如下:
-
#define LIST_CONCAT(s1, s2) LIST_CONCAT2(s1, s2)
-
#define LIST_CONCAT2(s1, s2) s1##s2
在C语言中,##表示连结(concatenate),#表示字符串化。那么,把上述语句翻译下,就是这样了:
-
LIST(channel_list);
-
static void *channel_list_list = NULL;
-
static list_t channel_list = (list_t) &channel_list_list;
这里涉及到指针的指针,channel_list是指向channel_list_list的指针,channel_list_list指向NULL指针。见下图:
图2 channel_list示意图
2.2 channel初始化channel_init
LIST(channel_list)只是声明了链表,使用之前需先初始化,即channel_init。结合图2可知,channel_init实际上是将channel_list指向的值设为NULL(不再是channel_list_list)。源代码如下:
-
void channel_init(void)
-
{
-
list_init(channel_list);
-
}
-
-
void list_init(list_t list)
-
{
-
*list = NULL;
-
}
2.3 channel_open
channel_open用于打开一个通道,实际上设置通道号并将通道c加到channel链表中,源代码如下:
-
void channel_open(struct channel *c, uint16_t channelno)
-
{
-
c->channelno = channelno;
-
list_add(channel_list, c);
-
}
list_add将节点加入到链表末尾,list_add源代码如下:
-
void list_add(list_t list, void *item)
-
{
-
struct list *l;
-
list_remove(list, item); /* Make sure not to add the same element twice */
-
-
((struct list*)item)->next = NULL;
-
-
l = list_tail(list);
-
if(l == NULL)
-
{
-
*list = item;
-
}
-
else
-
{
-
l->next = item;
-
}
-
}
2.4 channel_close
channel_close用于关闭一个通道,即从链表删除相应的channel。channel_close源代码如下:
-
void channel_close(struct channel *c)
-
{
-
list_remove(channel_list, c);
-
}
list_remove用于从链表移除指定的元素,源代码如下:
-
void list_remove(list_t list, void *item)
-
{
-
struct list *l, *r;
-
-
if(*list == NULL)
-
{
-
return ;
-
}
-
-
r = NULL;
-
for(l = *list; l != NULL; l = l->next)
-
{
-
if(l == item)
-
{
-
if(r == NULL)
-
{
-
*list = l->next; /* First on list */
-
}
-
else
-
{
-
r->next = l->next; /* Not first on list */
-
}
-
l->next = NULL;
-
return ;
-
}
-
r = l;
-
}
-
}
2.5 channel_lookup
查找通道号对应的channel,源代码如下:
-
struct channel *channel_lookup(uint16_t channelno)
-
{
-
struct channel *c;
-
for(c = list_head(channel_list); c != NULL; c = list_item_next(c))
-
{
-
if(c->channelno == channelno)
-
{
-
return c;
-
}
-
}
-
return NULL;
-
}
list_head用于返回链表的第一个元素,list_item_next用于返回当前元素的下一个元素,源代码如下:
-
void *list_head(list_t list)
-
{
-
return *list;
-
}
-
-
void *list_item_next(void *item)
-
{
-
return item == NULL ? NULL : ((struct list*)item)->next;
-
}
2.6 channel_set_attributes
channel_set_attributes用于设置通道的属性,首先用channel_lookup函数找到通道号对应的channel。而后设置attrlist、hdrsize成员变量。源代码如下:
-
void channel_set_attributes(uint16_t channelno, const struct packetbuf_attrlist attrlist[])
-
{
-
struct channel *c;
-
c = channel_lookup(channelno);
-
if(c != NULL)
-
{
-
c->attrlist = attrlist;
-
c->hdrsize = chameleon_hdrsize(attrlist);
-
}
-
}
chameleon_hdrsize用于计算channel的hdrsize,源代码如下:
-
//chameleon_hdrsize(attrlist);
-
int chameleon_hdrsize(const struct packetbuf_attrlist attrlist[])
-
{
-
return CHAMELEON_MODULE.hdrsize(attrlist);
-
}
CHAMELEON_MODULE由一系列条件编译定义,如下:
-
#ifndef CHAMELEON_MODULE
-
#ifdef CHAMELEON_CONF_MODULE
-
#define CHAMELEON_MODULE CHAMELEON_CONF_MODULE
-
#else
-
#define CHAMELEON_MODULE chameleon_bitopt
-
#endif
-
#endif
可见,可以自定义CHAMELEON_MODULE,即定义CHAMELEON_CONF_MODULE,默认情况是chameleon_bitopt,其源代码如下:
-
CC_CONST_FUNCTION struct chameleon_module chameleon_bitopt =
-
{
-
unpack_header, pack_header, header_size
-
};
chameleon_bitopt是chameleon_module结构体类型,源代码如下:
-
struct chameleon_module
-
{
-
struct channel *(*input)(void);
-
int(*output)(struct channel*);
-
int(*hdrsize)(const struct packetbuf_attrlist*);
-
};
可见,chameleon_module结构体包含3个函数指针,函数unpack_header、pack_header、header_size在core/net/rime/chameleon-bitopt.c实现,这里先不介绍,待用到再介绍。
2.7 hdrsize计算
透过2.6的分析,chameleon_hdrsize实际上是调用header_size函数。去除一些无关的注释,header_size源码如下:
-
//chameleon_hdrsize(const struct packetbuf_attrlist attrlist[])
-
static int header_size(const struct packetbuf_attrlist *a)
-
{
-
int size, len;
-
size = 0;
-
-
for(; a->type != PACKETBUF_ATTR_NONE; ++a)
-
{
-
#if CHAMELEON_WITH_MAC_LINK_ADDRESSES
-
if(a->type == PACKETBUF_ADDR_SENDER || a->type == PACKETBUF_ADDR_RECEIVER)
-
{
-
continue;
-
}
-
#endif
-
-
len = a->len;
-
size += len;
-
}
-
return size;
-
}
显然,channel的hdrsize为packetbuf_attrlist类型的数组attrlist[]中所有结构体中成员变量的长度len的总和。