C# - Remoting objektumok összehasonlítása

forráskód letöltése
A .NET Remoting szolgáltatásán belül lehetőség van arra, hogy meghatározzuk annak módját, milyen formában szeretnénk egy távoli objektumot létrehozni és inicializálni – vagyis aktiválni. Cikkünkben bemutatjuk a lehetséges eseteket, összefoglaljuk az egyes objektumtípusok létrehozásának módját, valamint egy példával illusztráljuk, hogy melyik hogyan viselkedik, ha egy metódusát meghívjuk.
A program használatakor ügyelnie kell arra, hogy az alkalmazások helyes sorrendben legyenek elindítva. Elsőként a RServer alkalmazást kell elindítani.
A távoli objektumoknak három típusa létezik attól függően, hogy hol történik az objektum létrehozása, valamint hogy a kliens-, vagy a szerver-alkalmazás tartja kézben az adott objektum életciklusát. Ebben a tekintetben vizsgálva a távoli objektumokat elmondható, hogy kétfajta szerver aktiválta, és egyfajta kliens aktiválta objektumról beszélhetünk.
Szerver aktiválta objektumok
A szerver aktiválta objektumok csak akkor jönnek létre, ha a kliens oldalról egy kérés érkezik az objektum valamely metódusára. Fontos tehát, hogy nem akkor, amikor a kliens osztályban példányosítjuk az objektumot a new metódussal, és nem is akkor, amikor meghívjuk az Activator osztály GetObject metódusát. Ezzel a hálózati kommunikációban megspórolhatunk egy oda-vissza utat, mely azt célozná, hogy az objektumpéldányt létrehozzuk.
Csak egy proxy objektum jön létre akkor, amikor a kliens példányosítja az objektumot. Ez azt eredményezi, hogy csak egy alapértelmezett konstruktort hozhatunk létre az objektum osztályában. Ha arra van szükségünk, hogy konstruktorban paramétereket adjunk át a létrejövő objektum inicializálásához, akkor kliens aktiválta objektumokat kell használnunk.
A szerver aktiválta objektumok két típusa (a WellKnownObjectMode enumerátor két lehetséges értéke) a SingleCall és a Singleton objektumok.
A SINGLETON objektumoknak csupán egy példánya létezhet az adott időpillanatban. Minden kliens oldali kérést, mely beérkezik az objektum metódusaira, ugyanaz az objektum szolgálja ki. Ha nem létezik az adott objektum még (mert nem volt kérés addig a pillanatig), akkor a szerver elkészít egy példányt, mely kiszolgálja a kéréseket.
Mivel a Singleton objektumtípus alapértelmezésben rendelkezik egy, az élettartamát meghatározó értékkel, a kliensek nem mindig kapják ugyanazt a referenciát az objektumra, még akkor sem, ha – mint jelen esetben – az objektumnak csupán egy példánya létezik. Az objektumok ugyanis az idő lejártával megszűnnek, és memóriaterületük felszabadul.
A SINGLECALL objektumnak minden klienshez létrejön egy példánya. Egy adott metódusra azonos klienstől érkező következő kéréskor egy másik objektumpéldány szolgálja ki, még akkor is, ha az előző példány még nem esett a szemétgyűjtés áldozatául.
Szerver aktiválta objektumok elérésekor a kliens-oldalon kell az elérést leprogramozni, és létrehozni az objektumot a new metódussal, vagy az Activator.GetObject metódust kell használni. Ezeknél az objektumoknál nem kell a kommunikációs csatornát regisztrálni. Ennek elmulasztásakor a remoting rendszer automatikusan lefoglalja a Machine.config állományban megadott alapértelmezett csatornát, a megadott porttal. Ekkor azonban nem értesül a kliens a szerver bizonyos eseményeiről.
Kliens aktiválta objektumok
A kliens aktiválta objektumok esetében az objektumok létezésének időtényezőit a kliens-alkalmazás befolyásolja, mintha csak az objektum a klienssel azonos helyen lenne. A hálózati közegben egy oda-vissza út lezajlik akkor, amikor az objektum létrejön, valamint amikor egy kliens proxy elkészül a típusra.
Az objektumok létrehozásakor a kliens meghatározza az adott objektum időtényezőit, melynek eredményeként az objektum meghatározott élettartammal rendelkezik. Amennyiben az idő lejár, az objektum megkérdezi a klienst, hogy meghosszabbodhat-e az élettartam. Amennyiben a kliens nem elérhető, akkor ismét egy alapértelmezett időtartam kerül megadásra, ami azt jelenti, hogy mennyi idő múlva kísérli meg a szerver felvenni a kapcsolatot a klienssel ismét, mielőtt elindulna a szemétgyűjtés.
A különbség a kliens és a szerver aktiválta objektumok időtényezői közt az, hogy a szerver aktiválta objektum minden klienskérést kiszolgál, míg a kliens aktiválta objektum csak azt, amelyik az adott referenciával rendelkezik.
A kliens aktiválta objektumok elérésekor a kliens-oldalon kell az elérést leprogramozni, és létrehozni az objektumot a new metódussal, vagy az Activator.CreateInstance metódust kell használni.
A mellékelt példában mindhárom objektumtípusra mutatunk egy példát. A kliens programból a három objektumtípust először létrehozhatjuk, majd meghívhatjuk azok műveletvégző metódusait, majd ennek eredményét megjeleníthetjük a program RichTextBox kontroljában. A mellékelt példa három projektet tartalmaz.
A .NET Framework Remoting szolgáltatásának, osztályainak eléréséhez meg kell adnunk referenciaként a System.Runtime.Remoting névteret. Ehhez válasszuk a Project - Add reference menüpontot, majd a megjelenő ablakban a .NET lapon keressük elő a System.Runtime.Remoting elemet. A forráskódot tartalmazó állományok elején meg kell adnunk, hogy a névtér osztályai közül melyekre van szükségünk.
RObject projekt
A projektben létrehoztunk egy–egy osztályt a kétfajta objektumnak, melyekben megadtuk a metódusok deklarációját, azonban mivel a szerver aktiválta osztály absztrakt osztály, így a metódusoknak csak a definícióját hozzuk létre:
public abstract class SAOClass : MarshalByRefObject
{
  public abstract void SetSAOValue(int op1, int op2);
  public abstract int GetSAOValue();
}
A kliens aktiválta objektum osztálya:
public class CAOClass : MarshalByRefObject
{
  private int outValue;
  public void SetCAOValue (ref int op)
  {
    outValue = op * 3;      
  }
  public int GetCAOValue()
  {
    return outValue;
  }
}
Az RObject objektum eléréséhez referenciaként mind a kliens-, mind a szerver-alkalmazásokhoz hozzá kell adnunk a RObject elemet. Ehhez válasszuk a Project - Add reference menüpontot, majd a megjelenő ablakban a Projects lapon jelöljük ki az RObject elemet.
RServer projekt
A szerver-alkalmazásban létrehozunk egy-egy osztályt a fenti absztrakt osztályból származtatva RemoteSingleton és RemoteSingleCall néven, melyek a kétfajta szerver aktiválta objektum osztályai, valamint egy RemoteServer osztályt a kommunikáció lebonyolításához. Ebben deklaráltunk egy delegate objektumot, melynek segítségével a kliens-alkalmazásban a RichTextBox kontrolba írjuk az eredményt.
public static AppendTextDelegate  Writer;
Az osztály konstruktorában foglaljuk le a csatornát:
public RemoteServer(AppendTextDelegate  inWriter)
{
  ...
  try
  {
    TcpServerChannel channel = new TcpServerChannel(mPort);
    ChannelServices.RegisterChannel(channel);
Valamint konfiguráljuk az objektumok elérését. A Singleton objektum:
    RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemoteSingleton),"TestSAOSingleton",WellKnownObjectMode.Singleton);
A SingleCall objekum:
    RemotingConfiguration.RegisterWellKnownServiceType(typeof(RemoteSingleCall),"TestSAOSingleCall",WellKnownObjectMode.SingleCall);
A kliens aktiválta objektum:
    RemotingConfiguration.ApplicationName = "TestCAO";        RemotingConfiguration.RegisterActivatedServiceType(typeof(CAOClass));
  }
}
A szerver aktiválta objektumok osztályaiban a metódusokat implementáljuk.
RClient projekt
A ClientClasses.cs állományban létrehozzuk az osztályokat, melyek a kliensoldali elérésben segédkeznek. Az osztályok konstruktoraiban gondoskodunk arról, hogy a műveletek eredményei a kontrolban megjelenjenek.
Az alkalmazás Form1.cs állományában már csak példányosítanunk kell az osztályokat:
private ClientCAO cao;
private ClientSAOSingleton  sao_ston;
private ClientSAOSingleCall sao_scall;
A konstruktorban a csatornát lefoglaljuk:
public Form1()
{
  InitializeComponent();
  chnl = new TcpClientChannel();
  ChannelServices.RegisterChannel(chnl);
}
A megfelelő gombokkal (LÉTREHOZ) megalkotjuk a kívánt objektumot:
sao_ston = new ClientSAOSingleton(new AppendTextDelegate(rtb.AppendText));
Majd a METÓDUS MEGHÍVÁSA gombbal végrehajtjuk az objektum műveletvégző metódusát:
sao_ston = new ClientSAOSingleton(new AppendTextDelegate(rtb.AppendText));
A SingleCall objektumok esetén megfigyelhető, hogy a példányosításból adódó sajátosságok miatt a művelet eredménye mindig nulla lesz.