遠端處理範例:非同步遠端處理
本主題專門說明一項為了在現有應用程式中提供回溯相容性而保留的舊有技術,不建議用於新的開發工作。分散式應用程式應使用 Windows Communication Foundation (WCF) 進行開發。
下列範例應用程式示範遠端處理案例中的非同步程式設計。範例首先建立遠端物件的同步委派,並加以叫用來說明等候傳回的執行緒。它會接著使用非同步委派與 ManualResetEvent 物件來叫用遠端物件方法並等候回應。
這個應用程式會在單一電腦或網路上執行。如果您要透過網路執行這個應用程式,您必須以遠端電腦的名稱取代用戶端組態中的 "localhost"。
注意: |
---|
.NET Framework 遠端處理依預設不進行驗證或加密。因此,建議您採取所有必要的步驟,以確認用戶端或伺服器的識別 (Identity),然後再與其進行遠端互動。由於 .NET Framework 遠端處理應用程式需要 FullTrust 權限才能執行,所以如果某個未經授權的用戶端被授與伺服器的存取權,該用戶端就可以執行程式碼,如同它已完全受信任。請務必驗證您的端點並加密通訊資料流,方法包括在網際網路資訊服務 (IIS) 中裝載遠端型別或建置自訂通道接收組來進行這項工作。 |
若要編譯這個範例
在命令提示字元上輸入下列命令:
vbc /t:library ServiceClass.vb vbc /r:ServiceClass.dll Server.vb vbc /r:ServiceClass.dll Client.vb
csc /t:library ServiceClass.cs csc /r:ServiceClass.dll Server.cs csc /r:ServiceClass.dll Client.cs
開啟兩個指向相同目錄的命令提示字元。在其中一個提示字元底下,輸入 server。在另一個命令提示字元底下,輸入 client。
ServiceClass
Imports System
Imports System.Runtime.Remoting
Public Class ServiceClass
Inherits MarshalByRefObject
Public Sub New()
Console.WriteLine("ServiceClass created.")
End Sub
Public Function VoidCall() As String
Console.WriteLine("VoidCall called.")
Return "You are calling the void call on the ServiceClass."
End Function
Public Function GetServiceCode() As Integer
Return Me.GetHashCode()
End Function
Public Function TimeConsumingRemoteCall() As String
Console.WriteLine("TimeConsumingRemoteCall called.")
For i As Integer = 0 To 20000
Console.Write("Counting: " & i.ToString() & vbCr)
Next
Return "This is a time-consuming call."
End Function
End Class
using System;
using System.Runtime.Remoting;
public class ServiceClass : MarshalByRefObject
{
public ServiceClass()
{
Console.WriteLine("ServiceClass created.");
}
public string VoidCall()
{
Console.WriteLine("VoidCall called.");
return "You are calling the void call on the ServiceClass.";
}
public int GetServiceCode()
{
return this.GetHashCode();
}
public string TimeConsumingRemoteCall()
{
Console.WriteLine("TimeConsumingRemoteCall called.");
for (int i = 0; i < 20000; i++)
{
Console.Write("Counting: " + i.ToString());
Console.Write("\r");
}
return "This is a time-consuming call.";
}
}
Server
Imports System
Imports System.Runtime.Remoting
Public Class Server
Public Shared Sub Main()
RemotingConfiguration.Configure("server.exe.config", False)
Console.WriteLine("Waiting...")
Console.ReadLine()
End Sub
End Class
using System;
using System.Runtime.Remoting;
public class Server
{
public static void Main(string[] args)
{
RemotingConfiguration.Configure("server.exe.config", false);
Console.WriteLine("Waiting...");
Console.ReadLine();
}
}
Server.exe.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.remoting>
<application>
<service>
<wellknown
type="ServiceClass, ServiceClass"
mode="Singleton"
objectUri="ServiceClass.rem"
/>
</service>
<channels>
<channel ref="http"
port="8080" />
</channels>
</application>
</system.runtime.remoting>
</configuration>
Client
Imports System
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Messaging
Imports System.Threading
Public Class Client
Inherits MarshalByRefObject
Public Shared e As ManualResetEvent
' Declares two delegates, each of which represents
' a function that returns a string. The names are strictly
' for clarity in the code – there is no difference between
' the two delegates. (In fact, the same delegate type
' could be used for both synchronous and asynchronous
' calls.)
Public Delegate Function RemoteSyncDelegate() As String
Public Delegate Function RemoteAsyncDelegate() As String
' This is the call that the AsyncCallback delegate references
<OneWay()> _
Public Sub OurRemoteAsyncCallback(ByVal ar As IAsyncResult)
Dim del As RemoteAsyncDelegate = CType(ar, AsyncResult).AsyncDelegate
Console.WriteLine(vbLf & "**SUCCESS**: Result of the remote AsyncCallBack: " + del.EndInvoke(ar))
' Signal the thread.
e.Set()
Return
End Sub
Public Shared Sub Main()
'IMPORTANT: .NET Framework remoting does not remote
'static members. This class must be an instance before
'the callback from the asynchronous invocation can reach this client.
Dim clientApp As Client = New Client()
clientApp.Run()
End Sub
Public Sub Run()
'Enable this and the e.WaitOne call at the bottom if you
'are going to make more than one asynchronous call.
e = New ManualResetEvent(False)
Console.WriteLine("Remote synchronous and asynchronous delegates.")
Console.WriteLine(New String("_", 80))
Console.WriteLine()
' This is the only thing you must do in a remoting scenario
' for either synchronous or asynchronous programming
' configuration.
RemotingConfiguration.Configure("Client.exe.config", False)
' The remaining steps are identical to single-
' AppDomain programming.
Dim obj As ServiceClass = New ServiceClass()
' This delegate is a remote synchronous delegate.
Dim Remotesyncdel As RemoteSyncDelegate = New RemoteSyncDelegate(AddressOf obj.VoidCall)
' When invoked, program execution waits until the method returns.
' This delegate can be passed to another application domain
' to be used as a callback to the obj.VoidCall method.
Console.WriteLine(Remotesyncdel())
' This delegate is an asynchronous delegate. Two delegates must
' be created. The first is the system-defined AsyncCallback
' delegate, which references the method that the remote type calls
' back when the remote method is done.
Dim RemoteCallback As AsyncCallback = New AsyncCallback(AddressOf OurRemoteAsyncCallback)
' Create the delegate to the remote method you want to use
' asynchronously.
Dim RemoteDel As RemoteAsyncDelegate = New RemoteAsyncDelegate(AddressOf obj.TimeConsumingRemoteCall)
' Start the method call. Note that execution on this
' thread continues immediately without waiting for the return of
' the method call.
Dim RemAr As IAsyncResult = RemoteDel.BeginInvoke(RemoteCallback, Nothing)
' If you want to stop execution on this thread to
' wait for the return from this specific call, retrieve the
' IAsyncResult returned from the BeginIvoke call, obtain its
' WaitHandle, and pause the thread, such as the next line:
' RemAr.AsyncWaitHandle.WaitOne();
' To wait in general, if, for example, many asynchronous calls
' have been made and you want notification of any of them, or,
' like this example, because the application domain can be
' recycled before the callback can print the result to the
' console.
' e.WaitOne();
' This simulates some other work going on in this thread while the
' async call has not returned.
Dim count As Integer = 0
While Not RemAr.IsCompleted
Console.Write("Not completed: " & count & vbCr)
count = count + 1
' Make sure the callback thread can invoke callback.
Thread.Sleep(1)
End While
End Sub
End Class
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using System.Threading;
using Shared;
public class Client : MarshalByRefObject
{
public static ManualResetEvent e;
// Declares two delegates, each of which represents
// a function that returns a string. The names are strictly
// for clarity in the code – there is no difference between
// the two delegates. (In fact, the same delegate type could
// be used for both synchronous and asynchronous calls.
public delegate string RemoteSyncDelegate();
public delegate string RemoteAsyncDelegate();
// This is the call that the AsyncCallBack delegate references.
[OneWayAttribute]
public void OurRemoteAsyncCallBack(IAsyncResult ar)
{
RemoteAsyncDelegate del = (RemoteAsyncDelegate)((AsyncResult)ar).AsyncDelegate;
Console.WriteLine("\r\n**SUCCESS**: Result of the remote AsyncCallBack: " + del.EndInvoke(ar));
// Signal the thread.
e.Set();
return;
}
public static void Main(string[] Args)
{
// IMPORTANT: .NET Framework remoting does not remote
// static members. This class must be an instance before
// the callback from the asynchronous invocation can reach this client.
Client clientApp = new Client();
clientApp.Run();
}
public void Run()
{
// Enable this and the e.WaitOne call at the bottom if you
// are going to make more than one asynchronous call.
e = new ManualResetEvent(false);
Console.WriteLine("Remote synchronous and asynchronous delegates.");
Console.WriteLine(new String('-', 80));
Console.WriteLine();
// This is the only thing you must do in a remoting scenario
// for either synchronous or asynchronous programming
// configuration.
RemotingConfiguration.Configure("Client.exe.config", false);
// The remaining steps are identical to single-
// AppDomain programming.
ServiceClass obj = new ServiceClass();
// This delegate is a remote synchronous delegate.
RemoteSyncDelegate Remotesyncdel = new RemoteSyncDelegate(obj.VoidCall);
// When invoked, program execution waits until the method returns.
// This delegate can be passed to another application domain
// to be used as a callback to the obj.VoidCall method.
Console.WriteLine(Remotesyncdel());
// This delegate is an asynchronous delegate. Two delegates must
// be created. The first is the system-defined AsyncCallback
// delegate, which references the method that the remote type calls
// back when the remote method is done.
AsyncCallback RemoteCallback = new AsyncCallback(this.OurRemoteAsyncCallBack);
// Create the delegate to the remote method you want to use
// asynchronously.
RemoteAsyncDelegate RemoteDel = new RemoteAsyncDelegate(obj.TimeConsumingRemoteCall);
// Start the method call. Note that execution on this
// thread continues immediately without waiting for the return of
// the method call.
IAsyncResult RemAr = RemoteDel.BeginInvoke(RemoteCallback, null);
// If you want to stop execution on this thread to
// wait for the return from this specific call, retrieve the
// IAsyncResult returned from the BeginIvoke call, obtain its
// WaitHandle, and pause the thread, such as the next line:
// RemAr.AsyncWaitHandle.WaitOne();
// To wait in general, if, for example, many asynchronous calls
// have been made and you want notification of any of them, or,
// like this example, because the application domain can be
// recycled before the callback can print the result to the
// console.
//e.WaitOne();
// This simulates some other work going on in this thread while the
// async call has not returned.
int count = 0;
while (!RemAr.IsCompleted)
{
Console.Write("\rNot completed: " + (++count).ToString());
// Make sure the callback thread can invoke callback.
Thread.Sleep(1);
}
}
}
Client.exe.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.runtime.remoting>
<application>
<client>
<wellknown
type="ServiceClass, ServiceClass"
url="https://localhost:8080/ServiceClass.rem"
/>
</client>
<channels>
<channel
ref="http"
port="0"
/>
</channels>
</application>
</system.runtime.remoting>
</configuration>