远程处理示例:异步远程处理

下面的示例应用程序演示如何在远程处理方案中进行异步编程。该示例首先为远程对象创建一个同步委托,然后调用该委托来阐释正在等待返回的线程。接下来,它使用异步委托和 ManualResetEvent 对象调用远程对象并等待响应。

此应用程序可以在一台计算机上运行,也可以在网络上运行。如果要在网络上运行此应用程序,必须用远程计算机的名称替换客户端配置中的“localhost”。

Caution note警告:

.NET Framework 远程处理在默认情况下不进行身份验证或加密。因此,在与客户端或服务器进行远程交互之前,建议您先执行所有必要的步骤来确认它们的身份。由于 .NET Framework 远程处理应用程序需要 FullTrust 权限才能执行,因此未经授权的客户端一旦获得服务器的访问权限,它就可以像完全受信任的客户端那样执行代码。应始终验证终结点的身份并对通信流加密,通过在 Internet 信息服务 (IIS) 中承载远程类型,或者通过生成自定义信道接收器对,可以完成这项工作。

编译此示例

  1. 在命令提示符处,键入下列命令:

    [Visual Basic]

    vbc /t:library ServiceClass.vb
    vbc /r:ServiceClass.dll Server.vb
    vbc /r:ServiceClass.dll Client.vb
    

    [C#]

    csc /t:library ServiceClass.cs
    csc /r:ServiceClass.dll Server.cs
    csc /r:ServiceClass.dll Client.cs
    
  2. 打开指向同一目录的两个命令提示符。在一个命令提示符下,键入 server。在另一个命令提示符下,键入 client

ServiceClass

[Visual Basic]

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

[C#]

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.";
    }
}

服务器

[Visual Basic]

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

[C#]

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>

客户端

[Visual Basic]

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

[C#]

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>

请参见

概念

异步远程处理

其他资源

远程处理示例

Footer image

版权所有 (C) 2007 Microsoft Corporation。保留所有权利。