ESFramework 开发手册(09) -- ACK机制、同步调用、回复异步调用
正如ESFramework 开发手册(01) -- 发送和处理信息一文中所介绍的,我们在客户端使用ICustomizeOutter接口的Send方法,可以给服务端或其它在线客户端发送自定义信息,那么,如何得知接收方是否已经收到了我们发出的信息了呢?特别是针对一些非常重要的信息,确认对方已经收到是非常重要的。ICustomizeOutter接口的SendCertainly方法就使用了带ACK机制的发送。
1.启用ACK机制
ACK,即确认的意思。当我们发送一个自定义信息给对方时,对方收到信息后,回复一个ACK给我们,我们接收到了ACK,就知道对方一定收到信息了。
(1)无论是客户端发送信息给服务端、还是客户端发送信息给其它客户端(ICustomizeOutter接口的SendCertainly方法),或者是服务端发送信息给客户端(ICustomizeController接口的SendCertainlyToLocalClient方法),它们采用的ACK机制的原理是一样的。
(2)当框架接收到需要ACK的信息时,会自动回复ACK给发送方。这是由ESPlus底层自动完成的,应用程序不需要关心。
(3)ESPlus会先回复ACK,然后才会调用处理信息的方法。所以,当发送方接收到ACK回复时,只是意味着接收方已经接收到了信息,并不表示接收方已经处理完了信息。
(4)如果接收方在规定的时间内(默认为30秒)都没有收到ACK,则会抛出超时的异常。当然,在规定的时间内没有收到ACK,并不一定就是接收方没有收到信息,而是有几种可能性:
- 由于网络慢,导致ACK延迟抵达发送方。
- 接收方已经掉线。
- 发送方已经掉线。
(5)SendCertainly方法和SendCertainlyToLocalClient方法采用的是阻塞模型,即只有收到ACK后才会返回,否则一直阻塞当前调用线程。如果既需要ACK机制的发送,又不希望阻塞当前线程,那么,可以异步调用SendCertainly和SendCertainlyToLocalClient方法,并通过回调获知是否有超时异常。
2.信息同步调用
我们首先介绍一下什么是“信息同步调用”?所谓同步调用,就是在调用线程中返回调用结果。
以客户端与服务端进行交互时最常见的一种情况为例:客户端发送一个请求给服务端,服务端处理后,返回回复消息。比如像这样,服务端提供一个加法运算的服务,客户端请求加法服务的消息类型为1001,消息体中包含加法运算所需的两个参数;服务端计算完成后给出的回复消息的类型为1002,回复消息的消息体中包含加法运算的结果。
站在客户端的角度,来看请求消息与应答消息:客户端在调用线程中向服务器发送请求消息,而在接收线程中收到服务器的回复消息,通常,调用线程与接收线程肯定不是同一个线程,所以,从最原始来说,请求消息与回复消息位于不同的线程中。这种模式更像是方法的异步调用。异步调用的好处是当前调用线程不会阻塞,而同步调用的好处是编程模型非常直观简单。毫无疑问,在通信框架中,原始的模型就是异步调用模型,而ESFramework也增加了同步调用的机制,使得编程模型更加丰富。使用者可以根据需要使用合适的模型。
使用自定义信息,我们有几个同步调用的方法,比如ICustomizeOutter接口的Query方法以及ICustomizeController接口的QueryLocalClient方法,这些同步调用的方法都是有返回值的,如果超时没有收到返回的信息,将抛出超时异常。
同步调用与带ACK机制的发送采用的是完全相同的模型,我们完全可以使用同步调用来模拟ACK机制,比如,需要确认的信息就使用同步调用发送,接收方在处理同步调用的时候直接返回null,就可以达到同样的效果。
但是,同步调用与带ACK机制的发送还是有两点小区别:
- 在同步调用中,接收方回复的是对应请求的答案;在带ACK机制的发送中,接收方回复的是ACK。
- 在同步调用中,接收方是处理完信息后才回复;在带ACK机制的发送中,接收方则是先回复ACK,再处理收到信息。
由于同步调用和带ACK机制的发送都有可能超时抛出异常,所以,我们在程序中应当将其try...catch起来,以防止应用程序抛出未被截获的异常。
3.回复异步调用
最后,我们来看看回复异步调用以及其使用场景。
ICustomizeOutter还有一个重载的Query方法,用于回复异步调用:
void Query(string targetUserID, int informationType, byte[] info, CallbackHandler handler, object tag);
如果调用该Query方法时,则当前线程并不阻塞以等待回复,而是继续向下执行,当接收线程接收到Query的回复信息时,会直接(在接收线程中)回调CallbackHandler委托指向的方法。这就是所谓的“回复异步调用”。
那么,何时使用回复异步调用了?
举个很常见的例子。比如,我们的C/S系统的客户端提供一个查询报表的服务,比较直观的方式当然是使用同步调用:客户端点击界面上的“查询”按钮,发出同步调用,等结果返回时刷新界面显示。很简单,是吧?但是这样做有个问题。假设,服务端生成报表比较复杂,需要较长的时间,又或者,网络状况不好,延时比较大,那么当用户点击按钮后,整个界面就“卡死”在哪里了,无法进行其它的操作,直到结果返回之前,界面一直不能操作。这I就严重地影响了用户体验。
这种情况下,使用回复异步调用就是更好的方式了。当客户端点击界面上的“查询”按钮,采用回复异步调用的模式发出请求,UI线程不会阻塞,而是继续向下执行,所以界面不会出现“卡”的现象。当回复到来时,目标委托方法被回调,在委托方法中通过Control.Invoke将回复的结果数据转发到UI线程进行显示。
同步调用以及回复异步调用,作为两种互补的方式而存在,具体使用那种模型,需根据您的具体需求来定夺。
-----------------------------------------------------------------------------------------------------------------------------------------------
关于ESFramework的任何问题,欢迎联系我们:
电话:027-87638960
Q Q:372841921