이 항목은 아직 평가되지 않았습니다.- 이 항목 평가

Microsoft .NET 원격 작업: 기술 개요

Piet Obermeyer, Jonathan Hawkins
Microsoft Corporation

요약 : 이 기사에서는 TCP 채널이나 HTTP 채널을 사용한 예제를 비롯하여 Microsoft .NET 원격 프레임워크의 기술적인 개요에 대해 설명합니다(15페이지/인쇄 페이지 기준).

목차

소개 소개
원격 개체 원격 개체
프록시 개체 프록시 개체
채널 채널
활성화 활성화
임대 개체 수명 임대 개체 수명
결론 결론
부록 A: TCP 채널을 사용한 원격 작업 예제 부록 A: TCP 채널을 사용한 원격 작업 예제
부록 B: HTTP 채널을 사용한 원격 작업 예제 부록 B: HTTP 채널을 사용한 원격 작업 예제


소개

Microsoft® .NET 원격 작업은 프레임워크를 통해 서로 다른 응용 프로그램 도메인에 있는 개체 간에 상호 작용할 수 있도록 합니다. 프레임워크는 원격 응용 프로그램에서 메시지를 주고 받는 통신 채널뿐만 아니라 활성화 및 수명 지원을 포함한 여러 서비스를 제공합니다. 각 채널로 메시지를 전송하기 전에 메시지를 인코딩 및 디코딩하는 데 포맷기가 사용됩니다. 응용 프로그램에서 성능이 중요한 곳에서는 이진 인코딩이 사용되고 다른 원격 프레임워크와의 상호 운용성이 필수적인 곳에서는 XML 인코딩이 사용됩니다. 모든 XML 인코딩은 응용 프로그램 도메인 간에 메시지를 전송할 때 SOAP 프로토콜을 사용합니다. 원격 작업은 보안을 고려하여 디자인되었으며, 스트림이 채널을 통해 전송되기 전에 먼저 순차적인 스트림뿐만 아니라 보안 싱크가 메시지에 액세스할 수 있도록 하는 몇몇 후크가 제공됩니다.

기본 프레임워크에서 지원하지 않는 원격 개체의 수명을 유지 관리하는 작업은 때로 성가신 일입니다. .NET 원격 작업에서는 선택 가능한 여러 수명 모델을 제공합니다. 이 수명 모델들은 다음과 같은 두 범주로 요약됩니다.

  • 클라이언트 활성화 개체

  • 서버 활성화 개체

클라이언트 활성화 개체는 임대 기반 수명 관리자에 의해 제어되는데, 이 관리자는 개체의 임대 기간이 만료되면 개체를 삭제하고 다른 삭제 항목들과 함께 둡니다. 서버 활성화 개체의 경우에는 개발자가 "단일 호출" 또는 "단일 항목" 모델을 선택할 수 있습니다.

원격 개체

원격 프레임워크의 주요 목적 중 하나는 원격 개체에 대한 메서드를 호출하고 결과를 반환하는 복잡함을 숨겨 주는 하부 구조를 제공하는 것입니다. 호출자의 응용 프로그램 도메인 외부에 있는 개체는 같은 시스템 상에서 실행되는 경우라도 원격 개체로 간주됩니다. 응용 프로그램 도메인 내에서는 모든 개체가 참조에 의해 전달되는 반면 원시 데이터 형식은 값에 의해 전달됩니다. 로컬 개체 참조는 이 참조가 만들어진 응용 프로그램에서만 사용할 수 있기 때문에 이러한 형식으로 원격 메서드 호출에 전달되거나 반환될 수 없습니다. 응용 프로그램 도메인 경계를 통과해야 하는 모든 로컬 개체는 값에 의해 전달되어야 합니다. 그리고 [serializable] 사용자 정의 특성으로 표시해야 하며 ISerializable 인터페이스를 구현해야 합니다. 개체가 매개 변수로서 전달되면, 프레임워크는 개체를 순차화하여 대상 응용 프로그램 도메인에 전송합니다. 그러면 대상 응용 프로그램 도메인에서 개체가 다시 만들어집니다. 순차화할 수 없는 로컬 개체는 다른 응용 프로그램 도메인으로 전송할 수 없기 때문에 원격 개체로 사용할 수 없습니다.

MarshalByRefObject에서 파생된 모든 개체는 원격 개체로 변경할 수 있습니다. 클라이언트가 원격 개체를 활성화하면 원격 개체에 대한 프록시를 받게 됩니다. 프록시에서 모든 작업은 적절하게 방향을 정해져 원격 하부 구조가 호출을 가로채거나 제대로 전달하도록 합니다. 이러한 프록시 작업은 성능에 영향을 미치지만, 프록시와 원격 개체가 동일한 응용 프로그램 도메인에 있을 경우에는 JIT 컴파일러 및 실행 엔진(EE)이 최적화되어 불필요한 성능 저하를 방지합니다. 프록시와 원격 개체가 각각 다른 응용 프로그램 도메인에 있는 경우에는, 스택에 있는 모든 메서드 호출 매개 변수는 메시지로 변환되어 원격 응용 프로그램 도메인에 전송됩니다. 원격 응용 프로그램 도메인에서 메시지는 스택 프레임으로 다시 바뀌고 메서드 호출이 실행됩니다. 메서드 호출에서 결과를 반환할 때는 동일한 프로시저가 사용됩니다.

프록시 개체

프록시 개체는 클라이언트가 원격 개체를 활성화할 때 만들어집니다. 프록시 개체가 원격 개체의 대표 역할을 수행하며 프록시 상에서 만들어진 모든 호출이 정확한 원격 개체 인스턴스에 제대로 전달되도록 합니다. 프록시 개체의 기능을 정확하게 이해하려면 프록시 개체에 대해 자세히 알아볼 필요가 있습니다. 클라이언트가 원격 개체를 활성화하면, 프레임워크는 원격 개체의 인터페이스 메서드와 함께 모든 클래스의 목록이 들어 있는 TransparentProxy 클래스의 로컬 인스턴스를 만듭니다. TransparentProxy 클래스는 만들어질 때 CLR에 등록되기 때문에 프록시의 모든 메서드 호출이 실행 시 노출됩니다. 여기에서 호출은 원격 개체의 메서드가 유효한지, 원격 개체의 인스턴스가 프록시와 동일한 응용 프로그램 도메인에 있는지를 알아보기 위해 쓰입니다. 원격 개체의 메서드가 유효하고 원격 개체의 인스턴스가 프록시와 동일한 응용 프로그램 도메인에 있으면 간단한 메서드 호출이 실제 개체로 라우팅됩니다. 개체가 다른 응용 프로그램 도메인에 있는 경우, 스택에 있는 호출 매개 변수는 IMessage 개체로 패키지가 되고, 개체의 Invoke 메서드를 호출하여 RealProxy 클래스로 전달됩니다. RealProxy 클래스(또는 클래스의 내부 구현)는 원격 개체로 메시지를 전달합니다. TransparentProxyRealProxy 클래스는 둘 다 원격 개체가 활성화될 때 만들어지지만, TransparentProxy 클래스만 클라이언트에게로 반환됩니다.

이러한 프록시 개체를 좀 더 이해하려면 ObjRef에 대해 간단하게라도 살펴볼 필요가 있습니다. ObjRef에 대한 자세한 설명은 활성화 항목에 잘 나와 있습니다. 다음은 ObjRef와 두 개의 프록시 클래스가 어떤 식으로 연관되어 있는지를 간단하게 설명하는 시나리오입니다. 이 시나리오는 프로세스에 대한 개괄적인 설명입니다. 따라서 개체가 클라이언트 활성화 개체인지 서버 활성화 개체인지에 따라, 또는 단일 항목 개체인지 단일 호출 개체인지에 따라 이 시나리오와는 다소 다른 다양한 결과가 나올 수 있습니다.

  • 원격 개체는 원격 시스템의 응용 프로그램 도메인에 등록됩니다. 이 개체는 ObjRef를 만들도록 마샬링됩니다. ObjRef에는 네트워크 상의 어느 위치에서든 원격 개체를 찾아 액세스할 수 있도록 필요한 모든 정보가 있습니다. 이 정보에는 클래스의 강력한 이름, 클래스의 계층 구조(부모 클래스), 클래스가 구현하는 모든 인터페이스 이름, 개체 URI, 등록되어 사용할 수 있는 모든 채널에 대한 상세 정보 등이 들어 있습니다. 원격 프레임워크가 특정 개체에 대한 요청을 받으면 개체 URI를 사용하여 원격 개체에 대해 만들어진 ObjRef 인스턴스를 검색합니다.

  • 클라이언트는 CreateInstance와 같은 Activator 함수나 new 함수 중 하나를 호출하여 원격 개체를 활성화합니다. 서버 활성화 개체의 경우에는, 원격 개체의 TransparentProxy가 클라이언트 응용 프로그램 도메인에서 만들어지고 클라이언트로 반환되며 원격 호출은 만들어지지 않습니다. 원격 개체는 클라이언트가 원격 개체에 대해 메서드를 호출할 때만 활성화됩니다. 이 시나리오는 클라이언트 활성 개체에서는 사용할 수 없습니다. 클라이언트에서는 요청 시 프레임워크가 개체를 활성화하는 것을 전제로 하기 때문입니다. 클라이언트가 활성화 메서드 중 하나를 호출한 경우, 활성화 프록시가 클라이언트에 만들어지고 URL 및 개체 URI를 종점으로서 사용하여 서버에서 원격 호출이 원격 활성기로 초기화됩니다. 원격 활성기는 개체를 활성화하고 ObjRef는 클라이언트에 스트림되는 데, 이 개체는 클라이언트로 반환되는 TransparentProxy를 만들도록 마샬링이 해제됩니다.

  • 마샬링을 해제하는 동안, ObjRef는 원격 개체의 메서드 정보를 추출하도록 구문 분석되고 TransparentProxyRealProxy의 두 개체가 만들어집니다. TransparentProxy가 CLR에 등록되기 전에 구문 분석된 ObjRef의 내용이 TransparentProxy의 내부 테이블에 추가됩니다.

TransparentProxy는 대체하거나 확장할 수 없는 내부 클래스입니다. 반면에, RealProxyObjRef 클래스는 공용 클래스로서 필요한 경우에는 확장하고 사용자 지정할 수 있습니다. 예를 들어, RealProxy 클래스는 원격 개체에서 모든 함수 호출을 처리하기 때문에 로드 균형 조정과 같은 작업을 수행하는 데 적합합니다. Invoke를 호출하면 RealProxy에서 파생된 클래스는 네트워크 상의 서버에 대한 로드 정보를 얻어 적절한 서버로 호출을 라우팅할 수 있습니다. 채널에서 요청된 ObjectURI에 대해 MessageSink를 요청하고 SyncProcessMessage 또는 AsyncProcessMessage를 호출하여 요청된 원격 개체로 호출을 전달합니다. 호출을 반환하면, RemotingServices 클래스에서 PropagateMessageToProxy를 호출하여 반환 매개 변수를 스택에 다시 넣습니다.

다음은 RealProxy에서 파생된 클래스를 사용하는 방법을 보여주는 코드입니다.

   MyRealProxy proxy = new MyRealProxy(typeof(Foo));
   Foo obj = (Foo)proxy.GetTransparentProxy();
   int result = obj.CallSomeMethod(); 

위에서 얻은 TransparentProxy는 다른 응용 프로그램 도메인에 전달할 수 있습니다. 두 번째 클라이언트가 프록시의 메서드 호출을 시도하면 원격 프레임워크가 MyRealProxy의 인스턴스를 만들고, 어셈블리를 사용할 수 있는 경우에는 모든 호출은 이 인스턴스를 통해 라우팅됩니다. 어셈블리를 사용할 수 없는 경우에는 호출은 기본 설정된 원격 RealProxy를 통해 라우팅됩니다.

ObjRef는 기본 ObjRef 속성인 TypeInfo, EnvoyInfo, ChannelInfo에 대한 대체 속성을 제공하므로 손쉽게 사용자 지정할 수 있습니다. 다음은 이러한 작업을 보여주는 코드입니다.

public class ObjRef {
  public virtual IRemotingTypeInfo TypeInfo 
  {
    get { return typeInfo;}
    set { typeInfo = value;}
  }
  public virtual IEnvoyInfo EnvoyInfo
  {
    get { return envoyInfo;}
    set { envoyInfo = value;}
  }
  public virtual IChannelInfo ChannelInfo 
  {
    get { return channelInfo;}
    set { channelInfo = value;}
  }
}

채널

채널은 원격 개체를 대상으로 하는 메시지 전송에 사용됩니다. 클라이언트가 원격 개체의 메서드를 호출하면 호출과 관련된 다른 정보 뿐만 아니라 매개 변수도 채널을 통해 원격 개체로 전송됩니다. 호출의 모든 결과는 동일한 방법으로 클라이언트로 다시 반환됩니다. 클라이언트는 "서버"에 등록된 채널 중 어떤 채널이든 선택하여 원격 개체와 통신할 수 있습니다. 따라서 개발자는 각자의 요구 사항에 가장 적합한 채널을 선택할 수 있습니다. 또한 모든 기존 채널을 사용자 지정하거나 다른 통신 프로토콜을 사용하는 채널을 새로 만들 수 있습니다. 채널을 선택할 때는 다음과 같은 규칙을 따릅니다.

  • 원격 개체가 호출되기 전에 최소한 한 개의 채널이 원격 프레임워크에 등록되어 있어야 합니다. 채널을 먼저 등록한 다음 개체를 등록해야 합니다.

  • 채널은 각 응용 프로그램 도메인에 등록됩니다. 단일 프로세스에 여러 개의 응용 프로그램 도메인이 있을 수 있습니다. 프로세스가 끝나면 등록된 모든 채널들이 자동으로 삭제됩니다.

  • 동일한 포트에서 수신 대기하는 채널을 같은 채널로 두 번 이상 등록할 수 없습니다. 채널이 각 응용 프로그램 도메인마다 등록되어 있다 할지라도 동일한 시스템에 있는 다른 응용 프로그램 도메인은 동일한 포트에서 수신 대기하는 채널을 같은 채널로 등록할 수 없습니다.

  • 클라이언트는 등록된 모든 채널을 사용하여 원격 개체와 통신할 수 있습니다. 클라이언트가 원격 개체를 연결할 때 원격 프레임워크는 원격 개체가 올바른 채널에 연결되도록 합니다. 클라이언트는 원격 개체와 통신하기 전에 ChannelService 클래스의 RegisterChannel을 호출해야 합니다.


모든 채널은 IChannel에서 파생되며 채널의 목적에 따라 IChannelReceiverIchannelSender를 구현합니다. 대부분의 채널은 받는 사람과 보내는 사람 인터페이스를 양방향으로 통신할 수 있도록 구현됩니다. 클라이언트가 프록시에서 메서드를 호출하면, 원격 프레임워크에서 호출을 가로채서 RealProxy 클래스 또는 RealProxy를 구현하는 클래스의 인스턴스에 전달되는 메시지로 변경합니다. RealProxy는 처리할 메시지 싱크에 메시지를 전달합니다. 메시지 싱크는 원격 개체에서 등록한 채널과 연결을 구축한 다음, 메시지가 원격 개체 자체로 발송되는 채널을 통해 메시지를 다른 응용 프로그램 도메인으로 전송합니다. 원격 개체가 활성화되면, 원격 개체와 통신할 수 있는 메시지 싱크는 선택된 채널의 CreateMessageSink를 호출하여 클라이언트가 선택한 채널에서 검색됩니다.

원격 프레임워크에서 한가지 복잡한 문제는 원격 개체와 채널과의 관계입니다. 예를 들어, 호출을 받았을 때만 SingleCall 원격 개체가 활성화되는 경우, 이 개체는 개체를 연결할 클라이언트 수신을 어떤 방식으로 관리해야 하는가 하는 문제입니다.

이 문제에 대한 해결책이 될 수 있는 하나의 실마리는 원격 개체들이 채널들을 공유한다는 사실입니다. 원격 개체 하나가 채널 하나를 소유하지 않습니다. 원격 개체를 호스팅하는 서버 응용 프로그램은 원격 프레임워크에 나타낼 개체뿐만 아니라 필요한 채널을 등록해야 합니다. 채널이 등록되면 지정된 포트에서 클라이언트 요청을 자동으로 수신하기 시작합니다. 원격 개체가 등록되면 개체에 대한 ObjRef가 만들어지고 테이블에 저장됩니다. 채널에 요청이 들어오면, 원격 프레임워크가 메시지를 검사하여 대상 개체를 확인하고 개체 참조의 테이블을 확인하여 테이블의 참조를 찾아냅니다. 개체 참조가 있으면, 프레임워크 대상 개체를 테이블에서 검색하거나 필요할 때 활성화한 다음, 프레임워크가 호출을 개체에 전달합니다. 동기식 호출의 경우, 메시지 호출 기간 동안에는 클라이언트와의 연결이 유지 관리됩니다. 각 클라이언트 연결은 자체 스레드에서 처리되기 때문에 단일 채널을 통해 여러 클라이언트를 동시에 처리할 수 있습니다.

업무 응용 프로그램을 만들 때 가장 중요하게 고려해야 할 사항은 바로 보안입니다. 따라서 개발자는 업무 요구 사항을 충족시킬 수 있도록 권한 부여나 암호화와 같은 보안 기능을 원격 메서드 호출에 추가해야 합니다. 이러한 요구 사항을 충족시키기 위해, 채널을 사용자 지정할 수 있으므로 개발자들은 원격 개체를 대상으로 메시지를 전송하는 실제 메커니즘을 원하는 대로 제어할 수 있습니다. 모든 메시지는 SecuritySink, TransportSink, FormatterSink를 통해 전달된 후에 원격 응용 프로그램으로 전송됩니다. 원격 응용 프로그램에서 메시지는 동일한 싱크를 통해 역순으로 이동합니다.

HTTP 채널

HTTP 채널은 SOAP 프로토콜을 이용하여 원격 개체를 대상으로 메시지를 전송합니다. 모든 메시지는 SOAP 포맷기를 통해 전달됩니다. SOAP 포맷기에서 메시지는 XML로 변경되고 순차화되어 SOAP 헤더가 스트림에 추가됩니다. 이진 포맷기도 지정할 수 있는데, 이진 포맷기는 이진 데이터 스트림으로 결과가 나타납니다. 그런 다음 데이터 스트림은 HTTP 프로토콜을 사용하여 대상 URI에 전송됩니다.

TCP 채널

TCP 채널은 이진 포맷기를 사용하여 이진 스트림에 모든 메시지를 순차화하고 TCP 프로토콜을 사용하여 스트림을 대상 URI에 전송합니다.

활성화

원격 프레임워크는 원격 개체의 서버 및 클라이언트 활성화를 지원합니다. 일반적으로 서버 활성화는 메서드 호출 간의 상태를 유지 관리하는 데 원격 개체가 필요하지 않은 경우에 사용됩니다. 또한 여러 클라이언트가 동일한 개체 인스턴스의 메서드를 호출하거나 개체가 함수 호출 간의 상태를 유지 관리하는 경우에 사용됩니다. 한편, 클라이언트 활성화 개체는 클라이언트에서 인스턴스를 생성합니다. 여기서 클라이언트는 개체 수명 관리 목적으로 제공된 임대 기반 시스템을 사용하여 원격 개체의 수명을 관리합니다.

원격 프레임워크에 모든 원격 개체를 등록하고 나면 클라이언트가 원격 개체를 액세스할 수 있습니다. 호스트 응용 프로그램이 시작되고 ChannelServices로 하나 이상의 채널을 등록한 다음 RemotingServices로 하나 이상의 원격 개체를 등록하여 RemotingServices가 종료될 때까지 대기하는 호스트 응용 프로그램에서 일반적으로 개체 등록이 이루어집니다. 등록된 채널 및 개체는 각각 등록된 프로세스가 활성 상태일 동안만 사용할 수 있습니다. 프로세스가 끝나면 이 프로세스에 등록된 모든 채널과 개체는 각각 등록된 원격 서비스에서 자동으로 제거됩니다. 다음은 프레임워크에 원격 개체를 등록할 때 필요한 네 가지 정보입니다.

  1. 클래스가 포함되어 있는 어셈블리 이름

  2. 원격 개체의 Type 이름

  3. 클라이언트가 개체의 위치를 찾을 때 사용할 개체 URI

  4. 서버 활성화에 필요한 SingleCall 또는 Singleton와 같은 개체 모드

RegisterWellKnownType를 호출하여 위 네 가지 정보를 매개 변수로서 전달하는 방식이나, 또는 구성 파일에 이 정보를 저장한 다음 ConfigureRemoting을 호출하여 구성 파일의 이름을 매개 변수로서 전달하는 방식으로 원격 개체를 등록할 수 있습니다. 이러한 두 함수들은 똑같은 기능을 수행하므로 두 방식 중 어떤 것을 사용해도 원격 개체를 등록할 수 있습니다. 호스트 응용 프로그램을 다시 컴파일하지 않고도 구성 파일의 내용을 변경할 수 있기 때문에 ConfigureRemoting를 사용하는 방식이 훨씬 편리합니다. 다음은 HelloService 클래스를 SingleCall 원격 개체로 등록하는 방법을 보여주는 코드입니다.

RemotingServices.RegisterWellKnownType(
  "server",
  "Samples.HelloServer",
  "SayHello",
   WellKnownObjectMode.SingleCall);

여기에서 "server"는어셈블리 이름이고 HelloServer는 클래스 이름이며 SayHello는 개체 URI입니다.

개체가 등록되면 프레임워크는 이 원격 개체에 대해 개체 참조를 만든 다음, 개체에 필요한 메타데이터를 어셈블리에서 추출합니다. URI와 어셈블리 이름이 함께 있는 이 정보는 개체 참조에 저장됩니다. 개체 참조는 등록된 원격 개체를 추적하는 데 사용되는 원격 프레임워크 테이블에 정리됩니다. 등록 프로세스에서 원격 개체 자체에 대한 인스턴스를 생성하지는 않음에 유의해야 합니다. 클라이언트가 개체의 메서드 호출을 시도하는 경우 또는 클라이언트쪽에서 개체를 활성화하는 경우에만 원격 개체의 인스턴스가 생성됩니다.

원격 개체의 URI를 알고 있는 모든 클라이언트는 ChannelServices로 채널을 등록하고 new, GetObject 또는 CreateInstance를 호출하여 개체를 활성화함으로써 원격 개체에 대한 프록시를 얻을 수 있습니다. 다음은 이러한 작업이 수행되는 예를 보여 주는 코드입니다.

      ChannelServices.RegisterChannel(new TCPChannel);
      HelloServer obj = (HelloServer)Activator.GetObject(
         typeof(Samples.HelloServer), "tcp://localhost:8085/SayHello"); 

여기에서 "tcp://localhost:8085/SayHello"는 포트 8085의 TCP를 사용하여 SayHello 종점의 원격 개체로 연결할 수 있도록 지정합니다. 컴파일러에는 이 클라이언트 코드가 컴파일될 때 HelloServer 클래스에 관한 유형 정보가 필요합니다. 이 정보는 다음과 같은 방법 중 하나를 사용하여 얻을 수 있습니다.

  • HelloService 클래스가 저장되어 있는 어셈블리에 참조를 제공합니다.

  • 원격 개체를 구현 및 인터페이스 클래스로 나누어 이 인터페이스를 클라이언트를 컴파일할 때 참조로서 사용합니다.

  • SOAPSUDS 도구를 사용하여 필요한 메타데이터를 종점에서 직접 추출합니다. 이 도구는 제공된 종점과 연결되어 메타데이터를 추출하며 클라이언트를 컴파일하는 데 사용되는 어셈블리 또는 원본 코드를 만듭니다.


GetObjectnew는 서버 활성화에 사용할 수 있습니다. 이 호출 중 하나가 만들어질 때 개체가 인스턴스를 생성하지 않는다는 사실에 유의해야 합니다. 사실상 어떤 네트워크 호출도 전혀 생성되지 않습니다. 프레임워크는 메타데이터에서 충분한 정보를 얻어서 원격 개체와 연결하지 않고도 프록시를 만들 수 있습니다. 클라이언트가 프록시의 메서드를 호출할 경우에만 네트워크 연결이 구축됩니다. 서버에 호출이 도달하면 프레임워크는 메시지에서 URI를 추출하고 원격 프레임워크 테이블을 검사하여 URI와 일치하는 개체 참조를 찾아냅니다. 그런 다음, 필요한 경우에 개체의 인스턴스를 생성하여 메서드 호출을 개체에 전달합니다. 개체가 SingleCall로 등록된 경우, 메서드 호출이 완료되면 이 개체는 삭제됩니다. 호출된 각 메서드에 개체의 새로운 인스턴스가 만들어집니다. GetObject는 URL을 매개 변수로서 지정할 수 있고, 반면 new는 구성 파일에서 URL을 얻는다는 점이 이 두 함수의 유일한 차이점입니다.

CreateInstancenew는 모두 클라이언트 활성화 개체에 사용할 수 있습니다. 이 두 함수 모두 매개 변수가 있는 생성자를 이용하여 개체의 인스턴스를 생성할 수 있습니다. 클라이언트 활성화 개체의 수명은 원격 프레임워크에서 제공한 임대 서비스에서 제어합니다. 개체 임대에 대한 자세한 정보는 다음 항목에서 설명됩니다.

임대 개체 수명

각 응용 프로그램 도메인에는 도메인에서 임대를 관리하는 임대 관리자가 있습니다. 모든 임대에 대해서 임대 만료 기간을 주기적으로 확인합니다. 임대 기간이 만료되면 임대를 갱신할 수 있는 하나 이상의 임대 스폰서가 호출됩니다. 임대를 갱신할 스폰서가 없으면 임대 관리자는 임대를 삭제하고 개체를 다른 삭제 항목과 함께 둡니다. 임대 관리자는 남은 임대 기간 순으로 분류한 임대 목록을 유지 관리합니다. 남은 임대 기간이 가장 짧은 임대가 목록의 맨 위에 저장됩니다.

임대는 ILease 인터페이스를 구현하고, 갱신할 정책 및 메서드를 결정하는 속성 집합을 저장합니다. 호출 시 임대를 갱신할 수 있습니다. 메서드가 원격 개체에 호출될 때마다 임대 기간은 현재 LeaseTimeRenewOnCallTime을 더한 최대 기간으로 설정됩니다. LeaseTime이 끝나면 스폰서에게 임대 기간을 갱신하도록 요청합니다. 신뢰할 수 없는 네트워크에서 작업해야 할 경우가 종종 있기 때문에 임대 스폰서를 사용할 수 없는 경우도 있고, 불필요한 개체가 서버에 남아 있지 않도록 하기 위해 각 임대는 SponsorshipTimeout 값을 가집니다. 이 값은 임대가 종료되기 전에 임대를 갱신할 스폰서가 대기해야 할 기간을 지정합니다. SponsershipTimeout 값이 null인 경우, CurrentLeaseTime은 임대 만료 기간을 결정하는 데 사용됩니다. CurrentLeaseTime 값이 0인 경우, 임대 기간이 만료되지 않았음을 나타냅니다. 구성 또는 API는 InitialLeaseTime, SponsorshipTimeout, RenewOnCallTime의 기본값을 무시하는 데 사용됩니다.

임대 관리자는 ISponsor 인터페이스를 구현하는 스폰서의 목록을 스폰서 기간이 긴 순서대로 관리합니다. 임대 기간을 갱신하는 데 스폰서가 필요한 경우, 목록의 상단부터 한 명 이상의 스폰서가 기간을 갱신하도록 요청됩니다. 목록의 상단은 가장 긴 임대 갱신 기간을 이전에 요청한 스폰서부터 나열되어 있습니다. 스폰서가 SponsorshipTimeOut 기간 연장에 응답하지 않으면 해당 스폰서는 목록에서 제거됩니다. GetLifetimeService를 호출하고 요청된 개체 임대를 매개 변수로서 전달하여 개체를 임대할 수 있습니다. 이 호출은 RemotingServices 클래스의 정적 메서드입니다. 개체가 응용 프로그램 도메인에 로컬 개체인 경우, 이 호출의 매개 변수는 개체에 로컬 참조가 되며 반환된 임대는 임대에 로컬 참조가 됩니다. 개체가 원격인 경우, 프록시가 매개 변수로서 전달되고 임대에 대한 투명 프록시가 호출자에게 반환됩니다.

개체는 개체 자체의 임대를 제공할 수 있기 때문에 자체 수명을 제어할 수 있습니다. 이러한 기능은 다음과 같이 MarshalByRefObjectInitializeLifetimeService 메서드를 무시함으로써 수행할 수 있습니다.

public class Foo : MarshalByRefObject {
  public override Object InitializeLifetimeService()
  {
    ILease lease = (ILease)base.InitializeLifetimeService();
    if (lease.CurrentState == LeaseState.Initial)  {
      lease.InitialLeaseTime = TimeSpan.FromMinutes(1);
      lease.SponsorshipTimeout = TimeSpan.FromMinutes(2);
      lease.RenewOnCallTime = TimeSpan.FromSeconds(2);
    }
    return lease;
  }
}

임대 속성은 임대가 초기 상태인 경우에만 바꿀 수 있습니다. InitializeLifetimeService의 구현은 일반적으로 기본 클래스의 해당 메서드를 호출하여 원격 개체에 대한 기존 임대를 검색합니다. 개체가 이전에 마샬링된 적이 없는 경우, 반환된 임대는 초기 상태로 되며 임대 속성을 설정할 수 있습니다. 개체가 마샬링되어 있는 경우, 임대는 초기 상태에서 활성 상태로 되고 임대 속성을 초기화하려는 시도는 모두 무시됩니다. 원격 개체가 활성화되면 InitializeLifetimeService가 호출됩니다. 임대 스폰서의 목록은 활성화 호출과 함께 제공되며 임대가 활성화 상태인 동안은 언제든지 스폰서를 추가할 수 있습니다.

임대 기간은 다음과 같은 방법으로 연장할 수 있습니다.

  • 클라이언트가 Lease 클래스에 Renew 메서드를 호출하여 연장합니다.

  • 스폰서에게 Renewal을 요청하여 연장합니다.

  • 클라이언트가 개체에 메서드를 호출하면 임대 기간은 RenewOnCall 값에 의해 자동으로 갱신됩니다.


임대 기간이 만료되면, 임대 기간의 내부 상태가 활성에서 만료됨으로 변경되고 스폰서에게 더 이상 추가 호출을 하지 않습니다. 그리고 개체가 삭제되어 다른 삭제 개체와 함께 놓여집니다. 스폰서가 웹에서 또는 방화벽 뒤에 배포되면, 원격 개체가 스폰서에서 콜백을 수행하기 어려운 경우가 있기 때문에 스폰서와 클라이언트는 동일한 위치에 있어서는 안됩니다. 스폰서의 위치는 원격 개체에 의해 도달할 수 있는 네트워크 내부의 어느 곳이든 됩니다.

임대를 사용하여 원격 개체의 수명을 관리하는 것은 참조 계산에 대한 대안이라 할 수 있습니다. 참조 계산은 신뢰할 수 없는 네트워크 연결에서는 복잡하고 비효율적인 경향을 가집니다. 원격 개체의 수명이 필요 이상으로 확장된다는 일부 의견이 있지만, 임대 방식을 사용함으로써 참조 계산 및 클라이언트에 대한 Ping 실행에 쓰여진 네트워크 트래픽을 줄일 수 있으므로 임대 방식이 더 나은 솔루션으로 인식되고 있습니다.

결론

대부분의 업무 응용 프로그램의 요구 사항을 완벽하게 충족시키는 원격 프레임워크를 제공하는 것은 확실히 어려운 일이긴 하지만, 그렇다고 불가능한 일은 아닙니다. Microsoft에서는 필요에 따라 확장 가능하고 사용자 지정할 수 있는 프레임워크를 제공함으로써 이러한 노력에 박차를 가하고 있습니다.

부록 A: TCP 채널을 사용한 원격 작업 예제

이 부록은 간단한 "Hello World" 원격 응용 프로그램을 작성하는 방법을 보여 줍니다. 클라이언트는 문자열을 원격 개체에 전달합니다. 원격 개체는 "안녕하세요"라는 단어를 문자열에 추가하고 결과를 클라이언트에 다시 반환합니다.


이 파일을 server.cs로 저장합니다. 서버에 대한 코드는 다음과 같습니다.


using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels.TCP;
namespace RemotingSamples {
  public class HelloServer : IHello {
    public static int Main(string [] args) {
      TCPChannel chan = new TCPChannel(8085);
      ChannelServices.RegisterChannel(chan);
      RemotingServices.RegisterWellKnownType(
        "server", "RemotingSamples.HelloServer", "SayHello", WellKnownObjectMode.SingleCall);
      System.Console.WriteLine("마치려면 <enter>를 누르십시오...");
      System.Console.ReadLine();
      return 0;
    }
    public HelloServer()
    {
      Console.WriteLine("HelloServer 활성화됨");
    }
    ~HelloServer()
    {
      Console.WriteLine("개체 삭제됨");
    }
    public ForwardMe HelloMethod(ForwardMe obj)
    {
      Console.WriteLine("Hello.HelloMethod : {0}", name);
      return "안녕하세요 " + name;
    }
  }
}

이 코드를 client.cs로 저장합니다.


using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels.TCP;
namespace RemotingSamples {
  public class Client
  {
    public static int Main(string [] args)
    {
      TCPChannel chan = new TCPChannel();
      ChannelServices.RegisterChannel(chan);
      ForwardMe param = new ForwardMe();
      HelloServer obj = (HelloServer)Activator.GetObject(
        typeof(RemotingSamples.HelloServer), "tcp://localhost:8085/SayHello");
      if (obj == null) System.Console.WriteLine("서버를 찾을 수 없습니다");
      else {
        Console.WriteLine("값은 " + param.getValue() + "입니다.");
        ForwardMe after = obj.HelloMethod(param);
        Console.WriteLine("호출 후의 값은 " + after.getValue() + "입니다.");
      }
      return 0;
    }
  }
}

makefile은 다음과 같습니다.


all: server.exe client.exe share.dll
share.dll: share.cs
   csc /debug+ /target:library /out:share.dll share.cs
server.exe: server.cs
   csc /debug+ /r:share.dll /r:System.Runtime.Remoting.dll server.cs
client.exe: client.cs server.exe
   csc /debug+ /r:share.dll /r:server.exe /r:System.Runtime.Remoting.dll client.cs
clean:
   @del server.exe client.exe *.pdb *~ *.*~

부록 B: HTTP 채널을 사용한 원격 작업 예제

이 파일을 server.cs로 저장합니다. 서버에 대한 코드는 다음과 같습니다.

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels.HTTP;
namespace RemotingSamples {
  public class HelloServer : IHello {
    public static int Main(string [] args) {
      HTTPChannel chan = new HTTPChannel(8085);
      ChannelServices.RegisterChannel(chan);
      RemotingServices.RegisterWellKnownType(
        "server", "RemotingSamples.HelloServer", "SayHello", WellKnownObjectMode.SingleCall);
      System.Console.WriteLine("마치려면 <enter>를 누르십시오...");
      System.Console.ReadLine();
      return 0;
    }
    public HelloServer()
    {
      Console.WriteLine("HelloServer 활성화됨");
    }
    ~HelloServer()
    {
      Console.WriteLine("개체 삭제됨");
    }
    public ForwardMe HelloMethod(ForwardMe obj)
    {
      Console.WriteLine("Hello.HelloMethod : {0}", name);
      return "안녕하세요 " + name;
    }
  }
} 

이 코드를 client.cs로 저장합니다.


using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels.HTTP;
namespace RemotingSamples {
  public class Client
  {
    public static int Main(string [] args)
    {
      HTTPChannel chan = new HTTPChannel();
      ChannelServices.RegisterChannel(chan);
      ForwardMe param = new ForwardMe();
      HelloServer obj = (HelloServer)Activator.GetObject(
        typeof(RemotingSamples.HelloServer), "http://localhost:8085/SayHello");
      if (obj == null) System.Console.WriteLine("서버를 찾을 수 없습니다");
      else {
        Console.WriteLine("값은 " + param.getValue() + "입니다.");
        ForwardMe after = obj.HelloMethod(param);
        Console.WriteLine("호출 후의 값은 " + after.getValue() + "입니다.");
      }
      return 0;
    }
  }
}

makefile은 다음과 같습니다.


all: server.exe client.exe share.dll
share.dll: share.cs
   csc /debug+ /target:library /out:share.dll share.cs
server.exe: server.cs
   csc /debug+ /r:share.dll /r:System.Runtime.Remoting.dll server.cs
client.exe: client.cs server.exe
   csc /debug+ /r:share.dll /r:server.exe /r:System.Runtime.Remoting.dll client.cs
clean:
   @del server.exe client.exe *.pdb *~ *.*~ 
이 정보가 도움이 되었습니까?
(1500자 남음)
© 2013 Microsoft. All rights reserved.