上一篇ESFramework扩展之EsfP2P -- 基于ESFramework的P2P实现 主要介绍了EsfP2P如何协助建立P2P Session,那只是整个故事的一半,EsfP2P主要职能的另一半是确保P2P消息的可靠传递。我们已经知道,目前的EsfP2P扩展的实现是基于UDP的NAPT穿透(以后还会加入基于TCP的NAPT穿透),而UDP是不可靠的,在传递的过程中可能会出现丢包、错包等情况,EsfP2P扩展在UDP之上进行了增强来保证P2P消息的可靠传递。这种可靠性需要解决三个主要问题,将分别阐述如下。
1.确保所有的P2P消息都能被对方接收到,并且处理正确的消息。
首先,要保证收到的消息是完整的。由于ESFramework的网络(Tcp/Udp)组件在接收消息时,会会消息的完整性进行检查,如果消息不完整或不满足验证规则,则将丢弃。这就保证了,消息分派器处理的始终都是完整正确的消息。
其次,EsfP2P通过加入ACK机制来确保所有的消息都能被对方接收到。当一个P2P消息通过P2PChannel传递时,会将MessageHeader中的TransByP2PChannel属性设为true,接收方一旦接收到一个消息并发现其该属性为true,就发送一个Ack消息给P2P消息的发送方。Ack消息与P2P消息的一一对应是通过MessageHeader中的CorrelationID相同来判定的。
每个TransByP2PChannel属性设为true的P2P消息在发送后都会寄存在一个Hashtable中,当收到ACK时,会从这个Hashtable中删除对应的P2P消息;否则,将定时重发这个P2P消息,如果超过了最大重发次数,仍然没有收到这个消息对应的Ack,则将触发P2PMessageLost事件,应用可以预定这个事件,在事件处理函数中,可以再次通过服务器来转发这个发送失败的P2P消息。
完成定时重发P2P消息的组件是IP2PAckManager。
再看接收方,我们需要再一个地方来发送Ack消息,从下面的ESFramework的消息流图中可以看到,最佳的位置在IGatewayMessageSpy处。
所以EsfP2P提供了一个P2PAckGatewayMessageSpy组件来完成这件事情。
2.如果P2P消息之间有顺序依赖,则确保消息按发送顺序到达对方
有些P2P消息之间可能有顺序,必须按指定的顺序传递,如果前面的一个消息没有发送成功,则不能发送后面的消息。比如文件的分包传递,必须按顺序传递这些包(当然也可以不按顺序传递,但是需要在接收方进行消息排序、重组,这对实现的要求比较高)。EsfP2P通过扩展MessageHeader,在MessageHeader中添加DependentMessageCoreID属性,来表明本消息所依赖消息的CorrelationID,如果DependentMessageCoreID为0,则表示没有依赖。IP2PMessageHeader是对ESFramework中的IMessageHeader的扩展。
P2PChannel在发送P2P消息前会检查DependentMessageCoreID属性,来判定是否有顺序依赖。如果有顺序依赖,则P2PChannel询问IP2PAckManager所依赖的消息是否已经发送成功,如果还未发送成功,则将等待一段时间再次询问,直到所依赖的消息发送成功或者超时才发送这个P2P消息。
3.接收方能从收到的消息中辨认并过滤掉重复的消息
辨别重复消息的最佳位置当然还是在IGatewayMessageSpy处,所以EsfP2P在P2PAckGatewayMessageSpy组件内部实现了重复消息的辨认和过滤功能。辨别重复消息的方法是,使用一个固定大小的Queue,每来一个消息,就将其标志(每个消息的标志是唯一的)压入到Queue中。新收到消息时,就在Queue中寻找是否有该消息的标志,如果有,则表明为重复的消息,将其丢弃。P2PAckGatewayMessageSpy.CheckRepeated属性控制是否开启重复消息检查、过滤。
当拥有了可靠的P2P通信后,我们IM系统常见的文件传递也可以通过P2P进行,而不需要通过服务器中转,从而进一步减轻了服务器的压力。使用P2P,我们在文字聊天的同时可以开视频、音频,同时还可以传递多个文件,如下图Demo所示:
转到:ESFramework体系 (序)