,,Microsoft .Net Remoting系列教程之一-.Net Remoting基础篇

,,Microsoft .Net Remoting系列教程之一:.Net Remoting基础篇

本文主要阐述了。Net Remoting,有需要的朋友可以参考一下。

一、Remoting基础

什么是远程处理?简而言之,我们可以把它看作一种分布式处理方法。从微软产品的角度来看,Remoting是DCOM的升级,它改进了许多功能,并很好地集成到了。Net平台。微软。NET远程处理提供了一个框架,允许一个对象通过应用程序域与另一个对象进行交互。这就是我们使用远程处理的原因。为什么?在Windows操作系统中,应用程序被分成单独的进程。这个过程形成了应用程序代码和数据之间的边界。如果不采用进程间通信(RPC)机制,在一个进程中执行的代码就不能访问另一个进程。这是应用程序的操作系统保护机制。但是,在某些情况下,我们需要跨越应用域,与另一个应用域进行通信,也就是跨越边界。

在远程处理中,两个应用程序域之间的对象通信是通过信道实现的。如图所示:

首先,客户端通过远程处理访问信道以获得服务器对象,然后通过代理将其解析为客户端对象。这提供了将服务器对象作为服务发布的可能性。远程对象代码可以在服务器上运行(比如服务器激活的对象和客户端激活的对象),然后客户端通过Remoting连接到服务器,获取服务对象,通过序列化在客户端运行。

在远程处理中,对于要传递的对象,设计者不需要知道数据包的格式,只需要知道信道的类型和端口号。但是,必须注意,当客户端获得服务器端对象时,它并没有获得实际的服务器端对象,而是获得了它的引用。这样既保证了客户端和服务器端的松耦合,又优化了通信性能。

1、Remoting的两种通道

远程处理有两个主要通道:Tcp和Http。英寸Net中,IChannel接口是在system . runtime . remoting . channel中定义的,IChannel接口包括TcpChannel通道类型和Http通道类型。它们分别对应于这两种类型的远程处理信道。

tcpChannel类型放在命名空间system . runtime . Remoting . channel . Tcp中,Tcp channel提供基于套接字的传输工具,并使用Tcp协议跨远程处理边界传输序列化的消息流。TcpChannel类型默认以二进制格式序列化消息对象,因此具有更高的传输性能。HttpChannel类型放在命名空间system . runtime . remoting . channel . http中.它提供了一种使用Http协议的方式,这样它就可以在互联网上通过防火墙传输序列化的消息流。默认情况下,HttpChannel类型使用Soap格式序列化消息对象,因此具有更好的互操作性。通常,在局域网中,我们使用TcpChannel更多时候;如果要穿越防火墙,使用HttpChannel。

2、远程对象的激活方式

在访问远程类型的对象实例之前,必须通过一个称为激活的过程来创建和初始化它。这个客户端通过一个通道创建一个远程对象,这被称为对象的激活。在远程处理中,远程对象的激活可以分为两类:服务器端激活和客户端激活。

(1) 服务器端激活,也叫WellKnow方法,很多都翻译成熟知的对象。为什么叫众所周知的对象激活模式?因为在激活对象实例之前,服务器应用程序将在众所周知的统一资源标识符(URI)上发布此类型。然后,服务器进程将为此类型配置一个已知的对象,并根据指定的端口或地址发布该对象。Net Remoting将服务器端激活分为单一模式和单一调用模式。

单例模式:这是一种有状态模式。如果设置为单独激活模式,远程处理将为所有客户端建立相同的对象实例。当对象处于活动状态时,单例实例将处理所有后续的客户端访问请求,不管它们是同一个客户端还是其他客户端。单例实例将在整个方法调用过程中保持其状态。例如,如果远程对象有一个累积方法(I=0;I),由多个客户端(例如两个)调用。如果设置为SingleTon模式,第一个客户得到值1,第二个客户得到值2,因为他们得到相同的对象实例。如果我们熟悉Asp.Net的状态管理,我们可以把它看作是一个应用程序状态。

SingleCall模式:SingleCall是一种无状态模式。一旦设置为SingleCall模式,当客户端调用远程对象的方法时,远程处理将为每个客户端建立一个远程对象实例,对象实例的销毁将由GC自动管理。和前面的例子一样,访问远程对象的两个客户都得到1。我们还是可以借鉴Asp的。Net的状态管理,并认为它是一个会话状态。

(2) 客户端激活。与众所周知的模式不同,远程处理在激活每个对象实例时,为每个客户端激活的类型分配一个URI。一旦客户端激活模式得到客户端的请求,它将为每个客户端建立一个实例引用。Lecall模式和客户端激活模式之间存在差异:首先,对象实例是在不同的时间创建的。客户端的激活方式是,一旦客户端发出调用请求,就被实例化;在调用object方法之前,不会创建SingleCall。其次,SingleCall模式激活的对象是无状态的,对象生命周期的管理由GC管理,而客户端激活的对象是有状态的,其生命周期是可以定制的。第三,这两种激活模式在服务器端和客户端以不同的方式实现。尤其是在客户端,SingleCall模式是由GetObject()激活的,它调用对象的默认构造函数。客户端激活模式由CreateInstance()激活,它可以传递参数,因此您可以调用自定义构造函数来创建实例。

二、远程对象的定义

如前所述,当客户端获取服务器端对象时,它并不获取实际的服务器端对象,而是获取其引用。因此,在远程处理中,远程对象需要遵循一些必要的定义规范。

因为远程处理传递的对象是通过引用传递的,所以传递的远程对象类必须继承MarshalByRefObject。MSDN对MarshalByRefObject的描述是,MarshalByRefObject是那些通过使用代理交换消息来跨越应用程序域边界进行通信的对象的基类。不从MarshalByRefObject继承的对象通过值隐式封送。当远程应用程序引用由值封送的对象时,该对象的副本将跨远程处理边界传递。您需要继承MarshallByRefObject,因为您希望通过代理方法而不是复制方法进行通信。

以下是远程对象类的定义:

公共类server object:MarshalByRefObject

{

公共人物GetPersonInfo(字符串名称,字符串性别,整数年龄)

{

Person person=新人();

人。Name=名称;

人。性=性;

人。年龄=年龄;

退货人;

}

}

这个类只实现了最简单的方法,就是设置一个人的基本信息,返回一个Person类对象。注意这里返回的Person类。因为这里传递的Person是通过传递值来完成的,并且远程处理要求它必须是被引用的对象,所以Person类必须被序列化。

因此,在远程处理远程对象时,如果要调用或传递某个对象(如类或结构),该类或结构必须序列化。

属性[SerializableAttribute]:

[可序列化]

公共阶层的人

{

公众人物()

{

}

私有字符串名称;

私弦性;

私人年龄;

公共字符串名称

{

获取{ return name}

set { name=value}

}

公共字符串性

{

获取{返回性;}

set { sex=value}

}

公共互联网

{

get { return age}

设置{ age=value}

}

}

将远程对象编译成类库形式的Dll。这个Dll将分别放在服务器端和客户端,以添加引用。

可以在远程处理中传递的远程对象可以是各种类型,包括复杂的数据集对象,只要它可以被序列化。远程对象也可以包含事件,但是服务器端以一种特殊的方式处理事件,我将在本系列的第三部分介绍这种方式。

三、服务器端

根据第一部分,根据激活方式的不同,通道类型的不同服务器端实现也是不同的。一般来说,服务器端应该分为三个步骤:

1、注册通道

要跨应用程序域进行通信,必须实现通道。如前所述,Remoting提供了IChannel接口,该接口包括两种类型的通道,分别是TcpChannel和HttpChannel。除了序列化数据的性能和格式不同之外,这两种类型的实现方式完全相同,所以我们以TcpChannel为例。

要注册TcpChannel,首先添加一个引用“System。然后使用命名空间:system . runtime . Remoting . channel . TCP。代码如下:

TcpChannel channel=新TcpChannel(8080);

频道服务。RegisterChannel(频道);

当实例化通道对象时,将端口号作为参数传递。然后调用静态方法RegisterChannel()来注册通道对象。

2、注册远程对象

注册通道后,要激活远程对象,必须在通道中注册该对象。根据激活模式的不同,注册对象的方法也不同。

(1) SingleTon模式

已知对象可以通过静态方法remoting configuration . registerwellknownservicetype()实现:

正在移除配置。RegisterWellKnownServiceType(

typeof(ServerRemoteObject。ServerObject),

' ServiceMessage ',WellKnownObjectMode。独生子女);

(2)SingleCall模式

注册对象的方法和SingleTon模式基本相同,只需将枚举参数WellKnownObjectMode改为SingleCall即可。

正在移除配置。RegisterWellKnownServiceType(

typeof(ServerRemoteObject。ServerObject),

' ServiceMessage ',WellKnownObjectMode。single call);

(3)客户端激活模式

对于客户端激活模式,使用的方法不同,但差别不大。看完代码一目了然。

正在移除配置。application name=' service message ';

正在移除配置。注册激活服务类型(

typeof(ServerRemoteObject。server object));

为什么要在注册对象方法之前设置ApplicationName属性?实际上,这个属性是对象的URI。对于公知模式,URI放在RegisterWellKnownServiceType()方法的参数中,当然也可以拿出来给ApplicationName属性赋值。但是RegisterActivatedServiceType()方法的重载没有ApplicationName的参数,所以必须分开。

3、注销通道

如果要关闭远程处理的服务,需要注销信道,或者可以关闭对信道的监视。在远程处理中,当我们注册信道时,我们会自动打开对信道的监视。如果关闭了对通道的监视,则通道无法接受客户端的请求,但通道仍然存在。如果要再次注册通道,将会抛出异常。

//获取当前注册的频道;

IChannel[]channels=channel services。注册的频道;

//关闭名为MyTcp的指定通道;

foreach(频道中的频道)

{

如果(每个频道。ChannelName=='MyTcp ')

{

TcpChannel TcpChannel=(TcpChannel)每个通道;

//关闭监控;

tcpChannel。停止列表(空);

//注销通道;

频道服务。unregister channel(tcpChannel);

}

}

代码,RegisterdChannel属性获取当前注册的通道。在远程处理中,允许同时注册多个通道,这将在后面解释。

四、客户端

客户端主要做两件事,一是注册通道。这可以从图1中看出。在远程处理中,服务器和客户端都必须通过信道传递消息来获取远程对象。第二步是获取远程对象。

1、注册通道:

TcpChannel channel=new TcpChannel();

频道服务。RegisterChannel(频道);

请注意,当客户端实例化通道时,调用的是默认的构造函数,即不传递端口号。事实上,这个端口号是必不可少的,但是它的名称作为Uri的一部分放在后面。

2、获得远程对象。

和服务器端一样,不同的激活方式决定了客户端的实现会有所不同。然而,这种差异只是众所周知的激活模式和客户端激活模式的差异,客户端实现对于SingleTon和SingleCall模式是完全相同的。

(1) WellKnown激活模式

要在服务器端获得众所周知的远程对象,可以通过Activator进程的GetObject()方法获得它:

ServerRemoteObject。server object server obj=(server remote object。ServerObject)激活器。GetObject(

typeof(ServerRemoteObject。ServerObject),' TCP://localhost:8080/service message ');

首先,它在WellKnown模式下被激活,客户端通过使用GetObject()获取对象。其中第一个参数是远程对象的类型。第二个参数是服务器端uri。如果是http通道,自然是3358 localhost:8080/service message。因为我使用的是本地机器,这里是localhost,您可以用一个特定的服务器IP地址来替换它。端口必须与服务器端的端口一致。后跟服务器定义的远程对象服务名,即ApplicationName属性的内容。

(2) 客户端激活模式

如前所述,已知模式只能在客户端创建对象时调用默认构造函数。上面的代码说明了这一点,因为GetObject()方法不能传递构造函数的参数。客户端激活模式可以通过自定义构造函数创建远程对象。

有两种方法可以激活客户端模式:

1) 调用RemotingConfiguration的静态方法RegisterActivatedClientType()。这个方法的返回值是Void。它只是在客户机中注册远程对象。的具体实例化也需要调用对象类的构造函数。

正在移除配置。RegisterActivatedClientType(

typeof(ServerRemoteObject。ServerObject),

TCP://localhost:8080/service message ');

ServerRemoteObject。server object server obj=new server remote object。server object();

2) 调用进程Activator的CreateInstance()方法。该方法将创建一个由方法参数指定类型的类对象。它与之前的GetObject()不同,它在客户端调用构造函数,而GetObject()只是获取对象,而创建实例是在服务器端完成的。CreateInstance()方法有许多重载,我将重点介绍两个常用的重载。

a、 public static object CreateInstance(Type type, object[] args, object[] activationAttributes);

参数描述:

类型:要创建的对象的类型。

Args:与调用构造函数的参数的数量、顺序和类型相匹配的参数数组。如果args是空数组或空引用(在Visual Basic中为Nothing),则调用不带参数的构造函数(默认构造函数)。

ActivationAttributes:包含一个或多个可参与激活的属性的数组。

这里的参数args是object[]数组类型。它可以在要创建的对象的构造函数中传递参数。其实从这里可以得出一个结论:公知激活方式传递的远程对象类只能使用默认的构造函数;在激活模式下,用户可以定义构造函数。ActivationAttributes参数通常用于在该方法中传递服务器的url。

假设我们的远程对象类ServerObject有一个构造函数:

ServerObject(字符串pName,字符串pSex,int pAge)

{

name=pName

sex=pSex

年龄=页面;

}

那么实现的代码是:

object[]attrs={ new URL attribute(' TCP://localhost:8080/service message ')};

object[] objs=新对象[3];

objs[0]=' way farer ';

objs[1]='男性';

objs[2]=28;

ServerRemoteObject。服务器对象=激活器。创建实例(

typeof(ServerRemoteObject。ServerObject)、objs、attrs);

如您所见,objs[]数组传递了构造函数的参数。

b、public static ObjectHandle CreateInstance(string assemblyName, string typeName, object[] activationAttribute);

参数描述:

AssemblyName:将在其中找到名为typeName的类型的程序集的名称。如果assemblyName为空引用(在Visual Basic中为Nothing ),则搜索正在执行的程序集。

TypeName:首选类型的名称。

ActivationAttributes:包含一个或多个可参与激活的属性的数组。

参数一目了然。请注意,此方法的返回值是ObjectHandle类型,因此代码与之前不同:

object[]attrs={ new URL attribute(' TCP://localhost:8080/echo message ')};

ObjectHandle句柄=Activator。create instance(' server remote object ',

ServerRemoteObject。ServerObject ',attrs);

ServerRemoteObject。server object obj=(server remote object。ServerObject)句柄。unwrap();

这个方法实际上是默认的构造函数。对象句柄。Unwrap()方法是返回被包装的对象。

解释:要使用UrlAttribute,还需要在命名空间中添加:using system . runtime . remoting . activation;

五、Remoting基础的补充

通过上面的描述,基本上最简单的Remoting程序已经完成了。这是创建Remoting程序的标准方法,但在实际开发过程中,我们可能会遇到各种情况。如果我们只掌握了一个所谓的“标准”,就无法想象我们可以“尝遍天下鲜”。

1、注册多个通道

在远程处理中,可以同时创建多个信道,即可以根据不同的端口创建不同的信道。但是,远程处理要求信道的名称必须不同,因为它被用作信道的唯一标识符。尽管IChannel具有ChannelName属性,但该属性是只读的。因此,上述创建信道的方法不能满足同时注册多个信道的要求。

这时,我们必须使用系统中的IDictionary接口。收藏:

注册Tcp通道:

IDictionary TCP prop=new Hashtable();

TCP prop[' name ']=' TCP 9090 ';

TCP prop[' port ']=9090;

IChannel channel=新的TcpChannel(tcpProp,

new BinaryClientFormatterSinkProvider(),

new BinaryServerFormatterSinkProvider());

频道服务。RegisterChannel(频道);

注册Http通道:

IDictionary http prop=new Hashtable();

http prop[' name ']=' http 8080 ';

http prop[' port ']=8080;

IChannel channel=new http channel(http prop,

新建SoapClientFormatterSinkProvider(),

new SoapServerFormatterSinkProvider());

频道服务。RegisterChannel(频道);

在“名称”属性中,定义不同的通道名称。

2、远程对象元数据相关性

因为服务器和客户端都使用远程对象,所以通常的方法是生成两个相同的对象dll并分别添加引用。但是,为了保证代码的安全性,减少客户端与远程对象元数据的相关性,有必要改变这种方法。也就是说,远程对象在服务器端实现,而这些实现的元数据在客户端被删除。

由于激活方式不同,在客户端创建对象的方法也不同,所以有两种情况来分离元数据的相关性。

(1) WellKnown激活模式:

通过界面。在服务器端,提供接口和具体类的实现,而在客户端,只提供接口:

公共接口是服务器对象

{

Person GetPersonInfo(字符串名称,字符串性别,int年龄);

}

公共类server object:MarshalByRefObject,IServerObject

{ .}

注意:两端生成此对象的程序集的名称必须相同,严格来说,命名空间的名称必须相同。

(2) 客户端激活模式:

如前所述,对于客户端激活模式,无论使用静态方法还是CreateInstance()方法,都必须在客户端调用构造函数来实例化对象。所以我们在客户端提供的远程对象不能只提供接口而没有类的实现。实际上,有两种方法可以从远程对象的元数据中分离出来:

a、利用WellKnown激活模式模拟客户端激活模式:

方法是利用设计模式中的“抽象工厂”。下面的类图描述了整体解决方案:

我们将抽象工厂的接口和实现类添加到服务器端的远程对象中:

公共接口是服务器对象

{

Person GetPersonInfo(字符串名称,字符串性别,int年龄);

}

公共接口IServerObjFactory

{

IServerObject create instance();

}

公共类server object:MarshalByRefObject,IServerObject

{

公共人物GetPersonInfo(字符串名称,字符串性别,整数年龄)

{

Person person=新人();

人。Name=名称;

人。性=性;

人。年龄=年龄;

退货人;

}

}

公共类ServerObjFactory:MarshalByRefObject,IServerObjFactory

{

公共IServerObject CreateInstance()

{

返回新server object();

}

}

那么在客户机的远程对象中只提供工厂接口和原始对象接口:

公共接口是服务器对象

{

Person GetPersonInfo(字符串名称,字符串性别,int年龄);

}

公共接口IServerObjFactory

{

IServerObject create instance();

}

我们用众所周知的激活模式注册远程对象,在服务器端:

//传递对象;

正在移除配置。RegisterWellKnownServiceType(

typeof(ServerRemoteObject。ServerObjFactory),

' ServiceMessage ',WellKnownObjectMode。single call);

注意,这里注册的不是ServerObject类对象,而是ServerObjFactory类对象。

客户:

ServerRemoteObject。IServerObjFactory服务器工厂=

(ServerRemoteObject。IServerObjFactory)激活器。GetObject(

typeof(ServerRemoteObject。IServerObjFactory),

TCP://localhost:8080/service message ');

ServerRemoteObject。IServerObject server obj=server factory。create instance();

为什么这是客户端激活模式的模拟?从激活的方法来说,我们使用SingleCall模式来激活对象,但此时我们要传递的不是远程对象,而是被激活的工厂对象。如果客户端想要创建一个远程对象,也应该通过工厂对象的CreateInstance()方法获得。而这个方法是在客户端调用的。因此,它的实现相当于客户端激活模式。

b、利用替代类来取代远程对象的元数据

实际上,我们可以使用一个技巧来欺骗远程处理。这里说的代课就是这一招。因为它提供服务,所以由Remoting交付的远程对象的实现细节当然放在服务器端。然而,将对象的副本放在客户端只是一个无奈的动作,因为客户端必须调用构造函数。由于具体实现在服务器端,为了在客户端实例化,最好在客户端实现这些。至于实现的细节,就不用管了。

如果远程对象有方法,服务器端提供方法实现,而客户端提供这个方法。至于里面的实现,要么抛出异常,要么返回空值;如果方法返回void,内部可以是空的。关键是这个客户端类对象要有这个方法。这个方法的实现其实和方法的声明差不多,所以我说这是个窍门。如果方法是这样,构造函数也是这样。

或者用代码来说明这个“阴谋”,这样更直观:

服务器端:

公共类server object:MarshalByRefObject

{

公共服务器对象()

{

}

公共人物GetPersonInfo(字符串名称,字符串性别,整数年龄)

{

Person person=新人();

人。Name=名称;

人。性=性;

人。年龄=年龄;

退货人;

}

}

客户:

公共类server object:MarshalByRefObject

{

公共ServerObj()

{

扔新系统。NotImplementedException();

}

公共人物GetPersonInfo(字符串名称,字符串性别,整数年龄)

{

扔新系统。NotImplementedException();

}

}

对比客户端和服务器,客户端的方法GetPersonInfo()没有具体的实现细节,只是抛出一个异常。或者直接写语句return null,还是可以的。我们将客户端的这个类称为远程对象的替代类。

3、利用配置文件实现

在上述方法中,服务器uri、端口和激活模式的设置是通过代码来完成的。其实我们也可以用配置文件来设置。这是一个优势,因为这个配置文件是一个Xml文档。如果我们需要改变端口或者别的什么,不需要修改程序重新编译,只需要改变这个配置文件就可以了。

(1) 服务器端的配置文件:

配置

系统.运行时.远程处理

应用程序名称='ServerRemoting '

服务

已知模式='Singleton '类型='ServerRemoteObject。' server object ' object uri=' service message '/

/服务

频道

通道引用='tcp '端口='8080'/

/频道

/应用程序

/system.runtime.remoting

/配置

如果是客户端激活模式,将wellknown改为activated,删除mode属性。

将配置文件放在服务器程序的应用程序文件夹中,并将其命名为ServerRemoting.config,然后前面的服务器端程序可以直接使用以下语句:

正在移除配置。configure(' server remoting . config ');

(2) 客户端配置文件

如果是客户端激活模式,修改同上。该调用还使用remoting configuration . configure()方法来调用存储在客户端中的配置文件。

配置文件也可以放在machine.config中。如果客户端程序是web应用程序,则可以放在web.config中

4、启动/关闭指定远程对象

远程处理不提供类似于UnregisterWellKnownServiceType()的方法,也就是说,一旦通过注册了远程对象,如果信道没有关闭,它将始终存在于信道中。每当客户端激活对象时,都会创建一个对象实例。如果Remoting只传输一个远程对象,没有问题,只需关闭通道。传输多个远程对象怎么办?我应该怎么做来关闭指定的远程对象?关机后需要启动怎么办?

我们注意到Remoting中提供了Marshal()和Disconnect()方法,答案就在这里。Marshal()方法是将MarshalByRefObject类对象转换为ObjRef类对象,其中存储了生成代理与远程对象通信所需的所有相关信息。这样,实例可以被序列化,以便在应用程序域之间和通过网络传输,并且客户端可以调用。Disconnect()方法将具体的实例对象从通道中断开。

该方法如下:

首先注册频道:

TcpChannel channel=新TcpChannel(8080);

频道服务。RegisterChannel(频道);

然后启动服务:

首先在服务器端实例化远程对象。

server object obj=new server object();

然后,注册该对象。请注意,这里使用的不是remoting configuration . registerwellknownservicetype(),而是RemotingServices。Marshal():

ObjRef ObjRef wellknown=remoting services。Marshal(obj,' service message ');

如果要注销对象,则:

远程服务。断开连接(obj);

注意,这里Disconnect的类对象必须是之前实例化的对象。正因为如此,我们可以根据需要创建指定的远程对象,当它关闭时,会断开之前实例化的对象。

至于客户端的调用,还是通过Activator获取的。GetObject()的方式与前面众所周知的模式相同。但是从实现代码中,我们会注意到一个问题。因为服务器显式地实例化远程对象,所以不管有多少个客户端,不管是否相同,都调用同一个远程对象。因此,我们称这种方法为模拟单例模式。

客户端激活模式

我们还可以通过Marshal()和Disconnect()来模拟客户端激活模式。首先,让我们回顾一下“远程对象的元数据相关性”一节。在本节中,我将讨论如何使用设计模式的“抽象工厂”来创建对象实例,从而用SingleCall模式模拟客户端激活模式。仔细想想前面模拟的单例模型。答案会出来吗?

在“模拟单例”模式中,我们封送一个特定的远程对象实例,以便客户端可以获得该对象的引用信息。那我们换个思路。当我们使用抽象工厂提供接口时,工厂类实现了创建远程对象的方法。然后我们在服务器端创建工厂类实例。然后封送这个工厂类实例。当客户端获取对象时,它不会获取特定的远程对象,而是获取特定的工厂类对象。然后调用CreateInstance()方法创建一个具体的远程对象实例。此时,对于多个客户端,调用同一个工厂类对象;但是,远程对象是由每个客户端创建的,因此对于远程对象,它由客户端激活,并创建不同的对象。

当我们想要启动/关闭指定的对象时,我们只需要使用Disconnet()方法来注销工厂类对象。

六、小结

遥控在Microsoft.Net真的意义深远。整个远程内容不是我在这篇文章中能够描述的,也不是我这个远程初学者能够掌握的。在《人间词话》一书中,郭旺写道:古今成就伟业的学者,必经三界。“昨夜西风凋碧树,我独登高楼,望天涯。”此第一境界也。“我宽了也不后悔,对伊拉克念念不忘。”此第二境界也。“众人寻他千遍,蓦然回首,那人已在昏黄的灯光下。”此第三境界也。这样来形容我对Remoting的学习,我还处在“一个人爬高楼,一路望天涯”的时候。我真的可以说,我从来都不是初学者。

也许你需要“越走越宽”,学会遥控“永不后悔”,才能“蓦然回首”。

以上是《基础》的全部内容。Net远程处理。希望给大家一个参考,多多支持我们。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: