0. 背景
上一文客户端websocket(C#)长连接及简易RPC框架设计(一)讲述了客户端websocket(C#)
的长连接,以及相关钩子函数的介绍。接下来文本介绍简易RPC框架,设计关系,最后通过一个echo服务来阐述整个由Client-->Server-->Client
调用流程。
1. 简易RPC框架组件介绍
在本文中的简易RPC框架包含七个组件:
- WebSocket实例(
WebSocket
) - 客户端套接字(
ClientSocket
) - 接收消息线程池处理服务(
Notifier
) - 事件分发(
Dispatcher
) - 消息处理(
Handler
) - RPC管理(
RPCManager
) - 消息控制(
MessageController
)
这七个组件有的相互引接收,发送句柄,有的引用组件本身,他们紧密配合,实现了简单的从远程网络中发送和接收数据消息的机制。客户端程序需要配置WebSocket实例的参数,然后新建消息控制MessageController
实例,然后调用该实例的服务端调用客户端接口注册和客户端调用服务端接口:
//服务端调用客户端接口注册API
public void RegisterHandlers(string funName, Function fun)
//客户端调用服务端API
public void CallServerMethod(string[] rpcSeg)
a). 七个组件的UML图
以上的七个组件相互关系如下图1 所示:
图1. 七个组件相互关系图
其中:
实黑箭头线表示组件所有对方组件作为属性,而实红箭头线表示本组件引用对方的组件的属性,红色双向表示组件间各自引用对方某个属性。
WebSocket
类是websocket-sharp.dll
提供的,它的介绍可以参考 客户端websocket(C#)长连接(一);
ClientSocket
是对WebSocket
的四个钩子函数的实现,可以参考 客户端websocket(C#)长连接(一)的叙述,它的主要属性如下图所示:
图2.
ClientSocket
类
Notifier
类是管理了一个线程池,去处理接收到消息,具体可参考 客户端websocket(C#)长连接(一)的叙述和Notifier.cs
代码 [github],它的主要属性如下图所示:
图3.
Notifier
类
接下来我们解释一下Dispatcher
, Handler
, RPCManager
, MessageController
这四个组件的作用。
b). Dispatcher
Dispatcher
的属性如下图所示 [github]:
图4.
Dispatcher
类
Dispatcher
是一个与ClientSocket
与Handler
打交道的类。
它的属性有两个委托recv
, send
和两个句柄函数法dispatchMsg
,sendMsg
。
其中dispatchMsg
是通过ClientSocket
桥接,从Notifier
中是线程池的worker正要处理的消息的接收句柄;且通过recv
委托将整条服务端消息转交给Handler
(最终通知客户端程序);
而sendMsg
是接收Handler
(起初由客户端程序)发送过来的消息的发送句柄,且send
是要将消息直接发送给ClientSocket
的发送委托。
它的作用是负责接收ClientSocket
发来的服务端消息透传交给Handler
处理,并且将Handler
处理后的客户端消息透传给ClientSocket
。
c). Handler
Handler
的属性如下图所示 [github]:
图5.
Handler
类
Handler
是一个与Dispatcher
与RPCManager
打交道的类。
它的属性有两个委托recvHandler
, sendHandler
和两个句柄函数法handleRecv
,handleSend
。
其中handleRecv
是将从Dispatcher
发来的服务端消息,根据自定义协议进行处理(以分割符’#’进行Split
);且通过recvHandler
将处理后的RPC消息协议转交给RPCManager
。
而handleSend
是接收RPCManager
(起初由客户端程序)发送过来的RPC消息协议的发送句柄;且根据自定义协议进行处理(以分割符’#’进行Join
);通过sendHandler
直接发送给Dispatcher
。
它的作用是负责接收Dispatcher
发来的服务端消息经过处理后透传交给RPCManager
处理,并且RPCManager
发来的消息协议进行处理,处理后转给Dispatcher
。
d). RPCManager
RPCManager
的属性如下图所示 [github]:
图6.
RPCManager
类
RPCManager
是一个与Handler
打交道,并且管理所有RPC的方法注册映射。
它的属性有一个委托sendHandler
,一个管理RPC的方法注册映射的Dictionary<string, Function>
的dic
,以及三个函数handle
,RegisterHandlers
,CallServerMethod
。
其中RegisterHandler
是在RPCManager
中进行方法注册,简单地,即是添加一个string_key,Function_value
,放入dict
中,作为后续handle
函数的调用。
函数handle
是将从Handler
发来的RPC协议,提取方法名(string
类型),根据dic
中的映射方法(Function
类型),进而调用客户端接口,并传入相应的参数。
函数CallServerMethod
是为客户端程序调用服务端RPC的API,它通过传入的服务端方法名,和参数,传给RPCManager
,并且通过sendHandler
委托,转交给Handler
进行自定义协议处理,转给Dispatcher
–>ClientSocket
–>Server
。
它的作用是负责接收Handler
发来的经过处理后的消息,然后根据RPC方法映射,调用客户端注册的接口,并且为客户端提供注册方法和直接调用服务端接口的API。
e). MessageController
MessageController
的属性如下图所示 [github]:
图7.
MessageController
类
MessageControllor
是一个为客户端程序服务的类。
它的属性有四个rpcMapper
,handler
, dispatcher
, cs
分别是上述的类RPCManager
, Handler
, Dispatcher
, ClientSocket
实例,并且提供了两个方法:RegisterHandlers
, CallServerMethod
。
在MessageController
的构造函数中,新建RPCManager
, Handler
, Dispatcher
, ClientSocket
实例,并将他们的委托和句柄进行赋值,以达到客户端接收消息和发送消息的流程贯通。
而RegisterHandler
,CallServerMethod
则是代理了RPCManager
中的方法,为客户端程序提供API。
f). 具体的组件关系
更为具体的组件关系如下所示:
图8. 具体的组件关系图
2. Echo例子阐述RPC调用流程
接下来本文通过客户端连接echo服务器,并发送一个消息,来阐述一下客户端调用服务端,服务端再回调给服务端的流程。客户端测试代码如下所示,详见[csdz github]。
public class Program
{
public static void Main(string[] args)
{
Program p = new Program();
MessageController msgCtrl = new MessageController();
msgCtrl.RegisterHandler("RecvCBTest", p.RecvCBTest);
msgCtrl.RegisterHandler("Connected", p.Connected);
msgCtrl.Connect();
msgCtrl.CallServerMethod(new string[] { "RecvCBTest", "I'm a student", "Why not Curry" });
Thread.Sleep(100 * 1000);
}
public void RecvCBTest(string[] msg)
{
for (int i = 0; i < msg.Length; i++)
{
Console.WriteLine("{0} param: {1}", i, msg[i]);
}
}
public void Connected(string[] msg)
{
Console.WriteLine("Client and Server connnected!");
}
}
a). 自定义通信协议
本文的自定义协议非常简单,它是:
- 通过一个字符串
string
的msg
,通过'#'
解析,得到string[]
的rpcSeg
; rpcSeg[0]
为方法名字,rpcSeg[1]~rpcSeg[Length-1]
为参数;- 方法名字通过
Dictionary<string, Function>
映射到对应接口,参数通过string
转成目标类型; - 参数不太适合传递
list,map
,自定义object
等复杂类型,支持基础类型传递。
b). 客户端注册回调函数及连接服务器流程
客户端注册回调函数及连接服务器流程如下:
图9. 客户端注册回调函数及连接服务器流程
c). 客户端调用服务端,服务端Echo返回客户端流程
客户端调用服务端,服务端Echo返回客户端流程如下:
图10. 客户端调用服务端,服务端Echo返回客户端流程
上述的流程已经画的很详细了,这里就不再文字赘述具体的流程细节。
3. 总结
文本主要介绍简易RPC框架以及内部几个组件的相互引用关系,最后通过一个echo服务来阐述整个由Client-->Server-->Client
调用流程。
这里需要注意几点:
- 这里没有涉及压缩/解压缩,加解密等消息处理,可以在
Handler
组件做相应处理; - 客户端发送给服务端消息是同步发送的,可以设计一个消息队列作为缓冲,并且异步发送;
- 这里的自定义协议过于简单,没有考虑复杂类型参数的序列化及反序列,如有需求,也可以自定义设计,或者使用成熟的序列化技术,如
json,protobuf,xml
等; - websocket_msg_dispatcher Demo 并没有考虑过多的错误处理,如有碰到,可以自行添加。