这一次的建模与前面的实例有相当的不同之处,这里建模的是一个协议,并且我们在此次建模使用的精化属于一种时间方面的精化。
需求
首先来看看这个任务的需求
文件传输协议是用一个代理方向另一个代理方传送一个顺序文件。接收到的文件应该和源文件相同
顺序文件由一个数据项的序列
文件需要以一个个片段的方式在站点之间传输
整个协议相当于一个分布式程序
初始模型
在初始模型中,我们只需建模协议的初始状态和终止状态,对于其中的细节部分我们一概不管
现在假设有一个发送发Sender,和一个接收方Receiver,初始状态时,发送方有一个代表文件的f,接收方啥都没有,终止状态时,发送方还是有一个f,不过接收方有了一个g,这个g应该和f是完全相同的,不然肯定是出了问题
那么我们如何建模代表文件的f呢?首先我们需要一个集合D,代表文件中所有可能存储数据的集合,然后需要一个正整数n,代表文件中的位置,f可以表示为区间[1,n]到D的全函数(可以类比一下,n可以代表文件的中第n个字节,f(n)就代表该字节存储的内容)
新建一个context,将上述内容建模
然后再来考虑会变的变量,变量g是[1,n]到D的偏函数,因为文件没有传输完成时,文件中存在某些位置没有数据,这一特征我们使用偏函数来表示,然后需要一个布尔变量b,它代表这个协议是否结束
下面的不变式2和3分别表示一些未结束以及协议结束的情况
最后再描述一下初始状态以及终止状态,第一次精化就这么结束了
第一次精化
在第一次精化中,我们要实现功能3,即文件可以一个片段一个片段地进行传输,我们要在这里加入一个receive事件,用来表示接受方获取发送方文件片的行为
初始模型和第一次精化行为对比:
第一次精化文件传输表现的行为:
引入一个新变量r,r的取值为1…n+1,其实r的作用相当于一个指针,用来指向下一个需要发送的文件片段,当r=n+1的时候,即r的指向为空,此时文件传输完毕
然后我们用变量h将变量g取代,当h的作用域为1…r-1的时候,变量h正好等于常量f
事件的话多了一个receive事件,每次执行接受事件时,都会将当前的文件片段加入h,并且指针+1
因为receive事件是新加入的,所以需要证明收敛,这里提出一个变动式
第一次精化结束
第二次精化
在第一次精化中,我们新增了receive这个事件,并且新增指针r作为确定当前发送的文件片段,很容易想到,我们还需要一个指针s,s指向发送方下一个要发送的文件片段,在执行Send操作之后,s指针将会下移,当成功执行receive操作之后,send才会发送下一个文件片段,第二次精化的执行轨迹如下图所示:
我们还需要一个变量来代表当前发送的文件片数据,使用d表示,d其实就等于f(s)。新定义的指针s和r有一定的关系,要么等于r要么比r大1,并且s同样小于等于n+1
对这个模型进行建模
这里的
d
:
∈
D
d:∈D
d:∈D是一个非确定性赋值操作,意思是d被赋予了D中的某个值,但不确定具体是哪个(其实感觉有点像编程语言中定义指针为空的感觉,不过这里还额外定义了范围)
send是个新事件,提出变动式n+1-s,第二次精化结束
第三次精化
其实这里的第三次精化并没有什么功能性的变化,只不过我们发现,我们其实不用完全比较s和r的数值,由于s和r最多只相差1,所以我们比较它们的奇偶性就可以了,第三次精化改动的部分就是使用奇偶性来判断文件片段是否传输到达
在这里我们需要一个新的context,定义parity函数,这个自定义函数就用使用判断奇偶性的,定义0为偶数,1为奇数,这里的parity采用了递归定义的方式,0的奇偶性是0,i的奇偶性是f(i),i+1的奇偶性是1-f(i),这里比较不同的地方是,我们在定义的时候加入了全称量词
machine这里新增了p和q这俩变量,分别代表s和r的奇偶性
事件这里改动不大,就是把s和r的判断改成p和q的判断而已
至此,第三次精化也完成了