【IT168技术】在《通过一个模拟程序让你明白ASP.NET MVC是如何运行的》一文中我通过一个普通的ASP.NET Web程序模拟了ASP.NET MVC的执行流程,现在我们通过类似的原理创建一个用于模拟WCF服务端和客户端工作原理的模拟程序。
相关阅读:
基本的组件和执行流程
我们只模拟WCF完成一个简单的服务调用所必需的组件和流程,下图反映了进行服务调用的必要步骤和使用的相关WCF组件。
下面列出了服务端涉及的组件和流程:
①请求消息的接收和回复消息的发送:服务端在传输层监听与接收来自客户的请求,并将经过编码后的回复消息通过传输层发送到客户端;
②请求消息的解码和回复消息的编码:将接收到的字节数组通过解码生成请求消息对象,并将回复消息通过编码转化成字节数组。消息的编码和解码通过消息编码器(MessageEncoder)完成,而消息编码器工厂(MessageEncoderFactory)负责创建该对象;
③请求消息的反序列化和回复消息的序列化:对请求消息进行反序列化,为服务操作的执行生成相应的输入参数,以及将服务操作执行的结果(返回值或输出/引用参数)序列化,并生成回复消息。序列化和反序列化通过分发消息格式化器(DispatchMessageFormatter)完成;
④服务对象的创建:创建或激活服务对象实例,实例提供者(InstanceProvider)用于服务对象的创建或获取,本例直接通过反射创建服务实例;
⑤服务操作的执行:调用创建的服务对象的操作方法,并传入经过反序列化生成的输入参数。操作调用器(OperationInvoker)完成对服务操作的最终执行。
相较于服务端的请求监听、消息接收、服务实例激活和操作调用流程,客户端的处理流程显得相对简单,仅仅包含以下3个必需的步骤:
①请求消息的序列化和回复消息的反序列化:生成请求消息并将输入参数序列化到请求消息中,以及对回复消息进行反序列化,转化成方法调用的返回值或输出/引用参数。序列化和反序列化通过ClientMessageFormatter完成;
②请求消息的编码和回复消息的解码:对请求消息进行编码生成字节数组供传输层发送,以及将传输层接收到的字节数组解码生成回复消息。消息的编码和解码通过消息编码器完成,而消息编码器工厂负责创建该对象;
③请求消息的发送和回复消息的接收:在传输层将经过编码的请求消息发送到服务端,以及接收来自服务端的回复消息。
本实例的解决方法依然采用包含Service.Interface、Service和Client三个项目的结构,不过Service项目现在是一个Web应用。也就是说我们通过一个Web应用的方式实现WCF端对服务调用请求的整个处理流程。
创建自定义HttpHandler实现对服务调用请求的处理
对于一个ASP.NET Web应用来说,对请求的处理最终都落实到一个具体的HttpHandler对象上,所以我们通过实现接口System.Web.IHttpHandler自定义了如下一个WcfHandler用于处理针对WCF服务请求的处理。
2: {
3: //其他成员
4: public Type ServiceType { get; private set; }
5: public MessageEncoderFactory MessageEncoderFactory { get; private set; }
6: public IDictionary<string, MethodInfo> Methods { get; private set; }
7: public IDictionary<string, IDispatchMessageFormatter> MessageFormatters { get; private set; }
8: public IDictionary<string, IOperationInvoker> OperationInvokers { get; private set; }
9:
10: public bool IsReusable
11: {
12: get { return false; }
13: }
14:
15: public WcfHandler(Type serviceType, MessageEncoderFactory messageEncoderFactory)
16: {
17: this.ServiceType = serviceType;
18: this.MessageEncoderFactory = messageEncoderFactory;
19: this.Methods = new Dictionary<string, MethodInfo>();
20: this.MessageFormatters = new Dictionary<string, IDispatchMessageFormatter>();
21: this.OperationInvokers = new Dictionary<string, IOperationInvoker>();
22: }
23: }
如上面代码所示,上述的关于WCF服务端框架所需的组件以只读属性的方式体现在WcfHandler上。ServiceType属性表示服务的类型,基于这个类型通过反射创建服务实例。消息编码器工厂通过MessageEncoderFactory属性表示,两个字典类型的属性MessageFormatters和OperationInvokers代表基于操作的分发消息格式化器和操作调用器列表,字典的Key为操作请求消息的
针对WCF服务的请求处理实现在如下的ProcessRequest方法中,执行的逻辑也不算复杂。我们直接通过消息编码器工厂创建的消息编码从当前HTTP请求的输入流中读取出消息。然后根据当前消息的
接着直接通过反射的方式根据服务类型创建服务实例对象。同样根据当前消息的
操作的执行结果通过分发消息格式化器进行序列化生成的消息最终通过消息编码器写入当前HTTP回复的输出流中返回给客户端。
2: {
3: //其他成员
4: public void ProcessRequest(HttpContext context)
5: {
6: //对HttpPRequest进行解码生成请求消息对象
7: Message request = this.MessageEncoderFactory.Encoder.ReadMessage(context.Request.InputStream, int.MaxValue, "application/soap+xml; charset=utf-8");
8:
9: //通过请求消息得到代表服务操作的Action
10: string action = request.Headers.Action;
11:
12: //通过Action从MethodInfo字典中获取服务操作对应的MethodInfo对象
13: MethodInfo method = this.Methods[action];
14:
15: //得到输出参数的数量
16: int outArgsCount = 0;
17: foreach (var parameter in method.GetParameters())
18: {
19: if (parameter.IsOut)
20: {
21: outArgsCount++;
22: }
23: }
24:
25: //创建数组容器,用于保存请求消息反序列后生成的输入参数对象
26: int inputArgsCount = method.GetParameters().Length - outArgsCount;
27: object[] parameters = new object[inputArgsCount];
28: try
29: {
30: this.MessageFormatters[action].DeserializeRequest(request, parameters);
31: } 32: catch 33: {}
34:
35: List<object> inputArgs = new List<object>();
36: object[] outArgs = new object[outArgsCount];
37: //创建服务对象,在WCF中服务对象通过InstanceProvider创建
38:
39: object serviceInstance = Activator.CreateInstance(this.ServiceType);
40:
41: //执行服务操作
42: object result = this.OperationInvokers[action].Invoke(serviceInstance,parameters, out outArgs);
43:
44: //将操作执行的结果(返回值或者输出参数)序列化生成回复消息
45: Message reply = this.MessageFormatters[action].SerializeReply(request.Version, outArgs, result);
46: context.Response.ClearContent();
47: context.Response.ContentEncoding = Encoding.UTF8;
48: context.Response.ContentType = "application/soap+xml; charset=utf-8";
49:
50: //对回复消息进行编码,并将编码后的消息通过HttpResponse返回
51: this.MessageEncoderFactory.Encoder.WriteMessage(reply, context.Response.OutputStream);
52: context.Response.Flush();
53: }
54: }