您的位置:首页 > 其它

Rime协议学习笔记:(十)polite广播

2017-01-03 23:02 656 查看

十.polite广播

   polite源码文件:contiki-3.0/core/net/rime/polite.[c/h]

  根据polite.h中的官方注释理解如下:

  polite模块在一个时间间隔内发送一个局域网广播数据包。如果在该时间间隔内从邻居节点接收到具有相同包头的数据包,则不发送该数据包。

  polite原语是根据polite gossip算法概括而来(该算法出自Levis等人,在2004年的NSDI会议)。polite gossip算法设计为不重传其它节点已经发送过的数据包,以此来减少总的数据包传输次数。polite广播原语的目的是为了避免在同一个时间间隔内本地邻居节点在指定的逻辑通道内重复发送指定系列的数据包属性。

  polite广播原语对实现广播协议很有用,例如否定应答。如果多个节点需要发送否定应答给同一个发送节点,只需要传递一条信息给发送者就够了。

  上层协议或应用程序使用polite广播原语时提供了一个间隔时间,和数据包及其数据包属性,从而避免了多份拷贝。polite广播原语将传出消息存储在队列缓冲区中,并存储数据包属性列表和设置计时器。其中定时器被设置为在间隔时间的下半部分中的一个随机时间(详见10.2.5polite_send函数)。

  在上半部分的时间间隔中,发送者监听其它传输。如果它监听到一个数据包的属性与上层协议或应用程序提供的属性相匹配,则发送者将该数据包丢弃;发送者的定时器已被设置为在间隔时间的下半部分中的一个随机时间,当定时器触发时,如果发送者还没有收到相同属性的数据包,则向所有的邻居节点广播其数据包。

  polite广播模块不会给发送出去的数据包添加任何数据包属性,只保留由上层添加的数据包属性。(关于此处的理解可以结合polite和netflood理解)

10.1 polite相关定义

  polite相关结构体:

struct polite_conn {
/**一个polite连接,对比abc多了定时器、队列缓冲区以及数据包头文件的尺寸*/
struct abc_conn c; //abc连接
const struct polite_callbacks *cb;
struct ctimer t; //ctimer定时器
struct queuebuf *q; //队列缓冲区
uint8_t hdrsize; /*上层会给polite传递一个表示数据包属性的结构体,polite将结构体加到数据包头,hdrsize即代表结构体尺寸。(详见12.1中netflood_hdr结构体)*/
};

struct polite_callbacks {
/**用户自定义polite回调函数的结构体,对比abc多了丢包回调函数*/
void (* recv)(struct polite_conn *c);
void (* sent)(struct polite_conn *c);
void (* dropped)(struct polite_conn *c); //丢弃一个数据包时调用
};


  polite数据包相关属性:

#define POLITE_ATTRIBUTES ABC_ATTRIBUTES //定义polite数据包属性为abc包属性


10.2 主要操作

10.2.1 回调函数

  (1)用户自定义回调函数:由用户在使用polite时自己定义并调用,调用时封装在polite_callbacks里,即polite的回调类型。(详见10.3polite实例中的使用方法)

  (2)系统回调函数为:接收回调recv和发送回调sent。调用系统回调函数时封装在abc
c995
_callbacks里,即abc的回调类型。具体代码如下:

static const struct abc_callbacks abc = { recv, sent };

static void recv(struct abc_conn *abc){
/**系统接收回调函数,首先会判断收到的数据包和本节点要发送的数据包是否相同*/
struct polite_conn *c = (struct polite_conn *)abc;

/*如果收到的数据包和本节点要发送的数据包相同*/
if(c->q != NULL &&
//队列缓冲区不为空
packetbuf_datalen() == queuebuf_datalen(c->q) &&
//数据包缓冲区中数据包长度和队列缓冲区中数据包长度相等
memcmp(packetbuf_dataptr(), queuebuf_dataptr(c->q), MIN(c->hdrsize, packetbuf_datalen())) == 0)
//数据包缓冲区中数据和队列缓冲区中数据的前MIN(c->hdrsize, packetbuf_datalen())字节相同
{
queuebuf_free(c->q); //释放queuebuf中的内存
c->q = NULL;
ctimer_stop(&c->t);
if(c->cb->dropped) {
c->cb->dropped(c); //调用用户自定义的丢包回调函数丢弃该数据包
}
}
if(c->cb->recv) {
c->cb->recv(c);
}
}

static void sent(struct abc_conn *c, int status, int num_tx){
/**系统定义发送回调函数为空*/
}


10.2.2 polite_open

void polite_open(struct polite_conn *c, uint16_t channel, const struct polite_callbacks *cb){
/**打开polite连接*/
abc_open(&c->c, channel, &abc); //打开abc连接,并打开系统回调
c->cb = cb; //打开用户自定义回调
}


10.2.3 polite_close

void polite_close(struct polite_conn *c){
/**关闭polite连接*/
abc_close(&c->c); //关闭abc连接
ctimer_stop(&c->t); //停止定时器
if(c->q != NULL) {
queuebuf_free(c->q); //释放queuebuf中的内存
c->q = NULL;
}
}


10.2.4 send

static void send(void *ptr){
/**实际发送数据包的函数(区别polite_send),调用了abc_send*/
struct polite_conn *c = ptr;

if(c->q != NULL) {
queuebuf_to_packetbuf(c->q); //从queuebuf中取出数据到packetbuf中
queuebuf_free(c->q);
c->q = NULL;
abc_send(&c->c); //调用abc_send发送数据
if(c->cb->sent) {
c->cb->sent(c); //调用用户自定义发送回调
}
}
}


10.2.5 polite_send

int polite_send(struct polite_conn *c, clock_time_t interval, uint8_t hdrsize){
/**设置定时器时间,到期后调用send发送数据包*/
/*如果queuebuf不为空,则代表还有准备好的数据未发送。此时清空queuebuf中的旧数据。
原因:send函数中每次发送数据会执行queuebuf_free(c->q)清空queuebuf。所以如果queuebuf还有数据,那便是准备好未发送的。*/
if(c->q != NULL) {
queuebuf_free(c->q);
}
c->hdrsize = hdrsize;
c->q = queuebuf_new_from_packetbuf();
if(c->q != NULL) {
/*设置定时器时间为在间隔时间的下半部分中的一个随机时间,interval为传输间隔时间*/
ctimer_set(&c->t, interval / 2 + (random_rand() % (interval / 2)), send, c);
return 1;
}
return 0;
}


10.2.6 polite_cancel

void polite_cancel(struct polite_conn *c){
/**取消polite,停止定时器后节点会停止发送数据,但是仍会接收数据(区别polite_close)*/
ctimer_stop(&c->t); //停止定时器
if(c->q != NULL) {
queuebuf_free(c->q);
c->q = NULL;
}
}


10.3 polite广播实例

10.3.1 官方实例源码

  官方源码中有自带的例子,简单清晰,对其自定义回调函数的打印信息稍作修改。

(1)代码如下:

#include "net/rime/polite.h"
#include "contiki.h"
#include <stdio.h>

PROCESS(example_polite_process, "example-polite");
AUTOSTART_PROCESSES(&example_polite_process);

static void recv(struct polite_conn *c){
/**用户自定义接收回调,打印收到的数据*/
printf("node1 recv '%s'\n",(char *)packetbuf_dataptr());//其它三个节点分别设置为node2~node4
}
static void sent(struct polite_conn *c){
/**用户自定义发送回调,打印已发送信息*/
printf("node1 sent\n"); //其它三个节点分别设置为node2~node4
}
static void dropped(struct polite_conn *c){
/**用户自定义丢包回调,打印丢包信息*/
printf("node1 dropped1\n"); //其它三个节点分别设置为node2~node4
}
static const struct polite_callbacks callbacks = { recv, sent, dropped };

PROCESS_THREAD(example_polite_process, ev, data)
{
static struct polite_conn c;
PROCESS_EXITHANDLER(polite_close(&c));
PROCESS_BEGIN();

polite_open(&c,136,&callbacks);//打开polite,设置信道号136,会依次打开系统回调和用户自定义回调

while(1) {
static struct etimer et;
etimer_set(&et, CLOCK_SECOND * 4);
PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et));
packetbuf_copyfrom("Dkit", 5);
polite_send(&c, CLOCK_SECOND * 4, 4); //设置传输间隔时间为4s,并设置包头结构体尺寸为4
}

PROCESS_END();
}


10.3.2 Cooja仿真测试

  (1)利用Cooja仿真器创建4个节点,将它们放置在各自的通信范围内,此时各个节点都能互相收到广播的数据包。观察Mote output窗口可发现当其中一个节点广播数据包以后,其他节点的recv回调函数监听到该数据包与自己待发送的数据包的包属性相同,就丢弃自己待发送的数据包。(recv代码中是先监听做出判断,再接收数据包,仿真输出结果的顺序与代码是一致的)

  原本程序中设置的是每个节点每隔4s发送一次数据包,现在4个节点也是每隔4s广播一次数据包,这样polite gossip算法就有效地就减少了在一个局域网中相同数据包的重复发送。





  (2)将其中两个节点删掉,剩余两个节点分别放置在对方的通信范围外,观察Mote output窗口可以发现,此时两个节点都监听不到其它节点广播的相同数据包,因此两个节点都不停地以4s的间隔时间广播各自的数据包。



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: