时间:2025-07-20 11:05
人气:
作者:admin
在Windows操作系统中,每一个应用程序都是相互独立的,它们拥有独立的内存空间,各个应用程序之间形成一道边界,不能互相访问和操作,这是操作系统为了保护应用程序的安全而设计的。这种看似“井水不犯河水”的设计同样有它的弊端,假如两个应用程序需要相互协作配合才能完成工作,那它们就需要进行通信和数据交互,今天以一个简单的小例子,简述一种基于.NET的进程间通信的方案Remoting技术,仅供学习分享使用,如有不足之处,还请指正。
Remoting是Mircrosoft提供的一种基于.Net框架的分布式处理方式,它允许对象通过应用程序域与另一对象进行交互。在Remoting中,是通过通道(Channel)来实现两个应用程序域之间对象的通信的,如下图所示:

在使用Remoting技术开发应用之前,需要了解Remoting的相关概念和知识点:
在本示例的解决方案中,主要包含4个项目,分别如下所示:
具体项目结构如下图所示:

在Remoting中,客户端调用的是远程对象数据的引用,所以远程对象数据的定义必须在客户端和服务器端保持一致,否则将无法调用。且为了数据的安全性和相关性,我们将远程对象抽离成接口,如下所示:
namespace Okcoder.Remoting.Interface
{
public interface IServerObject
{
Person GetPerson(string name, string sex, int age);
}
}
其中Person对象是我们定义的一个模型类,由于它需要在Remoting客户端和服务器端进行传递,所以需要声明为可序列化,如下所示:
namespace Okcoder.Remoting.Interface
{
[Serializable]
public class Person
{
public string Name { get; set; }
public string Sex { get; set; }
public int Age { get; set; }
public override string ToString()
{
return $"我是{Name},年龄是{Age},我是{Sex}";
}
}
}
为了保护远程对象IServerObject实现方式,我们定义了IServerOjbectFactory接口,它主要实现远程对象的创建,如下所示:
namespace Okcoder.Remoting.Interface
{
public interface IServerObjectFactory
{
IServerObject CreateInstance();
}
}
客户端只关注接口的定义,并不关注接口功能的实现,所以接口实现只在服务端调用,如下所示:
远程对象的实现,它实现IServerObject接口,实现具体的业务功能,由于它需要在Remoting的客户端和服务器端传递引用,所以必须继承自MarshalByRefObject,如下所示:
using Okcoder.Remoting.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Okcoder.Remoting.Imps
{
public class ServerObject:MarshalByRefObject,IServerObject
{
public Person GetPerson(string name,string sex,int age)
{
Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} GetPerson::Name={name},Sex={sex},Age={age}");
Person person = new Person()
{
Name = name,
Sex = sex,
Age = age,
};
return person;
}
}
}
远程对象工厂的实现,它主要用于创建远程对象,同样也需要继承自MarshalByRefObject,如下所示:
using Okcoder.Remoting.Interface;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Okcoder.Remoting.Imps
{
public class ServerObjectFactory :MarshalByRefObject, IServerObjectFactory
{
public IServerObject CreateInstance()
{
Console.WriteLine("创建ServiceObject");
return new ServerObject();
}
}
}
服务端主要创建通道,注册通道,以及注册服务。
TcpChannel channel = new TcpChannel(8080);
ChannelServices.RegisterChannel(channel,false);
注册服务分为两种方式:
服务端激活模式,如下所示:
RemotingConfiguration.RegisterWellKnownServiceType(typeof(ServerObjectFactory), "ServiceMessage", WellKnownObjectMode.Singleton);//Singleton模式
本示例采用TcpChannel,基于Socket进行消息流传递,端口号为8080, 并将ServerObjectFactory注册成服务端激活远程对象,其中WellKnownObjectMode为激活模式,包含SingleTom和SingleCall两种,本示例采用SingleTon模式。
客户端激活模式
对于客户端激活模式,使用的方法又有不同,但区别不大,看了代码就一目了然。
RemotingConfiguration.ApplicationName = "ServiceMessage";
RemotingConfiguration.RegisterActivatedServiceType(typeof(ServerObject));
为什么要在注册对象方法前设置ApplicationName属性呢?其实这个属性就是该对象的URI。对于WellKnown模式,URI是放在RegisterWellKnownServiceType()方法的参数中,当然也可以拿出来专门对ApplicationName属性赋值。而RegisterActivatedServiceType()方法的重载中,没有ApplicationName的参数,所以必须分开。
注销通道
在Remoting中当我们注册通道的时候,就自动开启了通道的监听。而如果关闭了对通道的监听,则该通道就无法接受客户端的请求,但通道仍然存在,如果你想再一次注册该通道,会抛出异常。
IChannel[] channels = ChannelServices.RegisteredChannels;
foreach (var item in channels)
{
if (item.ChannelName == "MyTcp")
{
TcpChannel tcp = (TcpChannel)item;
tcp.StopListening(null);
ChannelServices.UnregisterChannel(item);
}
}
客户端主要做两件事,一是注册通道。Remoting中服务器端和客户端都必须通过通道来传递消息,以获得远程对象。第二步则是获得该远程对象。
注册通道,在客户端定义通道不需要传递端口号,因为在获取对象时会的URI中包含端口号,如下所示:
TcpChannel channel = new TcpChannel();
ChannelServices.RegisterChannel(channel,false);
对于服务端激活模式,获取远程对象,通过Activator对象的GetOjbect方法获取远程对象。第一个参数为远程对象类型,第二个参数为远程对象URI。本示例获取IServerObjectFactory的远程对象,如下所示:
IServerObjectFactory serverObjectFactory = (IServerObjectFactory)Activator.GetObject(typeof(IServerObjectFactory), "tcp://localhost:8080/ServiceMessage");//WellKnown激活模式
IServerObject serverObject = serverObjectFactory.CreateInstance();
Person person = serverObject.GetPerson("okcoder", "male", 28);
Console.Write($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} 当前获取的对象为:{person.ToString()}");
获取远程对象后,即可获取服务端远程对象的引用,然后就可以调用远程对象的方法,就像操作本地对象一样。本示例先调用serverObjectFactory的CreateInstance方法获取远程对象,再调用ServerObject的GetPerson方法获取对象。
对于客户端激活模式,获取IServerObjectFactory远程对象的引用,如下所示:
object[] attrs = { new UrlAttribute("tcp://localhost:8080/ServiceMessage") };
object[] objs = new object[0];
IServerObjectFactory serverObjectFactory = (IServerObjectFactory)Activator.CreateInstance(typeof(IServerObjectFactory), objs, attrs);
当然也可以通过如下方式:
object[] attrs = { new UrlAttribute("tcp://localhost:8080/ServiceMessage") };
ObjectHandle objHandle = Activator.CreateInstance("Okcoder.Remoting.Interface", "Okcoder.Remoting.Interface.IServerObjectFactory", attrs);
IServerObjectFactory serverObject3 = (IServerObjectFactory)objHandle.Unwrap();
注册不同通道,在注册通道时,还可以为通道指定名称,端口号,所传递消息流的协议等具体内容:
//注册Tcp通道
IDictionary tcpProp = new Hashtable();
tcpProp["name"] = "tcp9090";
tcpProp["port"] = 9090;
IChannel channel1 = new TcpChannel(tcpProp, new BinaryClientFormatterSinkProvider(), new BinaryServerFormatterSinkProvider());
ChannelServices.RegisterChannel(channel1, false);
//注册Http通道
IDictionary httpProp = new Hashtable();
httpProp["name"] = "http8080";
httpProp["port"] = 8080;
IChannel channel2 = new HttpChannel(httpProp, new SoapClientFormatterSinkProvider(), new SoapServerFormatterSinkProvider());
ChannelServices.RegisterChannel(channel2, false);
在本实例中,服务端通常是长时间运行,供客户端随时调用,一般部署成后台服务,或宿主到指定容器,或命令行窗口的形式等内容,如下所示:

客户端通常是其他形式的应用程序,如控制台,WinForm,WPF等,如下所示:

如上图所示,当客户端调用时,服务器端会根据访问的URI返回指定的远程对象引用,则表示成功。
需要注意的是,Remoting技术是基于.NET Framework框架的分布式数据处理框架;在最新的.NET框架中,则已经不再使用此技术,如果实现此类功能,则可以采用SignalR,GRPC等技术。
以上就是《推荐一款基于.NET的进程间通信框架》的全部内容,旨在抛砖引玉,一起学习,共同进步。
作者:老码识途
出处:http://www.cnblogs.com/hsiang/
本文版权归作者和博客园共有,写文不易,支持原创,欢迎转载【点赞】,转载请保留此段声明,且在文章页面明显位置给出原文连接,谢谢。
关注个人公众号,定时同步更新技术及职场文章
Microsoft Agent Framework Skills 执行 Scripts(实
EF Core 原生 SQL 实战:FromSql、SqlQuery 与对