. .. : Swf.hu 1.0 archívum : Swf.hu főoldal >>> : .. .


 
 
SEGÉDLETEK Flash+Java

SimpleChat V1.0 - Flash-Chat alkalmazás készítése (JAVA)
  feltöltve: 2004.02.02 | szerző: tenegri | olvasva: 11798 alkalommal

       
 

Kliensoldal - chat-motor osztály

Most pedig következzen a Flash része a dolognak :) A chat kliensünket újrafelhasználható és rugalmasan kezelhető formában próbáljuk megvalósítani, ezért elválasztjuk egymástól a szerverrel kommunikáló részt és a felhasználói felületet. Mindkettőt önálló osztályként készítjük el, s az utóbbi pedig egyben vizuális komponens is lesz, azaz elhelyezhető a Flash komponenspalettáján és onnan egy mozdulattal beilleszthető bármelyik programba. A chat-motor elkülönítésével azonban megvan a lehetőség rá, hogy teljesen más kezelőfelülettel lássuk el a chat programot.

A chat-motor osztály neve ChatEngine, s a következő kívülről elérhető (public) tulajdonságai, illetve metódusai vannak:

Tulajdonságok

hostName
A chat-szerver neve, amelyhez csatlakozik a kliens. Alapesetben 6-os playerrel olyan szerverhez lehet csatlakozni, melynek a domainneve ugyanahhoz a fődomainhez tartozik, mint ahonnan a Flash mozi le lett töltve (tehát pl. ha a mozi a www.valami.hu címről származik, akkor csatlakozhat a www.valami.hu, chat.valami.hu, stb. nevű szerverekhez). A helyzet némileg szigorúbb 7-es player esetén, itt ugyanis csak pontosan ugyanahhoz a szerverhez lehet kapcsolódni, mint ahol a Flash mozi is elhelyezkedik. Ha mégis más szerver elérésére lenne szükség, azt is meg lehet oldani - a módszer jól le van írva a súgó About allowing cross-domain data loading című fejezetében. Ha a szerver nevének null-t adunk meg, akkor automatikusan ahhoz a szerverhez kapcsolódik, ahol a mozi található.
port
A port száma, melyen keresztül a chat-szerverhez csatlakozni lehet.
nick
A felhasználónév.
connected
A kliens állapotát mutaja, azaz hogy csatlakozva van-e a szerverhez vagy sem (csak olvasható).

Metódusok

connect(Void): Void
Kapcsolatot kezdeményez a chat-szerverrel.
disconnect(Void): Void
Bontja a kapcsolatot a szerverrel.
sendMessage(aMessage: String): Void
Üzenet küldése a többi felhasználónak.
getUserList(Void): Void
A felhasználói lista lekérése a szerverről. A válasz az onUserList esemény létrejöttekor érkezik meg.
addListener(listener: Object): Boolean
Új eseménykezelőt regisztrál. Részletesebben lásd alább.

Eseménykezelők

onStatus(aCode: Number, aStatusMessage: String): Void
Hibaüzenet (vagy nyugtázó üzenet) esetén keletkező esemény. A két paraméter a hiba kódját és szöveges magyarázatát tartalmazza. A hibaüzenetek egyrészt a szerver által küldöttek lehetnek (ezeket lásd a kommunikációs protokoll leírásánál, a STATUS üzenetnél), de kiegészülnek a következő kliensoldali hibaüzenetekkel:
99
Nem sikerült kapcsolódni a szerverhez!
98
Nem értelmezhető XML üzenet érkezett a szervertől.
97
Ismeretlen típusú üzenet érkezett a szervertől.
onDisconnected(Void): Void
A kapcsolat megszakadt.
onMessage(aSender: String, aMessage: String): Void
Üzenet érkezett valamely felhasználótól. A két paraméter a küldő neve és az üzenet szövege.
onUserConnected(aName: String): Void
Új felhasználó csatlakozott a chat-hez.
onUserDisconnected(aName: String): Void
Egy felhasználó kilépett a chat-ről.
onNotification(aNotification: String): Void
Tájékoztató üzenet érkezett a szervertől.
onUserList(aUserList: Array): Void
A szerverről megérkezett a belépett felhasználó listája. A paraméterként kapott tömbben a felhasználók nevei vannak.

Az eseménykezelők a Flash MX-ben bevezetett eseménymodellen (listener, AsBroadcaster) alapulnak és nem az MX 2004-ben megjelent, és az új komponensekben is használt rendszeren (event, EventDispatcher, UIEventDispatcher). A kettő közötti különbség részletes kifejtését inkább mellőzöm, ez kiderül a súgóból. A lényeg, hogy a ChatEngine osztály fenti eseményeihez az addListener() metódussal lehet eseménykezelőt rendelni, méghozzá úgy, hogy az addListener() paramétereként megadjuk azt az objektumot, mely tartalmazza a fenti definíciónak megfelelő metódusokat, s az események létrejöttekor ezek lesznek meghívva. Ezzel a megoldással amúgy eltérünk az ősként szolgáló XMLSocket osztály eseménykezelő rendszerétől, amely a Flash MX egy harmadik típusú eseménykezelését használja (metódusok felülírása magában az objektumban). Akár következetlennek is nevezhetnénk ezt, hiszen ha már eltérünk az eredetitől, akkor miért nem az újabb MX 2004-es módon tesszük ezt, de mentségemre szolgáljon, hogy ez így a konrét megvalósítást tekintve egyszerűbbnek és átláthatóbbnak tűnt, másrészt pedig apró módosítással átírható MX 2004-esre.

Az ActionScript az XMLSocket osztályt biztosítja számunkra, hogy TCP socketeket kezeljünk, így a chat-motor is ezen alapul, s az új osztályunkat is ebből fogjuk származtatni. Bár az osztály nevében benne van az XML szó, nem csak XML formájú adatokat tud fogadni és küldeni, hanem bármilyen szöveget (a fogadásnál csak az onData eseményt kellene használni), de ez most végül is mindegy, mert a szerverrel való kommunikációban úgyis XML üzeneteket használunk.

A ChatEngine osztály elkészítéséhez nem lesz másra szükség, mint egy editorra (használhatjuk a Flash MX 2004 Prof beépített szerkesztőjét, de talán kényelmesebb egy külső programot elővenni). Nyissunk egy új üres szövegfájlt, a neve egyezzen meg a leendő chat-motor osztályunk nevével, azaz legyen ChatEngine.as (ez fontos!). A forráskód pedig az alábbiak szerint alakul:

class chat.ChatEngine extends XMLSocket{

  /* A get/set tulajdonságok által használt mezők */
  private var fHostName: String = "localhost";
  private var fPort: Number = 4321;
  private var fNick: String = "Anonymus";
  private var fConnected: Boolean = false;

  /* Ez a konstruktorunk, nem csinálunk benne semmit :) */
  public function ChatEngine(Void) {
    super();
    /* Felruházzuk az osztályunkat a listener rendszerű eseménykezelés lehetőségével. Innentől
    az addListener() metódussal új eseménykezelőket lehet rendelni az eseményekhez, illetve
    a broadcastMessage() meghívásával ki lehet váltani egy-egy eseményt (ezt csak osztályon
    belülről persze). */

    AsBroadcaster.initialize(this);
  }

  /* A hostName tulajdonság get függvénye, mely a chat-szerver nevét tartalmazza */
  public function get hostName(): String {
    return fHostName;
  }

  /* A hostName tulajdonság set függvénye */
  public function set hostName(aHostName: String): Void {
    /* Csak akkor lehet megváltoztatni, ha nincs élő kapcsolat */
    if (!connected)
      fHostName = aHostName;
  }

  /* A port tulajdonság get függvénye, mely a chat-szerver által használt port
  számát tartalmazza */

  public function get port(): Number {
    return fPort;
  }

  /* A port tulajdonság set függvénye */
  public function set port(aPort: Number): Void {
    /* Csak akkor lehet megváltoztatni, ha nincs élő kapcsolat */
    if (!connected)
      fPort = aPort;
  }

  /* A nick tulajdonság get függvénye. Ez a tulajdonság tartalmazza a felhasználónevet */
  public function get nick(): String {
    return fNick;
  }

  /* A nick tulajdonság set függvénye */
  public function set nick(aNick: String): Void {
    /* Csak akkor lehet megváltoztatni, ha nincs élő kapcsolat */
    if (!connected)
      fNick = aNick;
  }

  /* A connected tulajdonság get függvénye. Ez a tulajdonság mutatja, hogy van-e élő kapcsolat.
  Set függvénye nincs, mert ezt a tulajdonságot nem lehet kívülről megváltoztatni. */

  public function get connected(): Boolean {
    return fConnected;
  }

  /* Kapcsolat kezdeményezése a szerverrel */
  public function connect(Void): Void {
    if (!connected)
      if (!super.connect(hostName, port))
        /* Ha már itt kiderült, hogy nem jött össze valami, akkor arról generálunk egy eseményt */
        broadcastMessage("onStatus", 99, "Nem sikerült kapcsolódni a szerverhez.");
  }

  /* Kapcsolatbontásnál csak jelezzük a szándékunkat a szervernek a
  megfelelő üzenet elküldésével, s a kapcsolat tényleges bontása rá
  marad */

  public function disconnect(Void): Void {
    if (connected)
      send("");
  }

  /* Hozzászólás/üzenet küldése a szervernek */
  public function sendMessage(aMessage: String): Void {
  var x: XML;

    /* Csak ha van élő kapcsolat */
    if (connected) {
      /* Az üzenetet az XML osztály erre szolgáló metódusaival rakjuk össze
      és nem csak egyszerűen sztringműveletekkel, mert így automatikusan XML-es
      formára alakítódnak az üzenetben esetlegesen előforduló és gondot jelentő
      karakterek (pl. < és ") */

      x = new XML();
      x.appendChild(x.createElement("MESSAGE"));
      x.firstChild.appendChild(x.createTextNode(aMessage));
      send(x.toString());
    }
  }

  /* Lekérdezi a belépett felhasználók listáját a szervertől */
  public function getUserList(Void): Void {
    if (connected)
      send("");
  }


  /* Ami ezután jön, az az osztály belügye, nem tartozik a külvilágra,
  s kívülről nem elérhető */


  /* Ezt csak azért van itt, hogy a fordító ne hiányolja :) Mert a valódi
  broadcastMessage() metódust csak a konstruktorban adtuk hozzá az osztályhoz
  az AsBroadcaster.initialize() meghívásával, így ha ez a deklaráció itt nem
  lenne, akkor a fordító fennakadna rajta. Deklarálhatnánk egyébként más
  módon is, pl. így: private var broadCastMessage: Function; */

  private function broadcastMessage() {
  }

  /* Új eseménykezelő objektumot regisztrál. A deklaráció hasonló célokat szolgál,
  mint az előzőnél. */

  public function addListener() {
  }

  /* Töröl egy eseménykezelő objektumot (deklarálás - lásd fentebb) */
  public function removeListener() {
  }

  /* Ez kezeli le az eredeti XMLSocket osztály csatlakozáskor keletkező
  eseményét*/

  private function onConnect(aSuccess: Boolean): Void {
  var x: XML;

    /* Ha sikeres a csatlakozás, beloginolunk a chat-szerverre, azaz
    elküldjük a felhasználónevet */

    if (aSuccess) {
      /* Itt is az XML objektumot és annak metódusait használjuk az üzenet
      összerakásához, mert a felhasználónév is tartalmazhat az XML-ben fontos
      és az értelmezést megkeverő jeleket (pl. < vagy ") */

      x = new XML();
      x.appendChild(x.createElement("LOGIN"));
      x.firstChild.appendChild(x.createTextNode(nick));
      send(x.toString());
    }
    else {
      /* Ha sikertelen, akkor aktiváljuk az onStatus eseményt a megfelelő hibaüzenettel */
      broadcastMessage("onStatus", 99, "Nem sikerült kapcsolódni a szerverhez.");
    }
  }

  /* A kapcsolat megszakadásakor keletkező eseménynél csak meghívjuk a saját,
  ilyen célre fenntartott eseménykezelőnket. */

  private function onClose(Void): Void {
    if (fConnected)
      /* Csak akkor kell eseményt létrehozni, ha a kapcsolat úgy szakadt meg, hogy rendesen be
      voltunk lépve (tehát pl. nem azért mert hibás volt a login) */

      broadcastMessage("onDisconnected");
    fConnected = false;
  }

  /* Ez kezeli le, ha üzenet érkezik a socket-en keresztül */
  private function onXML(aData: XML): Void {
  var data: XMLNode;

    /* Ha a paraméter undefined, akkor nem volt értelmes XML dokumentum
    a socket-en át érkezett adat */

    if (aData == undefined)
      /* És ezt egy esemény formájában közhírré is tesszük */
      broadcastMessage("onStatus", 98, "Nem értelmezhető XML üzenet érkezett a szervertől.");
    else {
      data = aData.firstChild;

      /* Az XML elem neve alapján az üzenetet a megfelelő feldolgozó
      metódusnak adjuk át. Az elemnevek (és egyébként az attribútumnevek is)
      mindig nagybetűsek kell hogy legyenek! Ez a Flash MX 2004 és a Java miatt
      van, ugyanis mindkettő megkülönbözteti a kis- és nagybetűket. Persze megírható
      lenne olyanra is a program, hogy ez ne legyen akadály, de minek? :) */

      switch (data.nodeName.toUpperCase()) {
        case "STATUS":
          processStatus(data);
          break;
        case "MESSAGE":
          processMessage(data);
          break;
        case "NOTIFICATION":
          processNotification(data);
          break;
        case "USERLIST":
          processUserList(data);
          break;
        case "USER_CONNECTED":
          processUserConnected(data);
          break;
        case "USER_DISCONNECTED":
          processUserDisconnected(data);
          break;
        default:
          /* Ha egyik sem */
          broadcastMessage("onStatus", 97, "Ismeretlen típusú üzenet érkezett a szervertől.");
      }
    }
  }

  /* Ez dolgozza fel, ha hibaüzenet érkezett a szervertől */
  private function processStatus(aData): Void {
  var code: Number;

    code = Number(aData.attributes.CODE);
    /* Ha az üzenet a kapcsolódási kísérlettel kapcsolatos */
    if (code >= 100 && code <= 102)
      /* A connected tulajdonságot a sikeres/sikertelen kapcsolódásnak megfelelően állítjuk be */
      fConnected = (code == 100);

    /* És egy eseményt is létrehozunk */
    broadcastMessage("onStatus", code, aData.firstChild.nodeValue);
  }

  /* Ez dolgozza fel, ha egy felhasználó új üzenetet küldött */
  private function processMessage(aData) {
    broadcastMessage("onMessage", aData.attributes.SENDER, aData.firstChild.nodeValue);
  }

  /* A szervertől érkező tájékoztató üzeneteket kezeli le */
  private function processNotification(aData) {
    broadcastMessage("onNotification", aData.firstChild.nodeValue);
  }

  /* A belépett felhasználók listáját kezeli */
  private function processUserList(aData): Void {
  var userList: Array;
  var i: Number;

    userList = new Array();

    /* Az XML-ből kigyűjtjük a felhasználóneveket egy tömbbe. Hibaellenőrzéssel
    nemigen foglalkozunk :) */

    for (i = 0; i < aData.childNodes.length; i++)
      userList.push(aData.childNodes[i].attributes.NAME);

    broadcastMessage("onUserList", userList);
  }

  /* Új felhasználó belépését kezeli le */
  private function processUserConnected(aData): Void {
    broadcastMessage("onUserConnected", aData.attributes.NAME);
  }

  /* Egy felhasználó kilépését kezeli le */
  private function processUserDisconnected(aData): Void {
    broadcastMessage("onUserDisconnected", aData.attributes.NAME);
  }

}

A forrásfájlt (ChatEngine.as) másoljuk be a következő könyvtárba:

C:\Documents and Settings\felhasználónév\Local Settings\Application Data\Macromedia\Flash MX 2004\en\Configuration\Classes\chat

Ez persze Windows esetén van így, Mac-en most fejből nem tudom hol van a Classes könyvtár, de ha valaki Mac-et használ és szüksége van rá, utánanézek :) Amúgy csupán azért célszerű bemásolni a Classes könyvtárba az osztály forráskódját, hogy bármely programunkból szabadon hozzáférhessünk, így csak az import chat.ChatEngine; sort kell hozzáadni a programjainkhoz (természetesen ha más könyvtárba tettük, akkor annak megfelelően változik a csomag útvonala, mindenesetre a továbbiakban következő forráskódban ezt használom).

 
       
 
 

© Devnet.hu. A segédletek semmilyen formában nem másolhatók, publikálhatók a Devnet.hu és a szerzők közös írásos engedélye nélkül.
 
. .. : Swf.hu 1.0 archívum : Swf.hu főoldal >>> : .. .