客户端websocket(c#)长连接及简易rpc框架设计(二)

来源:互联网 发布:堕落天使知乎 编辑:程序博客网 时间:2024/05/22 09:40

0. 背景

上一文客户端websocket(C#)长连接及简易RPC框架设计(一)讲述了客户端websocket(C#)的长连接,以及相关钩子函数的介绍。接下来文本介绍简易RPC框架,设计关系,最后通过一个echo服务来阐述整个由Client-->Server-->Client调用流程。

1. 简易RPC框架组件介绍

在本文中的简易RPC框架包含七个组件:

  1. WebSocket实例(WebSocket
  2. 客户端套接字(ClientSocket
  3. 接收消息线程池处理服务(Notifier)
  4. 事件分发(Dispatcher)
  5. 消息处理(Handler
  6. RPC管理(RPCManager)
  7. 消息控制(MessageController

这七个组件有的相互引接收,发送句柄,有的引用组件本身,他们紧密配合,实现了简单的从远程网络中发送和接收数据消息的机制。客户端程序需要配置WebSocket实例的参数,然后新建消息控制MessageController实例,然后调用该实例的服务端调用客户端接口注册和客户端调用服务端接口:

//服务端调用客户端接口注册APIpublic void RegisterHandlers(string funName, Function fun)//客户端调用服务端APIpublic 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是一个与ClientSocketHandler打交道的类。

它的属性有两个委托recv, send和两个句柄函数法dispatchMsgsendMsg

其中dispatchMsg是通过ClientSocket桥接,从Notifier中是线程池的worker正要处理的消息的接收句柄;且通过recv委托将整条服务端消息转交给Handler(最终通知客户端程序);

sendMsg是接收Handler(起初由客户端程序)发送过来的消息的发送句柄,且send是要将消息直接发送给ClientSocket的发送委托。

它的作用是负责接收ClientSocket发来的服务端消息透传交给Handler处理,并且将Handler处理后的客户端消息透传给ClientSocket

c). Handler

Handler的属性如下图所示 [github]:

这里写图片描述
图5. Handler

Handler是一个与DispatcherRPCManager打交道的类。

它的属性有两个委托recvHandler, sendHandler和两个句柄函数法handleRecvhandleSend

其中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,以及三个函数handleRegisterHandlersCallServerMethod

其中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是一个为客户端程序服务的类。

它的属性有四个rpcMapperhandler, dispatcher, cs分别是上述的类RPCManager, Handler, Dispatcher, ClientSocket实例,并且提供了两个方法:RegisterHandlers, CallServerMethod

MessageController的构造函数中,新建RPCManager, Handler, Dispatcher, ClientSocket实例,并将他们的委托和句柄进行赋值,以达到客户端接收消息和发送消息的流程贯通。

RegisterHandlerCallServerMethod则是代理了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). 自定义通信协议

本文的自定义协议非常简单,它是:

  1. 通过一个字符串stringmsg,通过'#'解析,得到string[]rpcSeg;
  2. rpcSeg[0]为方法名字,rpcSeg[1]~rpcSeg[Length-1]为参数;
  3. 方法名字通过Dictionary<string, Function>映射到对应接口,参数通过string转成目标类型;
  4. 参数不太适合传递list,map,自定义object等复杂类型,支持基础类型传递。

b). 客户端注册回调函数及连接服务器流程

客户端注册回调函数及连接服务器流程如下:

这里写图片描述
图9. 客户端注册回调函数及连接服务器流程

c). 客户端调用服务端,服务端Echo返回客户端流程

客户端调用服务端,服务端Echo返回客户端流程如下:

这里写图片描述
图10. 客户端调用服务端,服务端Echo返回客户端流程

上述的流程已经画的很详细了,这里就不再文字赘述具体的流程细节。

3. 总结

文本主要介绍简易RPC框架以及内部几个组件的相互引用关系,最后通过一个echo服务来阐述整个由Client-->Server-->Client调用流程。

这里需要注意几点:

  1. 这里没有涉及压缩/解压缩,加解密等消息处理,可以在Handler组件做相应处理;
  2. 客户端发送给服务端消息是同步发送的,可以设计一个消息队列作为缓冲,并且异步发送;
  3. 这里的自定义协议过于简单,没有考虑复杂类型参数的序列化及反序列,如有需求,也可以自定义设计,或者使用成熟的序列化技术,如json,protobuf,xml等;
  4. websocket_msg_dispatcher Demo 并没有考虑过多的错误处理,如有碰到,可以自行添加。
1 0
原创粉丝点击