小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

.NET Remoting監(jiān)聽(tīng)端口的Windows Service

 ThinkTank_引擎 2014-11-20
   

所謂.NET Remoting就是跨應(yīng)用程序域邊界調(diào)用程序集。如圖23-16所示,顯示了.NET Remoting應(yīng)用程序的基本構(gòu)架。

文本框:  
圖23-16  .NET Remoting
應(yīng)用程序基本構(gòu)架

從圖23-16中看到,Remoting服務(wù)端承載遠(yuǎn)程對(duì)象,使外界能與之通信,對(duì)外的信道可以是HTTP、TCP或者IPC。HTTP方式的信道在跨越防火墻上有優(yōu)勢(shì);TCP方式的信道常用在局域網(wǎng)內(nèi)通信,速度比HTTP快很多;IPC信道用于同一臺(tái)機(jī)器的進(jìn)程間通信,通信不占用網(wǎng)絡(luò)資源,速度又比TCP快很多。因此,這里的服務(wù)器是一個(gè)廣義的概念,對(duì)于TCP和HTTP信道,服務(wù)器可以是兩個(gè)獨(dú)立的物理計(jì)算機(jī)。

那么,最基本的.NET Remoting應(yīng)用程序應(yīng)該由三部分構(gòu)成:

·      服務(wù)端。承載遠(yuǎn)程對(duì)象。

·      遠(yuǎn)程對(duì)象。需要跨應(yīng)用程序域邊界調(diào)用的程序集。

·      客戶端。用于調(diào)用遠(yuǎn)程對(duì)象。

遠(yuǎn)程對(duì)象是根本,服務(wù)端只是一個(gè)載體,那么我們就先來(lái)創(chuàng)建一個(gè)簡(jiǎn)單的遠(yuǎn)程對(duì)象:

1.繼續(xù)使用前面的一個(gè)解決方案。右鍵單擊解決方案,選擇“添加”→“新建項(xiàng)目”命令,新建一個(gè)TestRemoteObject類(lèi)庫(kù)項(xiàng)目。

2.把默認(rèn)的Class1.cs重命名為RemoteObject.cs,打開(kāi)cs文件,修改代碼為:

using System;

namespace RemoteObject

{

    public class MyObject : MarshalByRefObject

    {

        public int Add(int a, int b)

        {

            return a + b;

        }

    }

}

在RemoteObject命名空間下有一個(gè)MyObject類(lèi),除了繼承MarshalByRefObject類(lèi)使之能跨應(yīng)用程序域邊界被訪問(wèn)之外,和一般的類(lèi)沒(méi)有任何區(qū)別。

3.右鍵單擊這個(gè)類(lèi)庫(kù)項(xiàng)目,如圖23-17所示。

圖23-17  項(xiàng)目屬性

我們看到這個(gè)項(xiàng)目的程序集名為T(mén)estRemoteObject,默認(rèn)的命名空間為T(mén)estRemoteObject。默認(rèn)的命名空間名字和程序集的名字是一樣的,但是在代碼中我們的命名空間名字為RemoteObject,和程序集名字不同以便進(jìn)行區(qū)分。

注意:程序集和命名空間是兩個(gè)不同的概念,一個(gè)程序集可以包括幾個(gè)命名空間,一個(gè)命名空間也可以由多個(gè)程序集來(lái)實(shí)現(xiàn)。

在創(chuàng)建了遠(yuǎn)程對(duì)象后就需要?jiǎng)?chuàng)建Remoting服務(wù)端來(lái)發(fā)布這個(gè)遠(yuǎn)程對(duì)象了。

4.服務(wù)端可以是一個(gè)控制臺(tái)應(yīng)用程序、Windows應(yīng)用程序、Windows服務(wù)甚至是IIS。為了簡(jiǎn)單,我們將首先使用控制臺(tái)應(yīng)用程序做服務(wù)端。在解決方案中新建一個(gè)名為T(mén)estRemotingConsoleServer的控制臺(tái)應(yīng)用程序,然后右鍵單擊項(xiàng)目,選擇添加應(yīng)用,如圖23-18所示,添加System.Runtime.Remoting的引用。

5.把Program.cs修改成如下:

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

namespace TestRemotingConsoleServer

{

    class Program

    {

        static void Main(string[] args)

        {

            // 新建一個(gè)TCP信道

            TcpChannel tc = new TcpChannel(9999);

            // 注冊(cè)TCP信道

            ChannelServices.RegisterChannel(tc, false);

            // 注冊(cè)知名對(duì)象

            RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemoteObject.

            MyObject), "myObject", WellKnownObjectMode.SingleCall);

            // 讓控制臺(tái)不會(huì)自動(dòng)關(guān)閉

            Console.ReadLine();

        }

    }

}

圖23-18  添加System.Runtime.Remoting的引用

我們看到,使用.NET Remoting發(fā)布遠(yuǎn)程對(duì)象并不復(fù)雜,首先需要告知程序使用哪種信道發(fā)布遠(yuǎn)程對(duì)象。在這里我們選擇TCP信道,并在9999端口通信。然后要告知程序把對(duì)象注冊(cè)為哪種類(lèi)型,在這里筆者不想詳細(xì)闡述遠(yuǎn)程對(duì)象的種類(lèi)和模式,讀者只需要理解在這里我們把Remote- Object.MyObject這個(gè)類(lèi)型使用一個(gè)固定的名字myObject來(lái)發(fā)布(因此叫做知名對(duì)象),對(duì)象的模式是SingleCall,SingleCall模式的對(duì)象是無(wú)狀態(tài)的。

最后我們來(lái)完成用客戶端應(yīng)用程序調(diào)用遠(yuǎn)程對(duì)象??蛻舳藨?yīng)用程序可以是ASP.NET應(yīng)用程序、控制臺(tái)應(yīng)用程序或者Windows應(yīng)用程序。那么,我們就直接使用前一節(jié)建立的ASP.NET應(yīng)用程序作為客戶端吧。

6.在TestWeb網(wǎng)站下新建一個(gè)RemotingTest.aspx,然后在頁(yè)面的Page_Load事件處理方法中調(diào)用遠(yuǎn)程對(duì)象。

protected void Page_Load(object sender, EventArgs e)

{

    RemoteObject.MyObject mo = (RemoteObject.MyObject)Activator.GetObject

    (typeof(RemoteObject.MyObject), "tcp://localhost:9999/myObject");

    Response.Write(mo.Add(1, 2));

}

在這里,我們從遠(yuǎn)程地址tcp://localhost:9999/myObject創(chuàng)建遠(yuǎn)程對(duì)象,并調(diào)用了對(duì)象的Add()方法。myObject就是在服務(wù)端中為知名對(duì)象起的名字。

7.編譯整個(gè)解決方案,IDE提示“找不到命名空間RemoteObject”,這是因?yàn)槲覀兊目蛻舳撕头?wù)端項(xiàng)目沒(méi)有引用遠(yuǎn)程對(duì)象類(lèi)庫(kù)項(xiàng)目。右鍵單擊服務(wù)端項(xiàng)目,選擇“添加引用”,在項(xiàng)目頁(yè)中找到類(lèi)庫(kù)項(xiàng)目,單擊“確定”按鈕,如圖23-19所示。

對(duì)于客戶端項(xiàng)目也同樣添加類(lèi)庫(kù)的引用,然后重新編譯解決方案。

8.現(xiàn)在就能進(jìn)行測(cè)試了。解決方案中的項(xiàng)目如圖23-20所示。

                   

                              圖23-19  添加項(xiàng)目引用                                                                  圖23-20  解決方案中的項(xiàng)目

要讓遠(yuǎn)程調(diào)用成功運(yùn)行,先要啟動(dòng)服務(wù)端使之監(jiān)聽(tīng)端口。如圖23-20所示,單擊控制臺(tái)應(yīng)用程序,項(xiàng)目名自動(dòng)以粗體標(biāo)識(shí),表示這是當(dāng)前項(xiàng)目,按Ctrl+F5組合鍵直接啟動(dòng)程序。然后再單擊TestWeb網(wǎng)站,右鍵單擊RemotingTest.aspx,選擇設(shè)為起始頁(yè),按Ctrl+F5組合鍵啟動(dòng)網(wǎng)站。

如圖23-21所示,頁(yè)面顯示3,成功了!

圖23-21  調(diào)用遠(yuǎn)程對(duì)象

注意圖23-21所示,在整個(gè)過(guò)程中需要確保服務(wù)端處于運(yùn)行狀態(tài)。至此,我們完成了第一個(gè).NET Remoting應(yīng)用程序。

23.3.2  Remoting的信道

前面提到過(guò),Remoting有多種信道可以選擇,這大大增加了我們分布式系統(tǒng)的靈活性。如果希望在廣域網(wǎng)通信,可以使用HTTP信道,如果希望在局域網(wǎng)通信取得更好的性能,可以使用TCP信道,如果希望在本機(jī)上的不同進(jìn)程間通信以獲得最好的性能,可以使用IPC信道。

下面我們來(lái)修改前面的程序,使之使用三種不同的Remoting信道,并且我們要比較三種信道在效率上差多少:

1.首先在遠(yuǎn)程對(duì)象中新增一個(gè)方法,使之返回大量的數(shù)據(jù)。

using System;

namespace RemoteObject

{

    public class MyObject : MarshalByRefObject

    {

        public int Add(int a, int b)

        {

            return a + b;

        }

        public string[] GetData()

        {

            string[] data = new string[100000];

            for (int i = 0; i < data.Length; i++)

                data[i] = "很大量的數(shù)據(jù)"+i;

            return data;

        }

    }

}

2.然后修改服務(wù)端,使之在三個(gè)不同的信道上發(fā)布遠(yuǎn)程對(duì)象。

using System;

using System.Runtime.Remoting;

using System.Runtime.Remoting.Channels;

using System.Runtime.Remoting.Channels.Tcp;

using System.Runtime.Remoting.Channels.Http;

using System.Runtime.Remoting.Channels.Ipc;

namespace TestRemotingConsoleServer

{

    class Program

    {

        static void Main(string[] args)

        {

            // 新建一個(gè)TCP信道

            TcpChannel tc = new TcpChannel(9999);

            // 新建一個(gè)HTTP信道

            HttpChannel hc = new HttpChannel(8888);

            // 新建一個(gè)IPC信道

            IpcChannel ic = new IpcChannel("testPipe");

            // 注冊(cè)TCP信道

            ChannelServices.RegisterChannel(tc, false);

            ChannelServices.RegisterChannel(hc, false);

            ChannelServices.RegisterChannel(ic, false);

            // 注冊(cè)知名對(duì)象

            RemotingConfiguration.RegisterWellKnownServiceType(typeof

            (RemoteObject.MyObject), "myObject", WellKnownObjectMode.SingleCall);

            // 讓控制臺(tái)不會(huì)自動(dòng)關(guān)閉

            Console.ReadLine();

        }

    }

}

注意,由于是在同一個(gè)機(jī)器上注冊(cè)多個(gè)信道,需要給每個(gè)信道使用不同的端口。對(duì)于IPC信道來(lái)說(shuō),使用一個(gè)管道名來(lái)區(qū)分而不是端口號(hào)。為了能更好地測(cè)試三者的差別,我們把服務(wù)端部署到另外一個(gè)服務(wù)器上(把EXE文件和DLL文件復(fù)制過(guò)去)。

3.在RemotingTest.aspx頁(yè)面上新建三個(gè)按鈕用于使用不同的信道調(diào)用遠(yuǎn)程對(duì)象。

<asp:Button ID="btn_HttpChannel" runat="server" Text="http通道"/>

<asp:Button ID="btn_TcpChannel" runat="server" Text="tcp通道"/>

<asp:Button ID="btn_IpcChannel" runat="server" Text="ipc通道"/>

按鈕的單擊事件處理方法如下:

protected void btn_HttpChannel_Click(object sender, EventArgs e)

{

    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

    sw.Start();

    RemoteObject.MyObject mo = (RemoteObject.

    MyObject)Activator.GetObject(typeof(RemoteObject.MyObject),

    "http://srv-devapphost:8888/myObject");

    Response.Write("HTTP信道<br/>");

    Response.Write(string.Format("記錄數(shù):{0}條<br/>", mo.GetData().Length));

    Response.Write(string.Format("花費(fèi)時(shí)間:{0}毫秒<br/>", sw.ElapsedMilliseconds));

}

protected void btn_TcpChannel_Click(object sender, EventArgs e)

{

    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

    sw.Start();

    RemoteObject.MyObject mo = (RemoteObject.MyObject)Activator.GetObject

    (typeof(RemoteObject.MyObject), "tcp://srv-devapphost:9999/myObject");

    Response.Write("TCP信道<br/>");

    Response.Write(string.Format("記錄數(shù):{0}條<br/>", mo.GetData().Length));

    Response.Write(string.Format("花費(fèi)時(shí)間:{0}毫秒<br/>", sw.ElapsedMilliseconds));

}

protected void btn_IpcChannel_Click(object sender, EventArgs e)

{

    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();

    sw.Start();

    RemoteObject.MyObject mo = (RemoteObject.

    MyObject)Activator.GetObject(typeof(RemoteObject.MyObject),

    "ipc://testPipe/myObject");

    Response.Write("IPC信道<br/>");

    Response.Write(string.Format("記錄數(shù):{0}條<br/>", mo.GetData().Length));

    Response.Write(string.Format("花費(fèi)時(shí)間:{0}毫秒<br/>", sw.ElapsedMilliseconds));

}

可以看到,使用三種信道調(diào)用的代碼僅在URL上有區(qū)別。在這里我們不但把服務(wù)端部署到了遠(yuǎn)程服務(wù)器上,而且在本地也開(kāi)了一個(gè)服務(wù)端用于在IPC上注冊(cè)遠(yuǎn)程對(duì)象。

4.測(cè)試結(jié)果如圖23-22所示。

圖23-22  Remoting的三種信道

可以看到在效率上三者有明顯的差別。IPC比TCP快是因?yàn)樗鼈鬟f數(shù)據(jù)不經(jīng)過(guò)網(wǎng)絡(luò),不占用網(wǎng)絡(luò)資源。TCP比HTTP快很多是因?yàn)槟J(rèn)情況下TCP信道使用二進(jìn)制序列化,序列化后的數(shù)據(jù)量很小,而HTTP默認(rèn)使用SOAP消息進(jìn)行格式化,基于XML的SOAP消息非常臃腫,因此在傳輸上會(huì)比TCP花費(fèi)更多的時(shí)間。不過(guò)不可否認(rèn)HTTP信道在跨防火墻上的優(yōu)勢(shì),因此使用哪種信道還需要根據(jù)自己的需求來(lái)選擇。

23.3.3  使用配置文件增加靈活性

雖然我們做的Remoting程序可以正常使用,但是整個(gè)程序非常不靈活:

·      服務(wù)端有關(guān)信道、端口等的配置都直接寫(xiě)死在程序里面。

·      客戶端設(shè)置的遠(yuǎn)程對(duì)象的地址也是寫(xiě)死在程序里面的。

對(duì)于客戶端的配置不是大問(wèn)題,因?yàn)槠鋵?shí)那個(gè)URL就是一個(gè)字符串。而服務(wù)端的配置文件應(yīng)該怎么做呢?其實(shí)一點(diǎn)也不復(fù)雜,添加一個(gè)app.config然后寫(xiě)入下面的內(nèi)容:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

  <system.runtime.remoting>

    <application name="RemoteHostService">

      <service>

        <wellknown type="RemoteObject.MyObject, TestRemoteObject" objectUri=

        "myObject" mode="SingleCall" />

      </service>

      <channels>

        <channel ref="tcp" port="9999" />

        <channel ref="http" port="8888" />

        <channel ref="ipc" portName="testPipe" />

      </channels>

    </application>

  </system.runtime.remoting>

</configuration>

可以看到配置文件主要由兩部分構(gòu)成:

·      定義遠(yuǎn)程對(duì)象類(lèi)型的service節(jié)點(diǎn)。在這里我們定義了一個(gè)知名對(duì)象,模式是SingleCall,對(duì)象名為myObject。

·      定義信道的channels節(jié)點(diǎn)。在這里定義了三個(gè)信道,和先前程序方式定義的一樣。

特別需要注意的是,這里的type="RemoteObject.MyObject,TestRemoteObject",格式是:

type="命名空間.類(lèi)型名,程序集名"

對(duì)比圖23-17看看,現(xiàn)在你知道為什么當(dāng)時(shí)筆者要把命名空間、類(lèi)型和程序集三者的名字設(shè)置不同了吧。那么,怎么讓服務(wù)端加載配置文件讀取Remoting的配置呢?只需要一行代碼就行。

RemotingConfiguration.Configure("TestRemotingConsoleServer.exe.config", false);

Console.ReadLine();

你可能會(huì)奇怪,配置文件是app.config,為什么這里寫(xiě)成了應(yīng)用程序名.config呢?其實(shí)在編譯的時(shí)候IDE會(huì)自動(dòng)把配置文件進(jìn)行改名,以免發(fā)生沖突,如圖23-23所示,可以看到Release目錄的        文件。

圖23-23  服務(wù)端程序release文件夾

真正有用的是加亮的三個(gè)文件(分別是遠(yuǎn)程對(duì)象、服務(wù)端和配置文件),在部署的時(shí)候只需要復(fù)制這些文件即可。

雖然改了服務(wù)端,但是我們并沒(méi)有改變通道的端口,因此客戶端不需要做任何修改就能直接運(yùn)行。如果你希望把URL從程序中分離的話,可以在配置文件中添加幾個(gè)節(jié)點(diǎn)。

<appSettings>

    <add key="HTTPChannel" value="http://localhost:8888/myObject"/>

    <add key="TCPChannel" value="tcp://localhost:9999/myObject"/>

    <add key="IPCChannel" value="ipc://testPipe/myObject"/>

</appSettings>

然后在代碼中調(diào)用配置文件讀取URL。

RemoteObject.MyObject mo = (RemoteObject.MyObject)

Activator.GetObject(typeof(RemoteObject.MyObject),

ConfigurationManager.AppSettings["HTTPChannel"]);

其他兩個(gè)信道的代碼差不多,就不列出來(lái)了?,F(xiàn)在這樣就非常靈活了,修改信道、修改端口甚至轉(zhuǎn)移服務(wù)端的位置只需要重新調(diào)整配置文件即可。

23.3.4  使用接口降低耦合

讀者首先要明確一點(diǎn),客戶端調(diào)用的遠(yuǎn)程方法是在服務(wù)端執(zhí)行的。如下,我們?cè)谶h(yuǎn)程對(duì)象中增加一個(gè)方法。

public void HelloWorld()

{

    Console.WriteLine("編程快樂(lè)");

}

重新編譯服務(wù)端和客戶端,運(yùn)行客戶端可以看到服務(wù)端控制臺(tái)程序上輸出了“編程快樂(lè)”字樣,如圖23-24所示。

文本框:  
圖23-24  遠(yuǎn)程對(duì)象在服務(wù)端執(zhí)行

那么問(wèn)題就來(lái)了,既然遠(yuǎn)程對(duì)象是在服務(wù)端執(zhí)行的,客戶端為什么要引用遠(yuǎn)程對(duì)象呢?假設(shè)我們的報(bào)表系統(tǒng)是使用.NET Remoting開(kāi)發(fā)的,難道要把核心DLL也公布給客戶嗎(要知道.NET應(yīng)用程序是很容易被反編譯得到“源代碼”的)?其實(shí),客戶端只需要得到遠(yuǎn)程對(duì)象的“描述”,知道遠(yuǎn)程對(duì)象的類(lèi)型以及成員定義,讓客戶端代碼能編譯通過(guò)即可。具體方法是什么,怎么實(shí)現(xiàn),客戶端并不關(guān)心。

那么,怎么構(gòu)建這個(gè)供客戶端使用的殼子呢?有兩種方法。

·      直接使用工具比如soapsuds.exe來(lái)生成。

·      使用基于接口的編程方法。

由于篇幅關(guān)系,在這里我們僅僅介紹第二種方法的實(shí)現(xiàn):

1.新建一個(gè)類(lèi)庫(kù)項(xiàng)目ITestRemoteObject,這個(gè)類(lèi)庫(kù)是前面TestRemoteObject的接口(Interface),因此以字母I開(kāi)頭。

2.打開(kāi)TestRemoteObject下的RemoteObject.cs,把鼠標(biāo)放在MyObject類(lèi)上單擊右鍵,選擇“重構(gòu)”→“提取接口”,如圖23-25所示。

單擊“全選”按鈕選中所有成員,單擊“確定”按鈕??梢钥吹絋estRemoteObject類(lèi)庫(kù)下面多了一個(gè)cs文件,如圖23-26所示。

                 

                                               圖23-25  提取接口                                                    圖23-26  自動(dòng)生成的接口

IMyObject就是MyObject類(lèi)對(duì)應(yīng)的接口。打開(kāi)這個(gè)文件可以看到接口其實(shí)就是對(duì)類(lèi)成員的定義,沒(méi)有實(shí)際的實(shí)現(xiàn)。

using System;

namespace TestRemoteObject

{

    interface IMyObject

    {

        int Add(int a, int b);

        string[] GetData();

        void HelloWorld();

    }

}

對(duì)這個(gè)接口我們要進(jìn)行一些改動(dòng):

·      要讓接口能被外部調(diào)用,需要把接口加上公有訪問(wèn)修飾符。

·      系統(tǒng)自動(dòng)以程序集的名字作為命名空間的命名,我們還是改回原來(lái)的RemoteObject。

using System;

namespace RemoteObject

{

    public interface IMyObject

    {

        int Add(int a, int b);

        string[] GetData();

        void HelloWorld();

    }

}

3.現(xiàn)在這個(gè)接口在遠(yuǎn)程對(duì)象文件中,我們需要把它移動(dòng)到ITestRemoteObject中,直接點(diǎn)擊文件,Ctrl+X(剪切)、CTRL+V(粘貼)即可。

4.回頭看MyObject文件:

public class MyObject : MarshalByRefObject, TestRemoteObject.IMyObject

系統(tǒng)自動(dòng)讓它繼承了TestRemoteObject.IMyObject,剛才我們把TestRemoteObject修改成了RemoteObject,現(xiàn)在這里也需要同樣修改。

既然讓類(lèi)實(shí)現(xiàn)接口,那么就需要讓TestRemoteObject項(xiàng)目引用ITestRemoteObject項(xiàng)目。右鍵單擊TestRemoteObject項(xiàng)目,選擇添加引用,在項(xiàng)目選項(xiàng)卡中找到ITestRemoteObject項(xiàng)目,單擊“確定”按鈕即可。

現(xiàn)在兩個(gè)項(xiàng)目的結(jié)構(gòu)應(yīng)該如圖23-27所示。

你可能會(huì)問(wèn),接口僅僅是對(duì)類(lèi)的一個(gè)定義嗎?不僅僅是這樣,接口還對(duì)類(lèi)有約束力,如果你修改了接口也一定要修改“實(shí)現(xiàn)”。如果你在接口中新加入一個(gè)Test()的方法,而不修改“實(shí)現(xiàn)”,編譯程序會(huì)得到編譯錯(cuò)誤,如圖23-28所示。

              

             圖23-27  基于接口的編程                                          圖23-28  類(lèi)需要實(shí)現(xiàn)接口的成員

5.現(xiàn)在,我們的客戶端就可以引用和使用接口,而不是直接引用和使用遠(yuǎn)程對(duì)象了。首先右鍵單擊TestWeb網(wǎng)站,選擇屬性頁(yè)。在引用頁(yè)找到原來(lái)的遠(yuǎn)程對(duì)象TestRemoteObject,刪除它的引用,并添加ITestRemoteObject的引用,如圖23-29所示。

圖23-29  修改網(wǎng)站項(xiàng)目的引用

查找替換Remoting.aspx.cs中的所有RemoteObject. MyObject為RemoteObject.IMyObject,比如:

RemoteObject.IMyObject mo = (RemoteObject.IMyObject)Activator.

GetObject(typeof(RemoteObject.IMyObject), ConfigurationManager.

AppSettings["TCPChannel"]);

mo.HelloWorld();

6.重新編譯解決方案,先后運(yùn)行服務(wù)端和客戶端,效果和原來(lái)的沒(méi)有什么不同。但是,這樣的方式更靈活了,或者說(shuō)耦合更低了。為什么這樣說(shuō)呢?因?yàn)椋F(xiàn)在如果希望在服務(wù)端的實(shí)現(xiàn)中做什么改動(dòng)的話,不需要重新編譯和部署客戶端程序。

23.3.5  使用Windows服務(wù)承載遠(yuǎn)程對(duì)象

現(xiàn)在的程序看似很完美,但是要想真正應(yīng)用還有一些問(wèn)題。我們的服務(wù)端是一個(gè)控制臺(tái)應(yīng)用程序,如果在服務(wù)器上需要有10個(gè)Remoting的服務(wù)端,那么我們服務(wù)器重啟動(dòng)后也需要重啟動(dòng)這10個(gè)程序嗎?讀者可能會(huì)說(shuō)可以把它們加入開(kāi)始菜單的啟動(dòng)中讓程序自動(dòng)啟動(dòng)。但是你有沒(méi)有想過(guò),在登錄到服務(wù)器進(jìn)行維護(hù)的時(shí)候很容易不小心把控制臺(tái)程序關(guān)閉了,而且關(guān)閉之后還不知道。

要想解決這個(gè)問(wèn)題就需要使用一種后臺(tái)式的程序來(lái)作為服務(wù)端,Windows服務(wù)正好可以滿足這個(gè)要求,而且還可以設(shè)置Windows服務(wù)自動(dòng)啟動(dòng)。使用VS 2005創(chuàng)建.NET的Windows服務(wù)非常簡(jiǎn)單,下面我們一起來(lái)實(shí)現(xiàn)Windows服務(wù)版本的Remoting服務(wù)端。

1.創(chuàng)建一個(gè)新的Windows服務(wù)項(xiàng)目TestRemotingService,如圖23-30所示。

圖23-30  創(chuàng)建新的Windows服務(wù)項(xiàng)目

2.打開(kāi)Service1代碼視圖,找到OnStart部分,加入代碼。

protected override void OnStart(string[] args)

{

System.Runtime.Remoting.RemotingConfiguration.Configure(AppDomain.CurrentDomain.

BaseDirectory + "TestRemotingService.exe.config", false);

}

這句代碼實(shí)現(xiàn)在Windows服務(wù)啟動(dòng)的時(shí)候從Windows服務(wù)安裝目錄所在的配置文件加載Remoting配置,然后把先前控制臺(tái)服務(wù)端的配置文件復(fù)制過(guò)來(lái)。

現(xiàn)在這個(gè)Windows服務(wù)是Remoting的服務(wù)端,因此也別忘記添加對(duì)TestRemoteObject遠(yuǎn)程對(duì)象的引用。

3.切換到Service1的設(shè)計(jì)視圖,在空白處右鍵單擊,然后選擇“添加安裝程序”選項(xiàng)。如圖23-31所示。

圖23-31  添加服務(wù)安裝程序

4.打開(kāi)系統(tǒng)自動(dòng)生成的ProjectInstaller.cs,如圖23-32所示,可以看到頁(yè)面上有兩個(gè)組件。

圖23-32  服務(wù)安裝程序

單擊serviceProcessInstaller1組件,觀察屬性窗口,如圖23-33所示。

在這里我們把Account屬性設(shè)置為L(zhǎng)ocalSystem,作為服務(wù)的賬戶類(lèi)型。然后單擊serviceInstaller1組件,觀察屬性窗口,如圖23-34所示。

                 

                      圖23-33  ServiceProcessInstaller組件                                          圖23-34  ServiceInstaller組件

在這里可以設(shè)置服務(wù)友好名、服務(wù)的描述、服務(wù)名和啟動(dòng)方式。只需要把StartType設(shè)置為Automatic,服務(wù)就能在系統(tǒng)重新啟動(dòng)后自動(dòng)啟動(dòng)。

5.現(xiàn)在就可以安裝服務(wù)了,單擊“開(kāi)始”菜單→“所有程序”→Microsoft Visual Studio 2005→Visual Studio Tools→“Visual Studio 2005命令行提示”,如圖23-35所示,使用installutil程序來(lái)安裝Windows服務(wù)。

圖23-35  使用installutil工具安裝Windows服務(wù)

如果你覺(jué)得輸入exe所在路徑太麻煩,可以直接打開(kāi)文件夾把exe文件拖入命令行窗口。卸載服務(wù)使用–u參數(shù)。

installutil -u Windows服務(wù)exe所在路徑

6.執(zhí)行“我的電腦右鍵”→“管理”→“服務(wù)和應(yīng)用程序”→“服務(wù)”命令。如圖23-36所示,可以在列表中找到我們的服務(wù)。

圖23-36  服務(wù)已經(jīng)安裝成功

查看這個(gè)服務(wù)的屬性,如圖23-37所示。

圖23-37  Windows服務(wù)的屬性

7.如果程序?qū)懙臎](méi)有什么問(wèn)題的話(其實(shí)我們只寫(xiě)了一行代碼),服務(wù)應(yīng)該能正常啟動(dòng),然后可以打開(kāi)網(wǎng)站進(jìn)行測(cè)試。

注意:由于安全問(wèn)題,必須為Windows服務(wù)指定一個(gè)有效賬戶(Account=User)才能使用IPC信道,在這里就不詳細(xì)敘述了。

除了使用Windows服務(wù)承載遠(yuǎn)程對(duì)象外,還可以使用IIS。不過(guò)需要注意,使用IIS承載遠(yuǎn)程對(duì)象只能在HTTP信道上通信,好處在于可以使用IIS來(lái)進(jìn)行安全管理。需要說(shuō)的是,HTTP方式的Remoting效率非常低(甚至不如Web Service),因此不推薦。具體實(shí)現(xiàn)IIS部署Remoting的方法在這里就不說(shuō)明了。

23.3.6  異步操作

在介紹Web服務(wù)的時(shí)候,我們介紹了異步調(diào)用Web服務(wù)的操作,在這里我們將介紹如何異步調(diào)用遠(yuǎn)程對(duì)象的方法。

1.首先在遠(yuǎn)程對(duì)象中加一個(gè)耗時(shí)2秒的方法。

public string LongWork()

{

    System.Threading.Thread.Sleep(2000);

    return "編程快樂(lè)";

}

別忘記同時(shí)更新接口。

using System;

namespace RemoteObject

{

    public interface IMyObject

    {

        int Add(int a, int b);

        string[] GetData();

        void HelloWorld();

        string LongWork();

    }

}

2.在客戶端RemotingTest.asp上添加一個(gè)按鈕,按鈕的單擊事件處理方法如下:

protected void btn_AsyncInvoke_Click(object sender, EventArgs e)

{

    sw = new System.Diagnostics.Stopwatch();

    sw.Start();

    RemoteObject.IMyObject mo = (RemoteObject.IMyObject)

    Activator.GetObject(typeof(RemoteObject.IMyObject), ConfigurationManager.

    AppSettings["TCPChannel"]);

    MyDelegate md = new MyDelegate(mo.LongWork);

    AsyncCallback ac = new AsyncCallback(this.CallBack);

    IAsyncResult Iar = md.BeginInvoke(ac, null);

    System.Threading.Thread.Sleep(1000);

}

在這里使用了兩個(gè)私有變量,一個(gè)是用于Stopwatch,另外一個(gè)是方法的代理,在Page_Load上        添加。

private delegate string MyDelegate();

private System.Diagnostics.Stopwatch sw;

在調(diào)用了異步方法后線程休息了1秒,異步方法完成之后會(huì)調(diào)用回調(diào)方法。

public void CallBack(IAsyncResult Iar)

{

    if (Iar.IsCompleted)

    {

        Response.Write("異步調(diào)用<br/>");

        Response.Write(string.Format("花費(fèi)時(shí)間:{0}毫秒<br/>", sw.

        ElapsedMilliseconds));

    }

}

3.由于方法是異步調(diào)用的,方法執(zhí)行2秒,我們當(dāng)前的按鈕單擊處理事件占用1秒,總共占用的時(shí)間也是2秒,如圖23-38所示。

圖23-38  異步調(diào)用方法

異步調(diào)用遠(yuǎn)程對(duì)象的方法和異步調(diào)用本地對(duì)象的方法其實(shí)差不多,在這里就不詳述了。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多