Promela BCL-6 B4.0 Protocol [Incomplete]

/*
*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
:: 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 ->
if
:: rdata ! msg[i] . type , msg[i] . version , msg[i] . seqno ->
flag[i]
= 0 ;
:: skip
fi
:: 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]中已经存有消息 */
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();
}

}

你可能感兴趣的:(protocol)