.NET Remoting是在不同应用程序域之间通信的技术,它可以用来访问另一个应用程序域(一个系统的不同进程或者不同系统的进程)中的对象。
让我们从计算个人所得税的简单例子开始认识.NET Remoting技术,对它的构成部分远程对象、服务器、客户机有一个感性的认识,而暂时不考虑Remoting技术细节。下图是示例中用到的类:
1.远程对象:远程对象类Tax继承了基类MarshalByRefObject和接口ITax,代码如下:
//定义接口类ITax
//编译生成ITaxTemoting.dll
//服务器端和客户端都要添加该类dll的引用
public interface ITax
{
double GetTax(int salary);
}
//定义远程对象,必须继承自MarshalByRefObject
//编译生成TaxRemoting.dll,服务器端必须添加该dll的引用
public class Tax:MarshalByRefObject,ITaxTemoting.ITax
{
public Tax()
{
Console.WriteLine("Remoting object Tax activated");
}
//根据薪水计算个人所得税
//公式:个税=(薪水-2000)*税率-速算扣除数
public double GetTax(int salary)
{
double tax = 0; //个人所得税
int balance = salary - 2000; //应纳税所得额
if (balance <= 0)
{
tax = 0;
}
else if (balance <= 500)
{
tax = balance * 0.05;
}
else if (balance <= 2000)
{
tax = balance * 0.10 - 25;
}
else if (balance <= 5000)
{
tax = balance * 0.15 - 125;
}
else if (balance <= 20000)
{
tax = balance * 0.20 - 375;
}
else if (balance <= 40000)
{
tax = balance * 0.25 - 1375;
}
else if (balance <= 60000)
{
tax = balance * 0.30 - 3375;
}
else if (balance <= 80000)
{
tax = balance * 0.35 - 6375;
}
else if (balance <= 100000)
{
tax = balance * 0.40 - 10375;
}
else
{
tax = balance * 0.45 - 15375;
}
Console.WriteLine("计算结果:薪水"+salary.ToString()+"时应缴纳的个人所得税为"+tax.ToString());
return tax;
}
}
//编译生成ITaxTemoting.dll
//服务器端和客户端都要添加该类dll的引用
public interface ITax
{
double GetTax(int salary);
}
//定义远程对象,必须继承自MarshalByRefObject
//编译生成TaxRemoting.dll,服务器端必须添加该dll的引用
public class Tax:MarshalByRefObject,ITaxTemoting.ITax
{
public Tax()
{
Console.WriteLine("Remoting object Tax activated");
}
//根据薪水计算个人所得税
//公式:个税=(薪水-2000)*税率-速算扣除数
public double GetTax(int salary)
{
double tax = 0; //个人所得税
int balance = salary - 2000; //应纳税所得额
if (balance <= 0)
{
tax = 0;
}
else if (balance <= 500)
{
tax = balance * 0.05;
}
else if (balance <= 2000)
{
tax = balance * 0.10 - 25;
}
else if (balance <= 5000)
{
tax = balance * 0.15 - 125;
}
else if (balance <= 20000)
{
tax = balance * 0.20 - 375;
}
else if (balance <= 40000)
{
tax = balance * 0.25 - 1375;
}
else if (balance <= 60000)
{
tax = balance * 0.30 - 3375;
}
else if (balance <= 80000)
{
tax = balance * 0.35 - 6375;
}
else if (balance <= 100000)
{
tax = balance * 0.40 - 10375;
}
else
{
tax = balance * 0.45 - 15375;
}
Console.WriteLine("计算结果:薪水"+salary.ToString()+"时应缴纳的个人所得税为"+tax.ToString());
return tax;
}
}
2.服务器端:定义信道并监听,注册远程对象
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using TaxRemoting;
namespace TaxServer
{
class Server
{
static void Main(string[] args)
{
TcpChannel channel = new TcpChannel(8085);
ChannelServices.RegisterChannel(channel,false);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(TaxRemoting.Tax), "Tax", WellKnownObjectMode.SingleCall);
System.Console.WriteLine("Press Enter key to exit");
System.Console.ReadLine();
}
}
}
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using TaxRemoting;
namespace TaxServer
{
class Server
{
static void Main(string[] args)
{
TcpChannel channel = new TcpChannel(8085);
ChannelServices.RegisterChannel(channel,false);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(TaxRemoting.Tax), "Tax", WellKnownObjectMode.SingleCall);
System.Console.WriteLine("Press Enter key to exit");
System.Console.ReadLine();
}
}
}
3.客户机端:注册信道,根据URL获取远程对象代理,使用代理调用服务器端的远程对象。
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using ITaxTemoting;
namespace TaxClient
{
class Client
{
static void Main(string[] args)
{
TcpChannel channel = new TcpChannel();
ChannelServices.RegisterChannel(channel,false);
ITax obj = (ITax)Activator.GetObject(typeof(ITaxTemoting.ITax), "tcp://localhost:8085/Tax");
if (obj == null)
{
Console.WriteLine("Could not locate TCP server");
}
Console.WriteLine("请输入薪水:");
int salary = int.Parse(Console.ReadLine());
Console.WriteLine("应缴纳的个人所得税为:" + obj.GetTax(salary).ToString());
Console.ReadLine();
}
}
}
using System.Collections.Generic;
using System.Text;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using ITaxTemoting;
namespace TaxClient
{
class Client
{
static void Main(string[] args)
{
TcpChannel channel = new TcpChannel();
ChannelServices.RegisterChannel(channel,false);
ITax obj = (ITax)Activator.GetObject(typeof(ITaxTemoting.ITax), "tcp://localhost:8085/Tax");
if (obj == null)
{
Console.WriteLine("Could not locate TCP server");
}
Console.WriteLine("请输入薪水:");
int salary = int.Parse(Console.ReadLine());
Console.WriteLine("应缴纳的个人所得税为:" + obj.GetTax(salary).ToString());
Console.ReadLine();
}
}
}
运行服务器端和客户端EXE程序,输出结果(上图为服务器端,下图为客户端)如下:
开始学习.NET Remoting时,一直迷惑于客户端和服务器端都要添加远程对象的引用,这样做不是把实现细节都暴露给客户端了吗?而且部署起来也很麻烦,客户端总是需要复制远程对象的程序集,这样做有什么意义呢?随着了解的增多,才逐渐明白:客户端并不需要远程对象的具体实现,因为客户端是通过远程对象代理调用服务器端远程对象方法实现的,客户端需要的仅仅是元数据,因此,我们应该采用面向接口编程的方法,用接口把客户端代码和服务器端代码分开。本文计算个人所得税的示例想要说明的就是这一点,当计算个人所得税的算法发生改变时,只需修改Tax类就可以了,而客户端的代码是不需要改变的,这也是面向接口编程的好处。
源代码下载地址: 点击下载完整示例程序