C# - TCP protokoll használata aszinkron módon

forráskód letöltése
A Microsoft által elkészített és elérhetővé tett Web Services Enhancements for Microsoft® .NET (WSE) névtér 1.0-ás verziója után megjelent a 2.0 verzió, mely számtalan újdonságot tartalmaz. Többek közt lehetővé teszi, hogy a TCP protokollon keresztül kommunikáljon egymással a szerver- és a kliensalkalmazás. Cikkünkben bemutatjuk, hogy a WSE csomag mely osztályaival valósítható meg a kommunikáció aszinkron módon.
A mellékelt példa kipróbálásakor ügyeljünk az alkalmazások megfelelő sorrendben történő elindítására. Elsőként a WSEServer alkalmazást indítsuk el. A mellékelt példa használatához telepítenünk kell a Microsoft WSE 2.0 csomagot. Ennek elvégzéséhez olvassa el a Kezdeti lépések című fejezetet.
Kezdeti lépések
A Microsoft által elkészített csomag a Web Services Enhancements for Microsoft .NET nevet viseli, és cikkünk írásakor a következő webcímen volt elérhető, mely természetesen megváltozhatott:
http://www.microsoft.com/downloads/details.aspx?displaylang=en&familyid=21fb9b9a-c5f6-4c95-87b7-fc7ab49b3edd
Letöltve az állományt (WSE20TP.exe) telepítenünk kell a .NET Framework alá. Az öntelepítő állomány erről gondoskodik, a használható .DLL állomány (Microsoft.Web.Services.dll), és a kapcsolódó dokumentáció alapértelmezésben a következő mappába kerül: %winroot%:\Program Files\Microsoft WSE\v2.0\.
A telepített assembly bekerül a globális assembly-listába, így amikor referenciaként megadjuk alkalmazásainkban, akkor a globális palettáról kell kiválasztanunk, nem pedig valamilyen alternatív mappából.
Szerveralkalmazás létrehozása
A TCP szoftvercsatornán zajló kommunikáció aszinkron formáját valósítjuk meg a példában, ahol a kliensalkalmazás elküldi a kérést, majd annak megérkezésekor bekövetkezik egy esemény. Az esemény kezelőjében a kapott eredmény-karakterláncot megjelenítjük. Ez a szerveralkalmazást futtató számítógép neve lesz, az egyszerűség kedvéért.
Az alkalmazások mindegyikében szükséges, hogy referenciaként megadjuk a Microsoft.Web.Services.dll assembly-t, valamint hogy a névterekre történő hivatkozást elhelyezzük a kódban.
using Microsoft.Web.Services;
using Microsoft.Web.Services.Addressing;
using Microsoft.Web.Services.Messaging;
Az alkalmazások mindegyikében kell deklarálnunk egy osztályt, melynek őse a SoapReceiver, és amelyikben felül kell írnunk az ős Receive metódusát. A metódus paramétere az üzenet objektuma (SoapEnvelope).
public class ReceiverServer : SoapReceiver    
{
  protected override void Receive(SoapEnvelope e)
  {
A metódusban megvizsgáljuk, hogy az üzenet tartalmaz-e hivatkozást a szerver URL-jére. Amennyiben igen, megkezdjük a feldolgozást.
    if (e.Context.Action.Value == "http://localhost/ReceiverServer" )
    {
Létrehozzuk az üzenetküldést végző objektumot.
      SoapSender sender = new SoapSender(e.Context.From.Address.Value);
Létrehozzuk az üzenetet befogadó SoapEnvelope objektumot.
      SoapEnvelope me = new SoapEnvelope();
Lekérdezzük a host nevét egy karakterláncba.
      string machinename = Environment.MachineName;
Beállítjuk a megfelelő URL-eket.
      me.Context.Action  = new Action( "http://localhost/ReceiverServer");
      me.Context.From    = new From( new Uri("soap.tcp://" + System.Net.Dns.GetHostName() + "/ReceiverServer"));
Elhelyezzük az üzenetet a borítékban, majd elküldjük azt.
      me.SetBodyObject(machinename);
      sender.Send(me);
    }
  }
}
A szerveralkalmazás indulásakor megadjuk azt a virtuális URL-t, melyen a szerveralkalmazás figyelni fog.
Uri uri = new Uri("soap.tcp://" + System.Net.Dns.GetHostName() + "/ReceiverServer");
Ez a következő lesz: soap.tcp://<host számítógép neve>/ReceiverServer. A következő lépésben az URL-el azonosítva megadjuk a kérést fogadó osztály típusát.
SoapReceivers.Add( uri, typeof(ReceiverServer));
Kliensalkalmazás létrehozása
A kliensalkalmazásban is létre kell hozni a SoapReceiver osztályból származó osztályt, mely fogadja az üzeneteket a szervertől. A Receive metódusban egyszerűen kibontjuk az üzenet objektumát, és kinyerjük az üzenet tartalmát.
public class ReceiverClient : SoapReceiver
{
  protected override void Receive(SoapEnvelope e)
  {
    string machinename = (string)e.GetBodyObject(typeof(string));        
Majd kiváltunk egy eseményt, melyet a Form1 osztályban kezelünk le.
    Form1.TextEvent(machinename);
  }
}
A WSEClient névtérben deklarálunk egy delegáltat, mely az üzenet érkezését kísérő esemény típusa lesz.
public delegate void TextDelegate(string str);
A delegált paramétere egy karakterlánc, mely az üzenet szövege lesz. Annak érdekében, hogy a Form1 osztályunkban is értesüljünk arról, hogy a ReceiverClient osztály megkapta az üzenetet, létre kell hoznunk egy statikus eseményt a Form1 osztályban.
public static WSEClient.TextDelegate TextEvent;
Ehhez kell egy kezelőmetódust deklarálnunk, melyben a paraméterként kapott karakterláncot – az üzenet szövegét –, a szövegmezőbe írjuk.
TextEvent += new WSEClient.TextDelegate(this_TextEvent);
...
private void this_TextEvent(string text)
{
  textBox1.Text = text;
}
A MŰVELET gombra kattintva példányosítunk egy üzenetobjektumot, majd a SetBodyObject metódussal elhelyezzük az üzenetben a kérés paraméterét, mely most egy tartalom nélküli object típusú objektum.
SoapEnvelope envelope = new SoapEnvelope();
object o = new Object();
envelope.SetBodyObject(o);
Amennyiben az üzenetben található válasz minősége valamilyen elküldött paraméter függvénye lenne, akkor itt kell megadni a feltétel paramétert.
Következő lépésben meg kell adnunk a feladó és a címzett URL-jét.
Uri to = new Uri("soap.tcp://" + System.Net.Dns.GetHostName() + "/ReceiverServer");
Uri from = new Uri("soap.tcp://" + System.Net.Dns.GetHostName() + ":9999/ReceiverClient");
A kliens esetén megadjuk a port számát is, mely most 9999. A művelet végén az üzenet borítékját „megcímezzük”, deklaráljuk a küldő objektumot, majd elküldjük az üzenetet.
envelope.Context.Action = new Action( "http://localhost/ReceiverServer");
envelope.Context.From = new From(from);
SoapSender s = new SoapSender(to);
s.Send(envelope);
Utolsó lépésként pedig – csakúgy, mint a szerver esetén –, regisztráljuk a ReceiverClient osztályt, mint az üzenetek feldolgozóját.
SoapReceivers.Add(from,typeof(ReceiverClient));