Rime协议栈所有通信都是通过通道channel标识的,本文介绍通道channel结构体及相关函数,包括channel_init
、channel_open
、channel_close
、channel_lookup
、channel_set_attributes
。
PS:channel有通道、信道、渠道、频道等含义,本文将其翻译为通道,理由是Rime中channel是逻辑上的,并不是真实的信道(信道指信号的传输媒质)。
1. 概述
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
数组):
图1 channel链表示意图
channel链表示意图源文件:Rime协议栈通道channel.vsd。
结构体channel各成员变量含义如下:
next
指向下一个channel,最后一个channel的next
指向空。
channelno
用两字节标识不同的channel。
attrlist
指向由类型type和长度len两个成员变量组成的结构体数组packetbuf_attrlist[]
。
hdrsize
hdrsize
是指数据报的头部大小。即为packetbuf_attrlist
类型的数组attrlist[]
中所有结构体中成员变量的长度len
的总和。详情见2.7。
2. 相关操作
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
的总和。