/*
*PROMELAValidationModel
*协议层验证
*
*验证BCL可靠性协议Cversion1.0
*
*Yuhuang
*2007-7-29
*/
/*
*本版本特点:采用集合点来通信,允许CHK、RTT、ACK、DATA丢包,实现了队列自动重发。
*加入了对传输介质的模拟。介质可以丢包,导致包失序。
*加入了对传输介质中存在延迟阻塞的模拟。包可以失序。
*加入对接收方向发送方的失序模拟。
*CHK的语义实现
*
*/
/*
*测试记录:
*WIN=5QSIZE=2跑了大约_秒钟
*WIN=7QSIZE=3跑了大约_分钟
*WIN=9QSIZE=4???
*/
/**************数据结构定义*************/
/*序号范围定义*/
#defineWIN7
/*发送队列大小定义*/
#defineQSIZE3
/*Note:上面是比较保险的做法:WIN=QSIZE*2+1*/
/*
#defineinc(x)(x=(x+1)%WIN)
*/
/*消息类型定义*/
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;
/*发送队列元素状态标志*/
boolsflag[WIN];/*=0,已确认;=1,未确认*/
/*发送窗口的范围是由head_seq和QSIZE一起决定的*/
/*AvaliableRange:head_seq~head_seq+QSIZE-1*/
/*接收方期望系列号*/
byteexpected_seqno;
/*接收方窗口*/
/*min,max*/
bytemin_seq;
bytemax_seq;
byterseqno;/*recvseqno*/
/*未收到消息记录链*/
boolrflag[WIN];/*=1表示未收到,=0表示已经收到*/
/*进程说明:
*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->
/*CHK语义实现*/
/*CHK的完整语义为:
检查CHK携带的seqno之前(含)的数据包是否发送到位.通过硬件相关技术,
它可以严格保证在CHK发出之前的所有数据包都已经丢失或者到达,
而不会存在某个数据包因为延迟而后于CHK包到达。
*/
i=0;
j=0;
if
::(type==CHK)->
do
::i<QSIZE->
if
::flag[i]!=0->/*当前Msg为一个不为空的阻塞Msg*/
if/*随机选择是发送还是丢弃*/
::rdata!msg[i].type,msg[i].version,msg[i].seqno/*发送*/
::skip/*丢弃*/
fi;
flag[i]=0;/*标记当前消息槽可用*/
::else
fi;
i=i+1;
::elsebreak
od;
::else->skip
fi;
/*======================================================*/
/*阻塞语义实现*/
/*不为空者逐个加一*/
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->/*选择到一个不为空的阻塞Msg*/
if/*随机选择是发送还是丢弃*/
::rdata!msg[i].type,msg[i].version,msg[i].seqno/*发送*/
::skip/*丢弃*/
fi;
flag[i]=0;/*标记当前消息槽可用*/
break;
::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]中已经存有消息*/
gotoendofsend1/*信息已经阻塞,无须发送*/
::else->/*Msg[i]中已有一条消息,且不阻塞这条消息了*/
if
::rdata!type,v,seq
::skip
fi
fi;
endofsend1:skip
::skip->/*直接传递或丢弃数据包*/
if
::rdata!type,v,seq
::skip
fi
fi
od
}
proctypereceiver2media()
{
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
::revts?type,v,seq->/*从receiver方接到消息*/
/*不为空者逐个加一*/
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
::sevts!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->/*选择到一个不为空的阻塞Msg*/
if/*随机选择是发送还是丢弃*/
::sevts!msg[i].type,msg[i].version,msg[i].seqno/*发送*/
::skip/*丢弃*/
fi;
flag[i]=0;/*标记当前消息槽可用*/
break;
::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]中已经存有消息*/
gotoendofsend2/*信息已经阻塞,无须发送*/
::else->/*Msg[i]中已有一条消息,且不阻塞这条消息了*/
if
::sevts!type,v,seq
::skip
fi
fi;
endofsend2:skip
::skip->/*直接传递或丢弃数据包*/
if
::sevts!type,v,seq
::skip
fi
fi
od
}
/*
*发送方
*发送两类消息:DATA(数据)、CHK(缓冲区可用性检查)
*
*接受两类消息:ACK(确认)、RRT(请求重传)
*/
proctypesender()
{
bytes;
byteversion;
intrrt_version;
bytei;
bytej;
/*发送队列*/
Msgsbuf[WIN];/*为了方便,不使用QSIZE。实际上,某个时刻起作用的个数仍然为QSIZE个*/
/*假设第0个包已经发出*/
head_seq=0;
tail_seq=0;
sflag[0]=1;
cur_seq=0;/*定位发送点*/
rrt_version=0;/*initrrtversionnumber*/
do
::(tail_seq+WIN-head_seq+1)%WIN<QSIZE->/**队列不为满(没有超出窗口范围),允许加入新的待发消息**/
/*加入一个元素到发送队列中*/
progress_2:tail_seq=(tail_seq+1)%WIN;/*更新tail,head保持不变*/
sflag[tail_seq]=1;
/*立即发送之*/
sdata!DATA,0,tail_seq;/*trytosendnow!*/
::sevts?ACK,version,s->/**接收到ACK**/
/*无须进行ACK失序处理*/
assert(s<WIN);
/*ACK的时候需要检查是否可以向前滑动窗口(这里头变尾不变)*/
i=s;
sflag[i]=0;/*标志sflag[s]已经确认*/
if
::i==head_seq->/*可以滑动,至少一格,多则数格*/
do
::sflag[i]==0->/*此元素已经确认*/
i=(i+1)%WIN;
::else->
/*将队列前面连续的都确认了的消息从窗口范围内删除。*/
head_seq=i;/*gotnewhead,此时do的第一个分支又可以工作了*/
break;
od
::else
fi
::sevts?RRT,version,s->/**接收到RRT**/
if
::(version==rrt_version)->/*预期rrt*/
/*重发s,rrt_version加1*/
sdata!DATA,0,i;
rrt_version=(rrt_version+1)%WIN;/*inc(rrt_version);*/
::else->/*接受到非预期rrt*/
skip/*简单丢弃之*/
fi
::timeout->/*超时*/
/*扫描发送队列,找到第一个可以发送的消息,对其发出CHK*/
i=head_seq;
j=tail_seq;
do
::i<=tail_seq->
if
::(sflag[i]==1)->
sdata!CHK,rrt_version,i;
break;
::else->skip
fi;
i=(i+1)%WIN;
::else->
break;
od;
od
}
/*
*接收方
*发送两类消息:ACK(确认)、RRT(请求重传)
*
*接受两类消息:DATA(数据)、CHK(缓冲区可用性检查)
*/
proctypereceiver()
{
bytev;
/*假设0号消息已经收到*/
max_seq=0;
min_seq=0;
rflag[0]=0;
do
::rdata?CHK,v,rseqno->/**收到CHK消息**/
if
::((min_seq+WIN-rseqno-1)%WIN<WIN/2)->/*seqno<min_seqno*/
revts!ACK,v,rseqno;
::((rseqno-max_seq+WIN-1)%WIN<WIN/2)->/*Seqno>max_seqno*/
revts!RRT,v,rseqno;
::else/*min_seqno<seqno<max_seqno*/
/*检查s是否已经接收到*/
if
::rflag[rseqno]==1->/*未收到*/
revts!RRT,v,rseqno;
::else->/*已收到*/
revts!ACK,v,rseqno;
fi
fi
::rdata?DATA,v,rseqno->/**收到DATA消息,s为消息序列号**/
if
::((rseqno-min_seq+WIN-1)%WIN<WIN/2&&(max_seq-rseqno+WIN-1)%WIN<WIN/2)->/*s在二者之间*/
rflag[rseqno]=0;/*标志为已经收到*/
revts!ACK,v,rseqno;
/*尝试更新min_seq*/
rseqno=min_seq;
do
::rflag[rseqno]==0->
if
/*::error(max_seq-rseqno+WIN-1)%WIN<WIN/2->*/
::(max_seq-rseqno+WIN)%WIN<=WIN/2->
rseqno=(rseqno+1)%WIN;
::else->break;
fi
::else->
break;
od;
min_seq=rseqno-1;
::((rseqno-max_seq+WIN-1)%WIN<=WIN/2)->/*seqno>max_seqno*/
/*将max_seq到s之间的数据全部标记为未收到*/
do
::max_seq<rseqno->
max_seq=(max_seq+1)%WIN;
rflag[max_seq]=1;/*标记未收到*/
::else->
break;
od;
rflag[max_seq]=0;/*max_seq已经更新成了s,标记为已经收到*/
::else->/*seqno=max_seqno||seqno<=min_seqno*/
assert(false&&1==0);/*deadlyfault*//*收到重复消息*/
fi
od
}
/*Promela入口程序*/
init
{
atomic{
runsender();
runsender2media();
runreceiver();
runreceiver2media();
}
}
*PROMELAValidationModel
*协议层验证
*
*验证BCL可靠性协议Cversion1.0
*
*Yuhuang
*2007-7-29
*/
/*
*本版本特点:采用集合点来通信,允许CHK、RTT、ACK、DATA丢包,实现了队列自动重发。
*加入了对传输介质的模拟。介质可以丢包,导致包失序。
*加入了对传输介质中存在延迟阻塞的模拟。包可以失序。
*加入对接收方向发送方的失序模拟。
*CHK的语义实现
*
*/
/*
*测试记录:
*WIN=5QSIZE=2跑了大约_秒钟
*WIN=7QSIZE=3跑了大约_分钟
*WIN=9QSIZE=4???
*/
/**************数据结构定义*************/
/*序号范围定义*/
#defineWIN7
/*发送队列大小定义*/
#defineQSIZE3
/*Note:上面是比较保险的做法:WIN=QSIZE*2+1*/
/*
#defineinc(x)(x=(x+1)%WIN)
*/
/*消息类型定义*/
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;
/*发送队列元素状态标志*/
boolsflag[WIN];/*=0,已确认;=1,未确认*/
/*发送窗口的范围是由head_seq和QSIZE一起决定的*/
/*AvaliableRange:head_seq~head_seq+QSIZE-1*/
/*接收方期望系列号*/
byteexpected_seqno;
/*接收方窗口*/
/*min,max*/
bytemin_seq;
bytemax_seq;
byterseqno;/*recvseqno*/
/*未收到消息记录链*/
boolrflag[WIN];/*=1表示未收到,=0表示已经收到*/
/*进程说明:
*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->
/*CHK语义实现*/
/*CHK的完整语义为:
检查CHK携带的seqno之前(含)的数据包是否发送到位.通过硬件相关技术,
它可以严格保证在CHK发出之前的所有数据包都已经丢失或者到达,
而不会存在某个数据包因为延迟而后于CHK包到达。
*/
i=0;
j=0;
if
::(type==CHK)->
do
::i<QSIZE->
if
::flag[i]!=0->/*当前Msg为一个不为空的阻塞Msg*/
if/*随机选择是发送还是丢弃*/
::rdata!msg[i].type,msg[i].version,msg[i].seqno/*发送*/
::skip/*丢弃*/
fi;
flag[i]=0;/*标记当前消息槽可用*/
::else
fi;
i=i+1;
::elsebreak
od;
::else->skip
fi;
/*======================================================*/
/*阻塞语义实现*/
/*不为空者逐个加一*/
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->/*选择到一个不为空的阻塞Msg*/
if/*随机选择是发送还是丢弃*/
::rdata!msg[i].type,msg[i].version,msg[i].seqno/*发送*/
::skip/*丢弃*/
fi;
flag[i]=0;/*标记当前消息槽可用*/
break;
::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]中已经存有消息*/
gotoendofsend1/*信息已经阻塞,无须发送*/
::else->/*Msg[i]中已有一条消息,且不阻塞这条消息了*/
if
::rdata!type,v,seq
::skip
fi
fi;
endofsend1:skip
::skip->/*直接传递或丢弃数据包*/
if
::rdata!type,v,seq
::skip
fi
fi
od
}
proctypereceiver2media()
{
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
::revts?type,v,seq->/*从receiver方接到消息*/
/*不为空者逐个加一*/
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
::sevts!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->/*选择到一个不为空的阻塞Msg*/
if/*随机选择是发送还是丢弃*/
::sevts!msg[i].type,msg[i].version,msg[i].seqno/*发送*/
::skip/*丢弃*/
fi;
flag[i]=0;/*标记当前消息槽可用*/
break;
::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]中已经存有消息*/
gotoendofsend2/*信息已经阻塞,无须发送*/
::else->/*Msg[i]中已有一条消息,且不阻塞这条消息了*/
if
::sevts!type,v,seq
::skip
fi
fi;
endofsend2:skip
::skip->/*直接传递或丢弃数据包*/
if
::sevts!type,v,seq
::skip
fi
fi
od
}
/*
*发送方
*发送两类消息:DATA(数据)、CHK(缓冲区可用性检查)
*
*接受两类消息:ACK(确认)、RRT(请求重传)
*/
proctypesender()
{
bytes;
byteversion;
intrrt_version;
bytei;
bytej;
/*发送队列*/
Msgsbuf[WIN];/*为了方便,不使用QSIZE。实际上,某个时刻起作用的个数仍然为QSIZE个*/
/*假设第0个包已经发出*/
head_seq=0;
tail_seq=0;
sflag[0]=1;
cur_seq=0;/*定位发送点*/
rrt_version=0;/*initrrtversionnumber*/
do
::(tail_seq+WIN-head_seq+1)%WIN<QSIZE->/**队列不为满(没有超出窗口范围),允许加入新的待发消息**/
/*加入一个元素到发送队列中*/
progress_2:tail_seq=(tail_seq+1)%WIN;/*更新tail,head保持不变*/
sflag[tail_seq]=1;
/*立即发送之*/
sdata!DATA,0,tail_seq;/*trytosendnow!*/
::sevts?ACK,version,s->/**接收到ACK**/
/*无须进行ACK失序处理*/
assert(s<WIN);
/*ACK的时候需要检查是否可以向前滑动窗口(这里头变尾不变)*/
i=s;
sflag[i]=0;/*标志sflag[s]已经确认*/
if
::i==head_seq->/*可以滑动,至少一格,多则数格*/
do
::sflag[i]==0->/*此元素已经确认*/
i=(i+1)%WIN;
::else->
/*将队列前面连续的都确认了的消息从窗口范围内删除。*/
head_seq=i;/*gotnewhead,此时do的第一个分支又可以工作了*/
break;
od
::else
fi
::sevts?RRT,version,s->/**接收到RRT**/
if
::(version==rrt_version)->/*预期rrt*/
/*重发s,rrt_version加1*/
sdata!DATA,0,i;
rrt_version=(rrt_version+1)%WIN;/*inc(rrt_version);*/
::else->/*接受到非预期rrt*/
skip/*简单丢弃之*/
fi
::timeout->/*超时*/
/*扫描发送队列,找到第一个可以发送的消息,对其发出CHK*/
i=head_seq;
j=tail_seq;
do
::i<=tail_seq->
if
::(sflag[i]==1)->
sdata!CHK,rrt_version,i;
break;
::else->skip
fi;
i=(i+1)%WIN;
::else->
break;
od;
od
}
/*
*接收方
*发送两类消息:ACK(确认)、RRT(请求重传)
*
*接受两类消息:DATA(数据)、CHK(缓冲区可用性检查)
*/
proctypereceiver()
{
bytev;
/*假设0号消息已经收到*/
max_seq=0;
min_seq=0;
rflag[0]=0;
do
::rdata?CHK,v,rseqno->/**收到CHK消息**/
if
::((min_seq+WIN-rseqno-1)%WIN<WIN/2)->/*seqno<min_seqno*/
revts!ACK,v,rseqno;
::((rseqno-max_seq+WIN-1)%WIN<WIN/2)->/*Seqno>max_seqno*/
revts!RRT,v,rseqno;
::else/*min_seqno<seqno<max_seqno*/
/*检查s是否已经接收到*/
if
::rflag[rseqno]==1->/*未收到*/
revts!RRT,v,rseqno;
::else->/*已收到*/
revts!ACK,v,rseqno;
fi
fi
::rdata?DATA,v,rseqno->/**收到DATA消息,s为消息序列号**/
if
::((rseqno-min_seq+WIN-1)%WIN<WIN/2&&(max_seq-rseqno+WIN-1)%WIN<WIN/2)->/*s在二者之间*/
rflag[rseqno]=0;/*标志为已经收到*/
revts!ACK,v,rseqno;
/*尝试更新min_seq*/
rseqno=min_seq;
do
::rflag[rseqno]==0->
if
/*::error(max_seq-rseqno+WIN-1)%WIN<WIN/2->*/
::(max_seq-rseqno+WIN)%WIN<=WIN/2->
rseqno=(rseqno+1)%WIN;
::else->break;
fi
::else->
break;
od;
min_seq=rseqno-1;
::((rseqno-max_seq+WIN-1)%WIN<=WIN/2)->/*seqno>max_seqno*/
/*将max_seq到s之间的数据全部标记为未收到*/
do
::max_seq<rseqno->
max_seq=(max_seq+1)%WIN;
rflag[max_seq]=1;/*标记未收到*/
::else->
break;
od;
rflag[max_seq]=0;/*max_seq已经更新成了s,标记为已经收到*/
::else->/*seqno=max_seqno||seqno<=min_seqno*/
assert(false&&1==0);/*deadlyfault*//*收到重复消息*/
fi
od
}
/*Promela入口程序*/
init
{
atomic{
runsender();
runsender2media();
runreceiver();
runreceiver2media();
}
}