zoukankan      html  css  js  c++  java
  • 介绍一下我自己开发的全新Remoting技术。(本地调用远程代码)

    前言

    ------------------

    本文介绍了一种全新的调用远程代码的技术。参考了微软的remoting、webservice。  

     

    基础知识

    ------------------

    先抛开具体的代码,如果要实现远程代码调用,一个最简单的模型是:

    1. 使用一个HttpHandler,当有请求的时候,调用对应的代码,返回。例如:

    代码
        class RemoteHandler : IHttpHandler, IReadOnlySessionState
        {
            
    public bool IsReusable
            {
                
    get
                {
                    
    return true;
                }
            }

            
    public void ProcessRequest(HttpContext context)
            {
                  
    //这里创建实例RemotingGreeting
                    
                    RemotingGreeting greeting 
    = new RemotingGreeting();

                   
    byte[] response = greeting.Helloworld();


                  
    //这里返回调用结果到客户端

                  context.Response.Clear();

                  context.Response.ContentType 
    = "application/octet-stream";

                  BinaryWriter writer 
    = new BinaryWriter(context.Response.OutputStream);

                  writer.Write(response);

                  writer.Flush();

                  writer.Close();

                context.Response.End();              
            }
        }

    2. 客户端使用Http去访问这个Handler,就实现了最原始的远程调用

     

    这段代码,就实现了远程调用RemotingGreeting这个类,获取方法Helloworld();的返回值。

     

    那么,这个过程如何实现通用呢?如何实现框架化?首先先看看实际代码的调用效果:

     

    代码实例 

    ------------------ 

    首先声明一个被远程调用的对象,RemotingGreeting. 以及一个接口IRemotingGreeing

    代码
        class RemotingGreeting : IRemotingGreeting
        {
            
    public string Greeting(string message)
            {
                
    return "Hi! " + message;
            }
        }

        [Remote(
    "Pixysoft.Framework.Remoting.Demo""Pixysoft.Framework.Remoting.Demo.RemotingGreeting")]//这里实际指定了接口具体实现的类的Assembly和Type
        
    public interface IRemotingGreeting
        {
            
    string Greeting(string message);
        }

     

    然后本地实现远程调用:

    代码
    using System;
    using System.Collections.Generic;
    using System.Text;

    namespace Pixysoft.Framework.Remoting.Demo
    {
        
    class testcase
        {
            
    public void test()
            {
                
    //指定了调用的入口点url
                string url = "http://localhost:1300/Apis/remoting.asmx";

                
    //创建本地调用的透明代理
                IRemoteChannel<IRemotingGreeting> channel = RemotingManager.CreateRemoteChannel<IRemotingGreeting>(url);

                
    //登录远程服务器
                channel.Login("xxxxxx""xxxxxxxxx");

                
    //远程调用
                string greeting = channel.RemoteProxy.Greeting("pixysoft");

                
    //登出
                channel.Logout();

                
    //打印结果,就是“Hi!pixysoft”
                Console.WriteLine(greeting);
            }
        }
    }

     

    正文 

    ------------------ 

    远程调用框架的思路是:

    1. 本地创建一个透明代理(RealProxy.GetTransparentProxy())

    2. 用户本地的请求,被透明代理序列化为XML

    3. XML传递到服务器的Handler,被解析后,加载对应的对象(Spring? 动态加载)

    4. Handler运行对象,获取返回值,再序列化为XML,返回本地。

    5. 本地透明代理解析XML,获取返回值。

     

     

    第一步,创建透明代理。请各位先阅读一篇相关的文章:

    http://www.cnblogs.com/zc22/archive/2010/02/22/1671557.html

    这里贴出核心代码的一个例子:

    代码
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.Remoting.Proxies;
    using System.Runtime.Remoting.Messaging;

    namespace Pixysoft.Framework.TestDrivens
    {
        
    public class Mock<TInterface> : RealProxy
        {
            
    public Mock()
                : 
    base(typeof(TInterface))
            {
            }

            
    public TInterface Value
            {
                
    get
                {
                    
    return (TInterface)this.GetTransparentProxy();
                }
            }

            
    public override IMessage Invoke(IMessage msg)
            {
                IMethodCallMessage methodCall 
    = msg as IMethodCallMessage;

                
    //我返回int = 1

                
    return new ReturnMessage(1null0null, methodCall);
            }
        }

        
    public interface IMock
        {
            
    int Devide(int a, int b);
        }

        
    public class testrealproxy //测试代码在这里!!!
        {
            
    public void test()
            {
                IMock mock 
    = new Mock<IMock>().Value;

                Console.WriteLine(mock.Devide(
    12));

                
    //输出 = 1
            }
        }
    }

    这篇文章讲解了如何实现一个接口的透明代理。本质在

    public override IMessage Invoke(IMessage msg)

    这里,对用户调用的方法进行序列化操作。

     

    第二步,调用的序列化。

    上文透明代理通过以下代码获取了用户调用的方法反射

                IMethodCallMessage methodCall = msg as IMethodCallMessage;

                MethodInfo method 
    = methodCall.MethodBase as MethodInfo;

    这里,要对调用方法MethodInfo进行序列化。当然,就是自己去建立一个MethodInfo的xml描述,例如:

    代码
    <method assembly="Pixysoft.Framework.Remoting" type="Pixysoft.Framework.Remoting.Core.RemotingHelloworld" method="HelloWorld" parametercount="4">
      
    <parameter type="DateTime" parameter="para1">2010-4-12 下午 08:52:21</parameter>
      
    <parameter type="String" parameter="para2">2</parameter>
      
    <parameter type="Int32" parameter="para3">12</parameter>
      
    <parameter type="IRemotingValue" parameter="para4" />
      
    <return type="IRemotingValue" />
    </method>

    这个是我实际建立的MethodInfo的xml描述。如何建立就不说了吧,很简单,用StringBuilder去拼就行了。

     

    第三步,httpHandler解析XML,加载对象运行结果。

    客户端通过HttpPost到服务端,服务端获取了XML之后,只要根据对应的参数加载Assembly,然后获取对象即可。具体涉及到了一些反射的操作:

    Assembly assembly = Assembly.LoadFrom(assemblyname);

    Type type 
    = assembly.GetType(typename);

    MethodInfo method 
    = type.GetMethod(methodname);

    获取了MethodInfo之后,只要把参数放入,获取返回值即可。

    代码
    //实例化一个对象

                ConstructorInfo constructorInfo 
    =
                    type.GetConstructor(BindingFlags.Public 
    | BindingFlags.NonPublic | BindingFlags.Instance,
                    
    nullnew Type[] { }, null);

                
    object remoteObject = constructorInfo.Invoke(new object[] { });

                
    //调用这个对象的方法 这里省略了如何获取parameters过程

                
    object returnvalue = method.Invoke(remoteObject, parameters);

     

    第四步 Handler序列化返回值为XML,返回本地。 

    只要把returnvalue序列化为xml即可。具体就不叙述了。

     

    第五步 本地透明代理解析XML,获取返回值。

    本地透明代理把序列化的returnvalue再反序列化为对象即可,然后返回

    return new ReturnMessage(returnvalue, null0null, methodCall);

     

    难点讲解 

    ------------------ 

    1. 整个调用过程最难的地方在于序列化操作。因为微软不支持接口的序列化、不支持内部类的序列化。这里需要自己实现。

     

    2. 其次最难的在于值类型的操作。因为值类型进入了RealProxy之后,全部被装箱成为了对象(object)。这个时候直接把对象返回会抛异常,因此需要根据具体的method.ReturnType, 逐一用值类型解析返回。

     

    3. 再次,就是动态加载问题。Assembly.LoadFrom会有很多问题,比如版本问题、路径问题。因此要实现一个事件

    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

    实现了这个event之后,能够代码指定搜索assembly的位置。具体代码我就不列举了。

     

    后记 

    ------------------ 

    写代码的过程,和拼装模型是一样的。只要我们手上的零件越来越多,能实现的功能和效果就越来越多!

     

     

  • 相关阅读:
    算法训练 P1103
    算法训练 表达式计算
    算法训练 表达式计算
    基础练习 时间转换
    基础练习 字符串对比
    Codeforces 527D Clique Problem
    Codeforces 527C Glass Carving
    Codeforces 527B Error Correct System
    Codeforces 527A Glass Carving
    Topcoder SRM 655 DIV1 250 CountryGroupHard
  • 原文地址:https://www.cnblogs.com/zc22/p/1710493.html
Copyright © 2011-2022 走看看