Promela BCL-6 B4.0 Protocol [Incomplete]

本文介绍了一种使用PROMELA语言验证BCL可靠性协议的方法,该协议版本为4.0。通过采用集合点通信和队列自动重发机制,允许CHK、RTT、ACK、DATA等包丢失情况下的可靠传输。同时,模拟了传输介质特性,包括丢包、包失序及延迟阻塞。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
* PROMELA Validation Model
* 协议层验证
*
* 验证BCL可靠性协议B version4.0
*
* Yuhuang
* 2007-7-27
*/





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






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





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


/* 序号范围定义 */
#define WIN 7
/*
 发送队列大小定义 */
#define QSIZE 3
/*
Note:上面是比较保险的做法: WIN = QSIZE * 2 + 1*/

/* 消息类型定义 */
mtype 
= { ACK,DATA,RRT,CHK }


/* 
 * { ACK|RRT,res|version, seqno }
 
*/
chan sevts 
= [0] of { mtype,byte,byte };
chan revts 
= [0] of { mtype,byte,byte };


/*
 * { DATA|CHK, res|version, seqno} 
 * 数据队列 
 
*/
chan sdata 
= [0] of { mtype,byte,byte };
chan rdata 
= [0] of { mtype,byte,byte };

/*
 * 为实现阻塞模拟,需要定义复合类型数组
 
*/
typedef Msg{
    mtype type;
    byte version;
    byte seqno;
};


/* 发送记录队列 */
/* 当ACK发生的时候,ACK(含)之前的内容都被清除 */
byte head_seq;
byte tail_seq;
byte cur_seq;
/*
#define inc(x) (x=(x+1)%WIN)
*/

/* 接收方期望系列号 */
byte expected_seqno;




/* 进程说明:
 * 1. sender2media    发送方到接收方的介质模拟进程
 * 2. receiver2media     接收方到发送方的介质模拟进程
 * 3. sender        发送方进程
 * 4. receiver        接收方进程
 
*/

proctype sender2media()
{
    byte seq;
    byte v;
    mtype type;
    
    
/* 阻塞队列 */
    Msg msg[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子序列结束
    
*/
    byte flag[QSIZE];    
    byte i;    
/* 记录随机数下标用 */
    byte j;    
/* 产生随机数用 */
    
    
    
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] 中已经存有消息*/
                goto endofsend    
/* 信息已经阻塞,无须发送 */
                
            
::else->    /* Msg[i]中已有一条消息,且不阻塞这条消息了 */
                
if
                
::rdata!type,v,seq
                
::skip
                fi
            fi;
endofsend
:        skip
        
::skip ->    /* 直接传递或丢弃数据包 */        
            
if
            
::rdata!type,v,seq
            
::skip
            fi
        fi
    od
}


proctype receiver2media()
{
    byte seq;
    byte v;
    mtype type;
    
    
do
    
::revts?type,v,seq ->
        
if
        
::sevts!type,v,seq
        
::skip
        fi
    od
}



/*
* 发送方
* 发送两类消息: DATA(数据) 、CHK( 缓冲区可用性检查) 

* 接受两类消息: ACK( 确认) 、RRT(请求重传) 
*/
proctype sender()
{

    byte s;
    byte version;
    byte rrt_version;
    
    head_seq 
= 0;
    tail_seq 
= 0;
    cur_seq 
= 0;
    
    rrt_version 
= 0/* init rrt version number */
    
    
do
    
::(tail_seq+WIN-head_seq+1)%WIN != QSIZE ->    /* 队列不为满,允许发送*/
        
if
        
::sdata!DATA,0,cur_seq    /* normal send action */
        fi;
        
progress_2
:    cur_seq=(cur_seq+1)%WIN;
        tail_seq
=(tail_seq+1)%WIN;;    /* 更新tail,  head保持不变 */
    


    
::sevts?ACK,version,->
        
/* 进行ACK失序处理 */
        
/* 若s不在当前的head_seq~tail_seq范围内,则简单忽略之 */
        
if
        
::(head_seq < tail_seq) ->    /*顺序递增情景 */
            
if
            
::(s > tail_seq || s < head_seq ) ->
                goto endofack
            
::else
            fi
        
::(head_seq > tail_seq) ->    /* 非递增情景 */
            
if
            
::( s < head_seq && s > tail_seq ) ->
                goto endofack
            
::else
            fi
        
::else ->    /* 队列为空  */
            goto endofack    
/* 此ACK是在介质中被延迟了的ACK, 简单丢弃之 */
        fi;
        
        
assert(s<WIN);
        head_seq 
= (s + 1)%WIN;    /* 根据ACK更新head_seq,暗含了累积更新 */
        cur_seq 
= head_seq;
        
endofack
:    skip
        
    
::sevts?RRT,version,->
        
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    /* normal send action */
        fi;        
        cur_seq
=(cur_seq+1)%WIN;    
        
        
    
::timeout ->    /* 超时 */
        sdata
!CHK,rrt_version,head_seq; /* 超时,发CHK消息,查询是否需要重发 */
    od
}



/*
* 接收方
* 发送两类消息: ACK( 确认) 、RRT(请求重传) 

* 接受两类消息: DATA(数据) 、CHK( 缓冲区可用性检查) 
*/
proctype receiver()
{

    byte s;
    byte v;
    
    
do
    
::rdata?CHK,v,->    /*收到CHK消息*/
        
if
        
::(s == expected_seqno) ->    /* chk检查的数据包即为接收方期望的数据包 */
            revts
!RRT,v,s    /* 直接返回RRT消息 */


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

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


    
::rdata?DATA,v,->    /* 收到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-> 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{
    run sender();
    run sender2media();
    run receiver();
    run receiver2media();
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值