ESFramework 通信框架介绍 -- 消息
时间:2010-09-14 来源:zhuweisky
需要交互的分布式系统之间通过消息来传递有意义的信息。消息是通信框架的核心。离开了消息,再谈通信框架就没有任何意义,所以,消息是ESFramework中一个最核心的概念。
一. 消息的类别
消息的两种形态指的是消息在网络上进行传输的形态和消息在通信框架内部(或应用程序中)的形态。
在网络上,消息表现为一串字节流;而在基于面向对象的通信框架内部,消息通常表示为一个对象。为了使消息能在分布式系统之间进行传递和处理,则要求消息能在这两种形态之间进行转换,而且这种转换必须是无损的和可逆的。所谓“无损”,指的是在转换的过程中不会遗失任何信息;所谓“可逆”,指的是消息从一种形态转换到另一种形态,然后再从另一种形态转换到原来的形态,所得到的结果应该跟初始状态完全相同。
我们将消息对象转换为字节流的过程称为“消息序列化”,将字节流转换为消息对象的过程称为“消息解析”。
在通信框架内部识别一个消息非常容易,因为它们就是一个个的对象实例;而在网络上识别一个消息就要复杂很多,你需要判断出哪个字节是一个消息的开始,又到哪个字节是一个消息的结束。
三. 消息协议
通过网络相互通信的系统之间要想正常交互,它们必须有“共同的语言”,这种语言就是消息协议。遵守消息协议的消息才能被我们的系统所理解。
由于系统的底层通信会被ESFramework通信框架所解决,所以框架会给出一个抽象的消息协议(通过接口来展现),其最主要的目的是使通信框架能非常清晰的识别一个单独、完整的网络上的消息。系统中的消息必须遵循这个消息协议。
消息协议主要可以分为两类:文本协议(如基于xml的协议)和流协议(又称为二进制协议)。
1. 文本协议
“文本协议”采用起始标志符和结束标志符的方法来标志一个完整的消息。比如,规定所有的消息都以“#”开始,并且以“#”结束。
如你所想,“文本协议”方案有着天生的缺陷,无论你采用哪个字符(或多个字符)作为起始结束标志,在消息的中间都可能出现同样的字符,如果出现这样的消息,就会导致该消息的识别发生错误,而且如果是在同一个Tcp连接上传输,就很可能导致后续所有消息的识别都出错。所以,如果使用文本协议,一定要保证消息的内容中不要包含标识符,一般可以使用复杂的标识符(连续地多个字符)或者转义符来解决这个问题。
采用标志符来识别消息还有一个缺陷,那就是效率低下。因为我们需要逐个扫描每一个字符来判断它是否是指定的标志符。
幸运的是,我们有更好的办法来解决这个问题,那就是使用“流协议”。
2. 流协议
流协议规定网络上传递的任何一个消息必须符合以下规则:
(1) 消息由“消息头”(Message Header)和“消息体”(Message Body)构成,消息体可以为空。
(2) 如果有消息体,则消息体必须紧接在消息头的尾部。
(3) 消息头中至少包含了一个信息,那就是消息体的长度。
(4) 同一个应用中所有的消息的消息头的长度都是固定的。
我们来分析一下,流协议是如何解决字符协议的缺陷的。当通信框架接收到一个网络消息字节流时,首先判断这个流的长度,如果长度小于应用指定的消息头的固定长度,则丢弃这个消息(或等待后续的字节流进行消息重组),否则解析消息头,并从消息头中获取消息体的长度,如果发现接收的字节流中消息体的长度小于预期的长度时,则丢弃这个消息(或等待后续的字节流进行消息重组),否则就可以对消息体进行解析了。
在采用基于非连接的通信协议(如Udp)时,我们可以直接丢弃不完整的消息;而在采用基于连接的通信协议(如Tcp)时,我们则可能需要对过长的消息进行分裂和对不完整的消息进行重组。由于类似Tcp这样的通信协议会保证数据正确无误地按顺序传送,所以依据上述的解析字节流的过程,通信框架会从字节流中正确地分解出一个个完整的消息,而不会发生错漏。
ESFramework即支持文本协议,也支持流协议,并且ESFramework提供了这两种类型的消息的消息头的实现、以及消息的序列化及消息的解析。使用ESFramework提供的实现,你就再不用为消息的封装、字节数组到消息的相互转换、消息粘包、消息断裂、消息重组等等这些事情而伤脑筋了。
接下来一节,我们将介绍ESFramework对消息相关元素的抽象定义。