Ahhoz ugyan, hogy a kliensgépek egymásra találjanak továbbra is szükséges egy központi szerver, de ha már létrejött a kapcsolat, az adatok közvetlenül utazhatnak a két gép és a rajtuk futó két Flash alkalmazás között. Hogy az új lehetőséget könnyen kipróbálhassuk saját szerver beüzemelése nélkül is (ami annál is inkább nehéz lenne, mert még nem jelent meg az új Flash Media Server verzió, ami alkalmas lesz a P2P kapcsolatok felépítésére) az Adobe elindította a Stratus névre keresztelt ingyenes szolgáltatását, mely non-profit, nem kereskedelmi célra lehetővé teszi a P2P kapcsolatok létrehozását. A Stratus igazából egy kísérleti terep, az Adobe mindig beépíti az új, még fejlesztés alatt levő technológiákat, ezért ez a szolgáltatás gyakorlatilag folymatos beta állapotban van.
A Flash Player 10 által biztosított P2P szolgáltatások szomorú korlátja, hogy nincs lehetőség több kliens számára egyidejű adatküldésre (multi-casting), azaz ha üzeneteinket több címzettnek is továbbítani szeretnénk, akkor minden egyes címzettel külön ki kell építeni a P2P kapcsolatot, s külön elküldeni ugyanazt az adatot. Ez egy egyszerű szöveges üzenetnél még nem is túl megterhelő, de ha pl. videót közvetítenénk, akkor ahány címzett van, annyiszor több sávszélességet igényel a továbbítás (márpedig az otthoni internetkapcsolatoknál jellemzően pont a kifelé irányuló sávszélesség jóval szűkebb, mint a befelé jövő adatoké).
Ezt a korlátot a Flash Player 10.1-ben megjelent új csoportkezelés kerüli meg, mely elosztja a csoport tagjai között az adatok továbbításának feladatát. Anélkül, hogy belemennénk ennek pontos és részletes működésébe, a mi szempontunkból röviden arról van szó, hogy a kliensgépek egy csoporthoz csatlakozhatnak, s a küldő fél csak a hozzá legközelebb eső csoporttagoknak továbbítja az adatokat, amelyek aztán automatikusan továbbküldik azt az ő szomszédaiknak, azok is az övéiknek és így tovább, míg mindenkihez eljut. A mi kis chat-alkalmazásunk is ezen a módon fog működni.
Az alkalmazás elkészítéséhez használhatnánk Flex Builder 3-at, Flash Builder 4-et vagy Flash CS3/CS4-et is – most az utóbbit (Flash CS4) választottam, hogy ne legyen riasztó azok számára sem, akik a Flex-szel esetleg nincsenek közelebbi ismeretségben. Amire még szükségünk lesz:
- A Flash Player 10.1 Release Candidate (azaz még nem végleges) változata.
- Egy új playerglobal.swc fájlra, melyet a Flash CS4 (és mellesleg a Flex is) a programok ellenőrzésénél és fordításánál használ. Ez ahhoz kell, hogy felismerje a 10.1-es verzióban bevezetett új osztályokat.
- Regisztráció az Adobe Stratus-hoz (ehhez előzetesen egy egyszerű Adobe-s regisztráció kell csak). Ez azért kell, mert az itt kapott egyedi azonosító kódot (developer key) be kell építenünk a programunkba, ezzel tudunk majd csatlakozni a Stratus-szerverhez, mely a P2P kliensek egymásra találását és a csoport felépítését segíti.
A Flash Player 10.1 telepítéséhez el kell majd távolítani a korábbi Flash Player verziót a gépről, de erre vonatkozóan a linkelt oldalon lehet részletesebb leírást találni. A fenti linkről letöltött playerglobal.swc-t pedig elegendő a Flash telepítési könyvtárába másolni, lecserélve a már ott lévő fájlt (persze előtte mindenképp készítsünk egy biztonsági másolatot a régi playerglobal.swc-ről!). Egész pontosan erről a könyvtárról van szó (Windowson):
C:\Program Files\Adobe\Adobe Flash CS4\Common\Configuration\ActionScript 3.0\FP10
Ha megvagyunk a Flash Player 10.1 telepítésével és a playerglobal.swc bemásolásával, akkor nekikezdhetünk. Nyissunk egy új FLA dokumentumot és az UI komponensek felhasználásával készítsük el az alábbi színpadtartalmat (a piros feliratok jelzik a példány/instance neveket):

A színpad tartalma
Ha megvannak a kezelőszervek, akkor jöhet a program elkészítése. Az egyszerűség kedvéért itt a főmozi első és egyetlen képkockájára fogjuk írni a scriptet – ezt persze írhatnánk külön .as fájlba, egy formás saját osztályba is, de megint csak nem szeretném ezzel elriasztani azokat, akik ilyen mélységig esetleg még nem járnak biztos lépésekkel az ActionScript területén (akik pedig igen, azoknak nem jelent gondot kedvük szerint átalakítani a forráskódot).
Kezdjük általános dolgokkal. Állítsuk be, hogy mely kezelőelemek legyenek elérhetők a program elindulásakor:
// beállítjuk a kezdetben használható kezelőelemeket sendButton.enabled = false; inputText.enabled = false;
Meg lehet adni egy felhasználónevet – ezt induláskor egy véletlen értékkel töltjük ki:
// a felhasználónév helyére generálunk egy véletlen nevet userText.text = "User " + Math.floor(Math.random() * 10000);
Jöjjön egy függvény, mely kiírja a szövegmezőbe a megadott szöveget (erre még többször szükségünk lesz):
// kiírja a szövegmezőbe a megadott szöveget
function writeText(text: String): void {
// fűzzük a szövegmező tartalmának végéhez a paraméterként kapott szöveget
textArea.appendText(text + "\n");
// és állítsuk a görgetési pozíciót a szövegmező aljára
textArea.verticalScrollPosition=textArea.maxVerticalScrollPosition;
}
Ezután kezeljük le az “Üzenetek törlése” gombra történő kattintást:
// kössünk a gombhoz egy CLICK eseménykezelőt
clearButton.addEventListener(MouseEvent.CLICK, clickConnect);
// az "Üzenetek törlése" gomb eseménykezelője
function clickClear(e: MouseEvent):void {
textArea.text = "";
}
Most egy kis érdemi rész, a “Kapcsolódás” gomb lenyomásának kezelése:
// kössünk a gombhoz egy CLICK eseménykezelőt
connectButton.addEventListener(MouseEvent.CLICK, clickConnect);
// a "Kapcsolódás" gomb eseménykezelője
function clickConnect(e: MouseEvent): void {
// meghívjuk a saját connect() függvényünket, ami felépíti a kapcsolatot
connect();
}
A szerverrel való kapcsolat kiépítéséhez szükségünk lesz egy NetConnection típusú objektumra. Ennek létrehozásakor egy URL-t kell megadnunk, mely a Stratus-szerverre mutat, s tartalmazza egyedi azonosítónkat is. Ez így néz ki:
rtmfp://stratus.rtmfp.net/egyedi_azonosító
A programrész pedig:
// a szerver címe
var serverURL: String = "rtmfp://stratus.rtmfp.net/" +
"ide jön a saját egyedi azonosítónk (developer key)";
// a kapcsolatot megvalósító objektum
var nc: NetConnection;
// a kapcsolat létrehozását végző függvény
function connect():void {
// kapcsoljuk ki a "Kapcsolódás" gombot és a felhasználónévnek szánt szövegmezőt
connectButton.enabled = false;
userText.enabled = false;
// létrehozzuk a NetConnection objektumot
nc = new NetConnection();
// feliratkozunk a NET_STATUS eseményére, mely a kapcsolódás sikeréről értesít majd
nc.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
// és kapcsolódunk a megadott URL-hez
nc.connect(serverURL);
// írjunk ki egy üzenetet a felhasználó tájékoztatására
writeText("Kapcsolódás folyamatban...");
}
A kapcsolat sikeres létrejöttéről vagy épp sikertelenségéről a NET_STATUS esemény értesít. Az információt az eseményobjektum info tulajdonsága által mutatott objektum level és code eleméből olvashatjuk ki. Hiba esetén a level értéke “error”, egyébként pedig “status”. A code tartalmazza a pontos eseményleírást szöveges formában – sikeres kapcsolódás esetén ez a “NetConnection.Connect.Success” szöveg. (A lehetséges kódok teljes listája megtalálható a dokumentációban.)
// a NetConnection és a NetGroup objektum NET_STATUS eseményeit lekezelő függvény
function netStatus(e: NetStatusEvent): void {
// nézzük meg milyen értesítést is kaptunk
switch (e.info.code) {
// ha sikeres volt a csatlakozás...
case "NetConnection.Connect.Success":
// ... akkor kiírunk erről egy üzenetet
writeText("Sikeres kapcsolódás a szerverhez.");
// és kapcsolódunk a csoporthoz
initNetGroup();
break;
// minden egyéb esetben
default:
// megnézzük, hogy hiba történt-e - ha igen kiírjuk mi az
if (e.info.level == "error") {
writeText("Hiba történt: " + e.info.code);
}
}
}
A csoporthoz kapcsolódást az initNetGroup() függvény valósítja meg. A csoport nevét és paramétereit egy GroupSpecifier osztályú objektumban adhatjuk meg, majd az ebből kinyert csoportazonosító szöveg felhasználásával egy NetGroup objektummal csatlakozhatunk a csoporthoz, s ugyenezzel kommunikálhatunk is vele. A kapcsolódás sikeréről itt is a fenti NetConnection objektum NET_STATUS eseménye értesít, míg a csoporttal való kommunikáció egyéb történéseiről a NetGroup objektum szintén NET_STATUS eseménye tájékoztat. A csoporthoz csatlakozás előtt a Flash lejátszó megjelenít egy üzenetablakot, melyben engedélyt kér a P2P kapcsolat kiépítéséhez – itt természetesen az Allow (engedélyez) gombot nyomjuk majd meg.

Az engedélykérő ablak
// a csoport leírását tartalmazó objektum
var groupSpecifier: GroupSpecifier;
// a csoporttal való kapcsolattartásért felelős objektum
var netGroup: NetGroup;
// a csoporthoz való kapcsolódásért felelős függvény
function initNetGroup(): void {
// a GroupSpecifier objektumnak kell megadni a csoport nevét,
// melyhez csatlakozni szeretnénk
groupSpecifier = new GroupSpecifier("CHAT");
// beállítjuk még a csoport pár tulajdonságát
// engedélyezzük az adatok küldését post() metódussal
groupSpecifier.postingEnabled = true;
// engedélyezzük az adattovábbítást - enélkül a többiek nem kapják
// meg az üzeneteket
groupSpecifier.routingEnabled = true;
// bekapcsoljuk, hogy a szerver segítsen megtalálni a csoporttagokat
groupSpecifier.serverChannelEnabled = true;
// Létrehozunk egy NetGroup objektumot, mely a csoporttal való
// kapcsolattartásért felelős. Meg kell adni a kapcsolatot (NetConnection)
// és a csoport leírását szövegesen (ezt a GroupSpecifier objektumunktól
// kérhetjük el a groupspecWithoutAuthorizations() vagy
// groupspecWithAuthorizations() metódusokkal).
netGroup=new NetGroup(nc,groupSpecifier.groupspecWithoutAuthorizations());
// itt is NET_STATUS esemény értesít a fejleményekről
netGroup.addEventListener(NetStatusEvent.NET_STATUS, netStatus);
}
A netStatus függvényünket kiegészítjük a csoporthoz kapcsolódó események figyelésével. Ez egyrészt a sikeres kapcsolódást jelző “NetGroup.Connect.Success” esemény lesz, másrészt a más csoporttagoktól érkező üzenetet jelző “NetGroup.Posting.Notify” esemény. Ez utóbbinál a kapott üzenet az eseményobjektum info elemében levő message tulajdonságban található.
// a NetConnection és a NetGroup objektum NET_STATUS eseményeit lekezelő függvény
function netStatus(e: NetStatusEvent): void {
// nézzük meg milyen értesítést is kaptunk
switch (e.info.code) {
// ha sikeres volt a csatlakozás...
case "NetConnection.Connect.Success":
// ... akkor kiírunk erről egy üzenetet
writeText("Sikeres kapcsolódás a szerverhez.");
// és kapcsolódunk a csoporthoz
initNetGroup();
break;
// ha sikeresen kapcsolódtunk a csoporthoz
case "NetGroup.Connect.Success":
// ... akkor kiírunk erről egy üzenetet
writeText("Sikeres kapcsolódás a csoporthoz.");
// és hozzáférhetővé tesszük a kezelőelemeket
sendButton.enabled=true;
inputText.enabled=true;
break;
// ha üzenet érkezett valamelyik csoporttagtól
case "NetGroup.Posting.Notify":
// akkor kiírjuk (a kapott adat az e,info.message tulajdonságban érhető el)
writeMessage(e.info.message);
break;
// minden egyéb esetben
default:
// megnézzük, hogy hiba történt-e - ha igen kiírjuk mi az
if (e.info.level == "error") {
writeText("Hiba történt: " + e.info.code);
}
}
}
A kapott (és majd az elküldött) üzenetek a jelen programban egy objektum formájában valósulnak meg, mely az alábbi elemekkel rendelkezik:
user– a küldő felhasználó nevetext– a küldött szövegtime– a küldés pontos időpontja időbélyegként
Valójában persze bármilyen adat küldhető és fogadható (objektum, szöveg, szám, stb.), így ez az objektum és annak felépítése most csupán a mi önkényes választásunk eredménye. Készítsünk egy függvényt, mely az előbb leírt felépítésű objektum tartalmát megjeleníti a szövegmezőben (s a fenti netStatus() függvényben már hivatkoztunk is rá):
// Kírja a szövegmezőbe egy üzenet tartalmát
function writeMessage(message: Object): void {
writeText("[" + message.user + "]: " + message.text);
}
Következzen az adatküldés, mely a “Küldés” gomb megnyomásakor hajtódik végre. Mivel több helyről is szeretnénk elérni (a “Küldés” gomb megnyomása mellett az ENTER lenyomásakor is), egy külön függvénybe helyeztem:
// elküld egy megadott szövegű üzenetet a csoportnak
function sendMessage(text: String): void {
// az elküldendő adat egy objektum lesz, melyről fentebb már szó volt
var message: Object = new Object();
// tároljuk a saját felhasználónevünket (küldő)
message.user = userText.text;
// az üzenetmezőbe írt elküldendő szöveget
message.text = text;
// és a pontos időt - erre azért van szükség, mert az elküldött üzeneteknek
// mindenképpen különbözniük kell egymástól, máskülönben esetleg figyelmen kívül
// lesznek hagyva
message.time = new Date().time;
// mehet az üzener
netGroup.post(message);
// mivel a saját üzeneteinkről nem kapunk értesítést, így magunknak kell
// gondoskodnunk a kiírásáról
writeMessage(message);
}
// kössünk a "Küldés" gombhoz egy CLICK eseménykezelőt
sendButton.addEventListener(MouseEvent.CLICK, clickSend);
// a "Küldés" gombra kattintást lekezelő függvény
function clickSend(e: MouseEvent): void {
// ha nem üres az input mező...
if (inputText.text != "") {
// ... akkor elküldjük a benne szereplő szövegt a csoportnak
sendMessage(inputText.text);
// és kiürítjük a tartalmát
inputText.text = "";
}
// a beviteli mezőre irányítjuk a beviteli fókuszt
inputText.setFocus();
}
// az üzenetbeviteli mezőhöz kötünk egy gombnyomást figyelő eseménykezelőt
// is, hogy az ENTER megnyomásával is lehessen üzenetet küldeni
inputText.addEventListener(KeyboardEvent.KEY_DOWN, keyDown);
// az ENTER gomb lenyomását figyelő függvény
function keyDown(e: KeyboardEvent): void {
// ha az ENTER-t nyomták le és nem üres az input mező
if (e.keyCode == Keyboard.ENTER && inputText.text != "") {
// ... akkor elküldjük a benne szereplő szövegt a csoportnak
sendMessage(inputText.text);
// és kiürítjük a tartalmát
inputText.text = "";
}
}
Ezzel tulajdonképpen készen is lennénk, csak még egy aprósággal egészítjük ki a programot. A szerver címét nem biztos, hogy érdemes bedrótozni a programba, ezért egy Flashvars paraméterrel kívülről megadhatóvá tesszük ezt:
// megnézzük, hogy kaptunk-e serverURL nevű Flashvars paramétert, // s ha igen, akkor azt használjuk a kapcsolódáskor if (loaderInfo.parameters.serverURL != null) serverURL = loaderInfo.parameters.serverURL;
Így már tényleg kész. Nem túl bonyolult, hisz természetesen a funkciók és szolgáltatások halmozása nem volt cél – ez csak egy alap, amit mindenki kedvére továbbfejleszthet. A kipróbálásnál ügyeljünk arra, hogy legalább Flash Player 10.1 verzióval próbáljuk futtatni. Mivel a CS4 beépített lejátszója (melyet a Test Movie-nál is használ) csak 10-es verziójú, így a Test Movie-val nem fogjuk tudni futtatni a programot, csak a lefordítás (Publish) után a böngészőben.
A kész forrás letölthető innen (nincs benne developer key megadva, azt mindenkinek magának kell beilleszteni!). Ha pedig valaki csak kipróbálni szeretné, akkor ide kattintva teheti meg. Több böngészőablakban is megnyitva egy gépen is tesztelhető. Ha a kapcsolat létrehozásával és az üzenetek továbbításával esetleg problémák lennének, akkor célszerű ellenőrizni, hogy a gép tűzfala átengedi-e az UDP üzeneteket.





Tényleg jó lett, köszönjük!
Annyi kiegészítéssel élnék, hogy flash CS4 alól is tesztelhető egy kis hackeléssel. Egy trial CS5 players könyvtárában (XP: c:\Program Files\Adobe\Adobe Flash CS4\Players\Debug\) lévő debug könyvtár flashPlayer.exe-vel felülírjuk a CS4 azonos könyvtárában lévővel, és CTRL+SHIFT+ENTER (debug) módban fordítva a munkánkat a 10.1-es verzióval fordítja le. A normál fordítású (CTRL+ENTER)-es flashPlayer.exe cserével viszont nem jutottam eredményre, de több külföldi oldal is ezzel a prblémával küzd. Így akinek CS4-van a gépén, és nem szeretné böngészőben tesztelni a munkáját, annak érdemes a fent leírtak elvégzése, illetve debug módban való fordítása. Biztonsági mentést azért érdemes készíteni a fileokról, mielőtt felülcsapnánk.
Persze akinek van fent CS5 annak ez nem lesz gond.
Pont mire megvan a VPS-em addigra teszik be ezt a funkciót a flash-ben. Bár szerintem egy összetettebb multiuser/multiplayer alkalmazáshoz célszerűbb egy szerverprogramban gondolkodni.
Biztos, hogy ezzel a megoldással el fognak szaporodni a multiplayeres játékok. És egy csomó fiatal programozó ezt a területet is kipróbálhatja.
Na igen, nem tudni meddig lesz hozzáférhető a Stratus – addig-e csak, míg kijön a P2P-t támogató Media Server vagy tovább is. És persze hogy kihagytam az LCCS említését a posztból, köszönöm a pótlást
Itt van akkor már hozzá két link:
LiveCycle Collaboration Service
Árak
Az LCCS-t egyébként épp nemrég bővítették nagyon kis finom funkciókkal (pl. saját szerver “beültetése” a csoportokba, stb.).
LCCS April Release : Feature Explosion!
És persze az LCCS-ről szóló blog: Collaborative Methods
Nagyon jó lelírás köszönjük.
Stratus egy nagyon ígéretes fejlesztés az egyetlen bajom vele hogy Adobe nem képes már hónapok óta információt adni arról hogy mindig is lesz-e lehetőség használni vagy egyszer csak kihúzzák a dugót. Még jó rég rá is kérdeztem a fórumban http://forums.adobe.com/message/2415742#2415742 akkor sem kaptam választ és azóta sincs megoldás. Persze jó pénzért LCCS-be benne van meg jön FMS, ingyenes megoldásnak meg a LAN-os peer discovery-t ajánlgatják de hát az édes kevés a lehetőség kihasználására szóval egyben nagyszerű meg idegesítő is.
Viszont láttam már netes állásajánlatokat a Stratus protocol kifürkészésére reméljük sikerül és közzé teszik aztán kenyeret és P2P-t mindenkinek