验证BCL可靠性协议C version1.0[Incomplete]

/*
*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 ;
:: else break
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
:: else break
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 ;
:: else break
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
:: else break
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
:: else break
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 ;
:: else break
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
:: else break
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();
}

}

你可能感兴趣的:(version)