다음을 통해 공유


원격 서비스 예제: 비동기 원격 서비스

이 항목은 이전 버전의 기존 응용 프로그램과의 호환성을 위해 유지되고 있으나 새로운 개발에는 권장되지 않는 레거시 기술에 대해 설명합니다. 분산 응용 프로그램은 이제 WCF(Windows Communication Foundation)를 사용하여 개발됩니다.

다음 샘플 응용 프로그램은 원격 시나리오의 비동기 프로그래밍을 보여 줍니다. 이 예제에서는 먼저 원격 개체에 대한 동기 대리자를 만들고 호출하여 반환 시 대기 중인 스레드를 보여 줍니다. 그런 다음 비동기 대리자와 ManualResetEvent 개체를 사용하여 원격 개체 메서드를 호출하고 응답을 기다립니다.

이 응용 프로그램은 단일 컴퓨터에서 실행되거나 네트워크에서 실행됩니다. 네트워크를 통해 이 응용 프로그램을 실행하려면 클라이언트 구성의 **"localhost"**를 원격 컴퓨터의 이름으로 바꿉니다.

0sa925ka.Caution(ko-kr,VS.100).gif주의:
.NET Framework Remoting에서는 기본적으로 인증 또는 암호화를 수행하지 않습니다. 따라서 원격으로 클라이언트 또는 서버와 상호 작용하기 전에 해당 클라이언트 또는 서버의 ID를 확인하는 데 필요한 모든 단계를 수행하는 것이 좋습니다. .NET Framework Remoting 응용 프로그램이 실행되려면 FullTrust 권한이 필요하기 때문에 인증되지 않은 클라이언트가 서버에 액세스할 수 있도록 허용된 경우에는 클라이언트가 완전히 신뢰된 것처럼 코드를 실행할 수 있습니다. 따라서 원격 형식을 IIS(인터넷 정보 서비스)에 호스팅하거나 이 작업을 수행할 사용자 지정 채널 싱크 쌍을 빌드하여 항상 끝점을 인증하고 통신 스트림을 암호화하십시오.

이 샘플을 컴파일하려면

  1. 명령 프롬프트에 다음 명령을 입력합니다.

    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
    
  2. 동일한 디렉터리를 가리키는 두 개의 명령 프롬프트를 엽니다. 한 프롬프트에는 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>

참고 항목

개념

비동기 원격 서비스

관련 자료

원격 서비스 예제