远程处理示例:异步远程处理
下面的示例应用程序说明远程处理方案中的异步编程。该示例首先创建远程对象的同步委托并且调用它来阐释等待返回的线程。然后,它使用异步委托和 ManualResetEvent 对象来调用远程对象方法并等待响应。
该应用程序可在单台计算机上运行或通过网络运行。如果要在网络上运行该应用程序,必须用远程计算机的名称替换客户端配置中的"localhost"。
警告
.NET Framework 远程处理在默认情况下不进行身份验证或加密。因此,建议您在与客户端或服务器进行远程交互之前,采取任何必要的措施以确认它们的身份。因为 .NET Framework 远程处理应用程序需要 FullTrust 权限才能执行,所以,未经授权的客户端如果被授予访问您服务器的权限,该客户端就可能像完全受信任的客户端一样执行代码。应始终验证终结点的身份并将通信流加密,通过在 Internet 信息服务 (IIS) 中承载远程类型,或者通过生成自定义信道接收对来完成这项工作。
编译此示例
在命令提示符处键入以下命令:
csc /t:library /out:ServiceClass.dll ServiceClass.cs
vbc -r:ServiceClass.dll RemoteAsyncVB.vb
csc /r:ServiceClass.dll Server.cs
csc /r:ServiceClass.dll RemoteAsync.cs
打开两个指向同一目录的命令提示符。在其中一个中键入 server。在另一个中键入 RemoteAsyncVB 或 RemoteAsync。
RemoteAsyncVB.vb
Imports System
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Messaging
Imports System.Threading
Imports Microsoft.VisualBasic
Public Class RemotingDelegates
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.)
Delegate Function RemoteSyncDelegate() As String
Delegate Function RemoteAsyncDelegate() As String
<MTAThread()> _
Public Shared Sub Main()
e = New ManualResetEvent(False)
Console.WriteLine("Remote synchronous and asynchronous delegates.")
Console.WriteLine(New [String]("-"c, 80))
Console.WriteLine()
' Configuration is required in a remoting scenario, for both
' synchronous and asynchronous programming. You can use either
' a configuration file, as shown here, or method calls.
RemotingConfiguration.Configure("SyncAsync.exe.config")
' The remaining steps are identical to single-
' AppDomain programming. First, create the service class. In this
' case, the object is actually created on the server, and only a
' single instance is created. (See the server configuration file,
' ServerVB.exe.config.)
Dim obj As New ServiceClass()
If RemotingServices.IsTransparentProxy(obj) Then
Console.WriteLine("It's remote.")
Else
Console.WriteLine("Uh-oh. It's local.")
Return
End If
' This delegate is a remote synchronous delegate.
Dim RemoteSyncDel As New RemoteSyncDelegate(AddressOf obj.VoidCall)
' When invoked, program execution waits until the method returns.
' The return value is displayed to the console.
Console.WriteLine(RemoteSyncDel())
' Next, the remote asynchronous call.
' First, create an AsyncCallback delegate that represents
' the method to be executed when the asynchronous method has finished
' executing.
Dim RemoteCallback As New AsyncCallback(AddressOf RemotingDelegates.OurRemoteAsyncCallBack)
' Second, create the delegate to the method to
' executed asynchronously.
Dim RemoteDel As New RemoteAsyncDelegate(AddressOf obj.TimeConsumingRemoteCall)
' Finally, begin the method invocation.
' 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 block on the return:
' RemAr.AsyncWaitHandle.WaitOne();
' RemoteCallback(RemAr);
' Provide some feedback to indicate continued processing prior to
' the return of the asynchronous call.
Dim count As Integer = 0
While Not RemAr.IsCompleted
Console.Write("Not completed -- " & count & vbCr)
count += 1
' The callback is made on a low priority worker thread. Blocking
' the foreground thread for a millisecond each time enables the
' callback to execute.
Thread.Sleep(New TimeSpan(TimeSpan.TicksPerMillisecond))
End While
Console.WriteLine(vbCrLf & "Completed.")
End Sub 'Main
' This is the method that the server calls when asynchronous
' execution is complete. The method is represented by an AsyncCallBack
' delegate, which determines its signature.
Public Shared Sub OurRemoteAsyncCallBack(ByVal iar As IAsyncResult)
' The following nested cast looks confusing, because of the nature
' of the Visual Basic casting operator, CType. Note that you could
' get the same result with two separate casts:
' Dim ar As AsyncResult = CType(iar, AsyncResult)
' Dim del As RemoteAsyncDelegate = CType(ar.AsyncDelegate, RemoteAsyncDelegate)
' The first line casts the interface to an AsyncResult object to
' access the AsyncDelegate property (in the second
' line). This property returns a reference to the original delegate,
' which must be cast back to its own type.
Dim del As RemoteAsyncDelegate = CType(CType(iar, AsyncResult).AsyncDelegate, RemoteAsyncDelegate)
Console.WriteLine(vbCrLf & "Result of the remote AsyncCallBack: " & del.EndInvoke(iar))
Return
End Sub 'OurRemoteAsyncCallBack
End Class 'RemotingDelegates
RemoteAsync.cs
using System;
using System.Reflection;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Channels;
using System.Threading;
public class RemotingDelegates : MarshalByRefObject{
public static ManualResetEvent e;
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.
RemotingDelegates HandlerInstance = new RemotingDelegates();
HandlerInstance.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("SyncAsync.exe.config");
// 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);
}
}
}
Server.cs
using System;
using System.Runtime.Remoting;
public class Server{
public static void Main(){
RemotingConfiguration.Configure("server.exe.config");
Console.WriteLine("Waiting...");
Console.ReadLine();
}
}
ServiceClass.cs
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.exe.config
<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>
SyncAsync.exe.config
<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>