/*
*PROMELAValidationModel
*协议层验证
*
*验证BCL可靠性协议Bversion4.0
*
*Yuhuang
*2007-7-27
*/




/*
*本版本特点:采用集合点来通信,允许CHK、RTT、ACK、DATA丢包,实现了队列自动重发。
*加入了对传输介质的模拟。介质可以丢包,导致包失序。
*加入了对传输介质中存在延迟阻塞的模拟。包可以失序。
*
*加入对CHK的语义实现、接收方向发送方的失序模拟。
*/





/*
*测试记录:
*WIN=5QSIZE=2跑了大约_秒钟
*WIN=7QSIZE=3跑了大约_分钟
*WIN=9QSIZE=4???
*/




/**************数据结构定义*************/

/*序号范围定义*/
#defineWIN7
/*发送队列大小定义*/
#defineQSIZE3
/*Note:上面是比较保险的做法:WIN=QSIZE*2+1*/
/*消息类型定义*/
mtype={ACK,DATA,RRT,CHK}

/*
*{ACK|RRT,res|version,seqno}
*/
chansevts=[0]of{mtype,byte,byte};
chanrevts=[0]of{mtype,byte,byte};

/*
*{DATA|CHK,res|version,seqno}
*数据队列
*/
chansdata=[0]of{mtype,byte,byte};
chanrdata=[0]of{mtype,byte,byte};
/*
*为实现阻塞模拟,需要定义复合类型数组
*/
typedefMsg{
mtypetype;
byteversion;
byteseqno;
};

/*发送记录队列*/
/*当ACK发生的时候,ACK(含)之前的内容都被清除*/
bytehead_seq;
bytetail_seq;
bytecur_seq;
/*
#defineinc(x)(x=(x+1)%WIN)
*/
/*接收方期望系列号*/
byteexpected_seqno;



/*进程说明:
*1.sender2media发送方到接收方的介质模拟进程
*2.receiver2media接收方到发送方的介质模拟进程
*3.sender发送方进程
*4.receiver接收方进程
*/
proctypesender2media()
{
byteseq;
bytev;
mtypetype;
/*阻塞队列*/
Msgmsg[QSIZE];
/*flag语义:
*flag[i]=0的时候,表示msg[i]为空
*flag[i]=m(m>0)的时候,表示从msg[i]被阻塞以来,没有被阻塞的包的个数为m
*flag用法:
*(1)每次收到一个消息,将不为零的flag[i]逐个加一
*(2)发现某个flag[i]的值大于等于QSIZE时必须将其从阻塞队列中取出,进行发送或丢弃,转4
*(3)随机选择一个不为空的阻塞Msg,将其从阻塞队列中取出,进行发送或丢弃
*(4)flag子序列结束
*/
byteflag[QSIZE];
bytei;/*记录随机数下标用*/
bytej;/*产生随机数用*/

do
::sdata?type,v,seq->
/*不为空者逐个加一*/
i=0;
j=0;
do
::i<QSIZE->
if
::flag[i]!=0->
flag[i]=flag[i]+1
::else->skip
fi;
i=i+1
::elsebreak
od;
/*寻找需要立即处理的Msg*/
i=0;
j=0;
do
::i<QSIZE->
if
::(flag[i]==QSIZE-1)->
if
::rdata!msg[i].type,msg[i].version,msg[i].seqno->
flag[i]=0
fi
::else->skip
fi;
i=i+1;
::elsebreak
od;
/*随机选择一个不为空的阻塞Msg,将其从阻塞队列中取出,进行发送或丢弃*/
i=0;
j=0;
do
::i<QSIZE->
if
::flag[i]!=0->
if
::rdata!msg[i].type,msg[i].version,msg[i].seqno->
flag[i]=0;
::skip
fi
::skip
fi;
i=i+1
::elsebreak
od;
/*阻塞或传递当前的数据包*/
i=0;
j=0;
if
::skip->/*阻塞这个数据包*/
/*随机选择一个空位*/
do
::j>=0->
j=(j+1)%QSIZE
::skip->
i=j;/*获得随机数*/
break
od;
if
::flag[i]==0->/*Msg[i]为空,可以放下一条阻塞消息*/
msg[i].type=type;
msg[i].version=v;
msg[i].seqno=seq;
flag[i]=1;/*标记Msg[i]中已经存有消息*/
gotoendofsend/*信息已经阻塞,无须发送*/
::else->/*Msg[i]中已有一条消息,且不阻塞这条消息了*/
if
::rdata!type,v,seq
::skip
fi
fi;
endofsend:skip
::skip->/*直接传递或丢弃数据包*/
if
::rdata!type,v,seq
::skip
fi
fi
od
}

proctypereceiver2media()
{
byteseq;
bytev;
mtypetype;
do
::revts?type,v,seq->
if
::sevts!type,v,seq
::skip
fi
od
}


/*
*发送方
*发送两类消息:DATA(数据)、CHK(缓冲区可用性检查)
*
*接受两类消息:ACK(确认)、RRT(请求重传)
*/
proctypesender()
{
bytes;
byteversion;
byterrt_version;
head_seq=0;
tail_seq=0;
cur_seq=0;
rrt_version=0;/*initrrtversionnumber*/
do
::(tail_seq+WIN-head_seq+1)%WIN!=QSIZE->/*队列不为满,允许发送*/
if
::sdata!DATA,0,cur_seq/*normalsendaction*/
fi;
progress_2:cur_seq=(cur_seq+1)%WIN;
tail_seq=(tail_seq+1)%WIN;;/*更新tail,head保持不变*/


::sevts?ACK,version,s->
/*进行ACK失序处理*/
/*若s不在当前的head_seq~tail_seq范围内,则简单忽略之*/
if
::(head_seq<tail_seq)->/*顺序递增情景*/
if
::(s>tail_seq||s<head_seq)->
gotoendofack
::else
fi
::(head_seq>tail_seq)->/*非递增情景*/
if
::(s<head_seq&&s>tail_seq)->
gotoendofack
::else
fi
::else->/*队列为空*/
gotoendofack/*此ACK是在介质中被延迟了的ACK,简单丢弃之*/
fi;
assert(s<WIN);
head_seq=(s+1)%WIN;/*根据ACK更新head_seq,暗含了累积更新*/
cur_seq=head_seq;
endofack:skip
::sevts?RRT,version,s->
if
::(version==rrt_version)->/*预期rrt*/
/*将发送链上s(eqno)之前的数据都摘除*/
/*发送链上s之后的所有数据都重发,rrt_version加1*/
head_seq=s;/*注意:由于是响应RRT消息,不要写成了head_seq=(s+1)%WIN*/
cur_seq=head_seq;/*调整发送指针,准备重发*/
rrt_version=(rrt_version+1)%WIN/*inc(rrt_version);*/
::else->/*接受到非预期rrt*/
skip/*简单丢弃之*/
fi
::((cur_seq!=tail_seq)&&(tail_seq-head_seq)!=0)->
/*队列不为空,允许发送.此情景配合timeout时重发使用*/
/*第一个判断是为了防止发送不存在内容*/
if
::sdata!DATA,0,cur_seq/*normalsendaction*/
fi;
cur_seq=(cur_seq+1)%WIN;

::timeout->/*超时*/
sdata!CHK,rrt_version,head_seq;/*超时,发CHK消息,查询是否需要重发*/
od
}


/*
*接收方
*发送两类消息:ACK(确认)、RRT(请求重传)
*
*接受两类消息:DATA(数据)、CHK(缓冲区可用性检查)
*/
proctypereceiver()
{
bytes;
bytev;
do
::rdata?CHK,v,s->/*收到CHK消息*/
if
::(s==expected_seqno)->/*chk检查的数据包即为接收方期望的数据包*/
revts!RRT,v,s/*直接返回RRT消息*/

::((s<expected_seqno&&expected_seqno-s<WIN/2)||
(s>expected_seqno&&s-expected_seqno>WIN/2))->/*小于期望包号,ACK之*/
revts!ACK,0,(expected_seqno+WIN-1)%WIN

::else->/*CHK检查消息错,系统出错*/
printf("s=%dexp=%d ",s,expected_seqno);
assert(false)
fi

::rdata?DATA,v,s->/*收到DATA消息,s为消息序列号*/
/*s与expected_seqno在队列中前后关系的判断:
1.if(s>expected_seqno&&s-expected_seqno<WIN/2)or*normal
(s<expected_seqno&&expected_seqno-s>WIN/2)then
s'>'expected_seqno--收到的消息(s)排在期待数(expected_seqno)的后面
2.if(s<expected_seqno&&expected_seqno-s<WIN/2)or*normal
(s>expected_seqno&&s-expected_seqno>WIN/2)then
s'<'expected_seqno--收到的消息排在期待数的前面
3.if(s==expected_seqno)then
s'=='expected_seqno--收到的消息等于期待的数
*/
if
::((s>expected_seqno&&s-expected_seqno<WIN/2)||
(s<expected_seqno&&expected_seqno-s>WIN/2))->/*大于*/
/*在存在丢包的流水环境下,这种情况是完全会出现的,不应该看作系统严重错误*/
/*assert(false)*//*非预期消息,系统严重错误*/
/*Yuhuang的方案(2007-7-27):
简单忽略之
*/
skip
::(s==expected_seqno)->/*等于*/
revts!ACK,0,expected_seqno;
expected_seqno=(expected_seqno+1)%WIN
::else->/*小于*/
revts!ACK,0,(expected_seqno+WIN-1)%WIN
fi
od


}

/*Promela入口程序*/
init
{
atomic{
runsender();
runsender2media();
runreceiver();
runreceiver2media();
}
}
本文介绍了一种基于PROMELA的BCL可靠性协议版本4.0的验证模型。该模型通过采用集合点通信方式,允许丢包并实现队列自动重发机制。同时,模拟了传输介质中的丢包、包失序及延迟阻塞现象。
1086

被折叠的 条评论
为什么被折叠?



