前言
------------------
本文介绍了一种全新的调用远程代码的技术。参考了微软的remoting、webservice。
基础知识
------------------
先抛开具体的代码,如果要实现远程代码调用,一个最简单的模型是:
1. 使用一个HttpHandler,当有请求的时候,调用对应的代码,返回。例如:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
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
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
{
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);
}
然后本地实现远程调用:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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
这里贴出核心代码的一个例子:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
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( 1 , null , 0 , null , 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( 1 , 2 ));
// 输出 = 1
}
}
}
这篇文章讲解了如何实现一个接口的透明代理。本质在
这里,对用户调用的方法进行序列化操作。
第二步,调用的序列化。
上文透明代理通过以下代码获取了用户调用的方法反射
MethodInfo method = methodCall.MethodBase as MethodInfo;
这里,要对调用方法MethodInfo进行序列化。当然,就是自己去建立一个MethodInfo的xml描述,例如:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
< 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,然后获取对象即可。具体涉及到了一些反射的操作:
Type type = assembly.GetType(typename);
MethodInfo method = type.GetMethod(methodname);
获取了MethodInfo之后,只要把参数放入,获取返回值即可。
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![ExpandedBlockStart.gif](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
ConstructorInfo constructorInfo =
type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance,
null , new Type[] { }, null );
object remoteObject = constructorInfo.Invoke( new object [] { });
// 调用这个对象的方法 这里省略了如何获取parameters过程
object returnvalue = method.Invoke(remoteObject, parameters);
第四步 Handler序列化返回值为XML,返回本地。
只要把returnvalue序列化为xml即可。具体就不叙述了。
第五步 本地透明代理解析XML,获取返回值。
本地透明代理把序列化的returnvalue再反序列化为对象即可,然后返回
难点讲解
------------------
1. 整个调用过程最难的地方在于序列化操作。因为微软不支持接口的序列化、不支持内部类的序列化。这里需要自己实现。
2. 其次最难的在于值类型的操作。因为值类型进入了RealProxy之后,全部被装箱成为了对象(object)。这个时候直接把对象返回会抛异常,因此需要根据具体的method.ReturnType, 逐一用值类型解析返回。
3. 再次,就是动态加载问题。Assembly.LoadFrom会有很多问题,比如版本问题、路径问题。因此要实现一个事件
实现了这个event之后,能够代码指定搜索assembly的位置。具体代码我就不列举了。
后记
------------------
写代码的过程,和拼装模型是一样的。只要我们手上的零件越来越多,能实现的功能和效果就越来越多!