/**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->skipfi;i=i+1::elsebreakod;/*寻找需要立即处理的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]=0fi::else->skipfi;i=i+1;::elsebreakod;/*随机选择一个不为空的阻塞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;::skipfi::skipfi;i=i+1::elsebreakod;/*阻塞或传递当前的数据包*/i=0;j=0;if::skip->/*阻塞这个数据包*//*随机选择一个空位*/do::j>=0->j=(j+1)%QSIZE::skip->i=j;/*获得随机数*/breakod;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::skipfifi;endofsend:skip::skip->/*直接传递或丢弃数据包*/if::rdata!type,v,seq::skipfifiod}proctypereceiver2media(){byteseq;bytev;mtypetype;do::revts?type,v,seq->if::sevts!type,v,seq::skipfiod}/**发送方*发送两类消息: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::elsefi::(head_seq>tail_seq)->/*非递增情景*/if::(s<head_seq&&s>tail_seq)->gotoendofack::elsefi::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)thens'>'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)thens'<'expected_seqno--收到的消息排在期待数的前面3.if(s==expected_seqno)thens'=='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)%WINfiod}/*Promela入口程序*/init{atomic{runsender();runsender2media();runreceiver();runreceiver2media();}}