Možno ste videli jeden z mnohých chatovacích systémov založených na prostredí Java, ktoré sa objavili na webe. Po prečítaní tohto článku pochopíte, ako fungujú - a viete, ako si vytvoriť jednoduchý chatovací systém.
Tento jednoduchý príklad systému klient / server má demonštrovať, ako zostavovať aplikácie iba pomocou prúdov dostupných v štandardnom rozhraní API. Chat používa na komunikáciu zásuvky TCP / IP a dá sa ľahko zabudovať na webovú stránku. Pre porovnanie, poskytujeme bočný panel vysvetľujúci komponenty Java sieťového programovania, ktoré sú relevantné pre túto aplikáciu. Ak stále stúpate na rýchlosť, najskôr sa pozrite na bočný panel. Ak sa v prostredí Java už dobre vyznáte, môžete skočiť priamo dovnútra a vyhľadať odkaz v bočnom paneli.
Budovanie chatovacieho klienta
Začíname s jednoduchým grafickým klientom pre chat. Trvá dva parametre príkazového riadku - názov servera a číslo portu, ku ktorému sa chcete pripojiť. Vytvorí soketové pripojenie a potom otvorí okno s veľkou výstupnou oblasťou a malou vstupnou oblasťou.
Rozhranie ChatClient
Keď užívateľ zadá text do vstupnej oblasti a klikne na Return, text sa prenesie na server. Server odráža všetko, čo odošle klient. Klient zobrazí všetko prijaté zo servera vo výstupnej oblasti. Keď sa k jednému serveru pripojí viac klientov, máme k dispozícii jednoduchý četovací systém.
Trieda ChatClient
Táto trieda implementuje chatovacieho klienta, ako je opísané. To zahŕňa nastavenie základného používateľského rozhrania, spracovanie interakcie používateľa a príjem správ zo servera.
import java.net. *; import java.io. *; import java.awt. *; public class ChatClient extends Frame implements Runnable {// public ChatClient (String title, InputStream i, OutputStream o) ... // public void run () ... // public boolean handleEvent (Event e) ... // public static void main (String args []) vyvolá IOException ...}
The ChatClient
trieda predlžuje Rám
; to je typické pre grafickú aplikáciu. Realizujeme Spustiteľné
rozhranie, aby sme mohli spustiť a Závit
ktorý prijíma správy zo servera. Konštruktér vykoná základné nastavenie grafického používateľského rozhrania, run ()
metóda prijíma správy zo servera, handleEvent ()
metóda zvláda interakciu používateľa a hlavný()
metóda vykoná počiatočné pripojenie k sieti.
chránený DataInputStream i; chránený DataOutputStream o; chránený výstup TextArea; chránený vstup TextField; chránený poslucháč vlákien; public ChatClient (nadpis reťazca, InputStream i, OutputStream o) {super (nadpis); this.i = nový DataInputStream (nový BufferedInputStream (i)); this.o = nový DataOutputStream (nový BufferedOutputStream (o)); setLayout (nový BorderLayout ()); add ("Stred", výstup = nový TextArea ()); output.setEditable (false); add ("Juh", vstup = nový TextField ()); balenie (); šou (); input.requestFocus (); poslucháč = nové vlákno (toto); listener.start (); }
Konštruktor má tri parametre: nadpis okna, vstupný prúd a výstupný prúd. The ChatClient
komunikuje cez určené toky; vytvárame dátové toky vo vyrovnávacej pamäti i a o, aby sme cez tieto toky poskytli efektívne komunikačné prostriedky na vyššej úrovni. Potom sme nastavili naše jednoduché užívateľské rozhranie pozostávajúce z: TextArea
výstup a Textové pole
vstup. Rozložíme a ukážeme okno a začneme a Závit
poslucháč, ktorý prijíma správy zo servera.
public void run () {try {while (true) {String line = i.readUTF (); output.appendText (riadok + "\ n"); }} catch (IOException ex) {ex.printStackTrace (); } nakoniec {poslucháč = null; input.hide (); potvrdiť (); skus {o.close (); } catch (IOException ex) {ex.printStackTrace (); }}}
Keď vlákno poslucháča vstúpi do metódy spustenia, sedíme v nekonečnej slučke a čítame String
s zo vstupného toku. Keď String
dorazí, pripojíme ho k výstupnej oblasti a opakujeme slučku. An Výnimka IO
môže dôjsť, ak dôjde k strate spojenia so serverom. V takom prípade vytlačíme výnimku a vykonáme čistenie. Upozorňujeme, že to bude signalizovať znak Výnimka EOFE
z readUTF ()
metóda.
Aby sme to vyčistili, najskôr tomu priradíme náš poslucháčsky odkaz Závit
do nulový
; to znamená pre zvyšok kódu, že vlákno bolo ukončené. Potom skryjeme vstupné pole a zavoláme potvrdiť()
aby bolo rozhranie opäť rozložené a zatvorte OutputStream
o zabezpečiť, aby bolo spojenie zatvorené.
Všimnite si, že všetky čistenie vykonávame v a konečne
doložka, tak k tomu dôjde, či Výnimka IO
tu nastane alebo je vlákno násilne zastavené. Okno nezatvárame okamžite; predpokladá sa, že používateľ môže chcieť čítať reláciu aj po strate spojenia.
public boolean handleEvent (Event e) {if ((e.target == input) && (e.id == Event.ACTION_EVENT)) {try {o.writeUTF ((String) e.arg); o.flush (); } catch (IOException ex) {ex.printStackTrace (); listener.stop (); } input.setText (""); návrat pravdivý; } else if ((e.target == this) && (e.id == Event.WINDOW_DESTROY)) {if (listener! = null) listener.stop (); skryť (); návrat pravdivý; } návrat super.handleEvent (e); }
V handleEvent ()
metódou, musíme skontrolovať dve významné udalosti používateľského rozhrania:
Prvou je akčná udalosť v Textové pole
, čo znamená, že používateľ stlačil kláves Return. Keď zachytíme túto udalosť, napíšeme správu do výstupného toku a potom zavoláme spláchnuť()
aby sa zabezpečilo jeho okamžité odoslanie. Výstupný prúd je a DataOutputStream
, aby sme mohli použiť writeUTF ()
poslať a String
. Ak je Výnimka IO
nastane spojenie muselo zlyhať, tak zastavíme vlákno poslucháča; toto automaticky vykoná všetky potrebné vyčistenia.
Druhou udalosťou je používateľ, ktorý sa pokúša zavrieť okno. Je na programátorovi, ako sa o túto úlohu postará; zastavíme vlákno poslucháča a skryjeme Rám
.
public static void main (String args []) hodí IOException {if (args.length! = 2) hodí novú RuntimeException ("Syntax: ChatClient"); Socket s = new Socket (args [0], Integer.parseInt (args [1])); new ChatClient ("Chat" + args [0] + ":" + args [1], s.getInputStream (), s.getOutputStream ()); }
The hlavný()
metóda spustí klienta; zabezpečíme, aby bol zadaný správny počet argumentov, otvoríme a Zásuvka
na zadaného hostiteľa a port a vytvoríme a ChatClient
pripojený k prúdom zásuvky. Vytvorenie soketu môže spôsobiť výnimku, ktorá ukončí túto metódu a bude zobrazená.
Budovanie servera s viacerými vláknami
Teraz vyvíjame chatovací server, ktorý dokáže prijať viac pripojení a ktorý bude vysielať všetko, čo číta, z ktoréhokoľvek klienta. Je pevne pripojený na čítanie a písanie String
s vo formáte UTF.
V tomto programe sú dve triedy: hlavná trieda, ChatServer
, je server, ktorý prijíma pripojenia od klientov a priraďuje ich k novým objektom obsluhujúcim pripojenie. The ChatHandler
trieda vlastne robí prácu tak, že počúva správy a vysiela ich všetkým pripojeným klientom. Jedno vlákno (hlavné vlákno) spracováva nové pripojenia a je tu vlákno (vlákno ChatHandler
triedy) pre každého klienta.
Každý nový ChatClient
sa pripojí k ChatServer
; toto ChatServer
odovzdá pripojenie novej inštancii súboru ChatHandler
triedy, ktorá bude dostávať správy od nového klienta. V rámci ChatHandler
triedy sa zachová zoznam aktuálnych obslužných programov; the vysielanie ()
metóda používa tento zoznam na prenos správy všetkým pripojeným ChatClient
s.
Trieda ChatServer
Táto trieda sa zaoberá prijímaním pripojení od klientov a spúšťaním vlákien obslužných rutín na ich spracovanie.
import java.net. *; import java.io. *; import java.util. *; public class ChatServer {// public ChatServer (int port) throws IOException ... // public static void main (String args []) throws IOException ...}
Táto trieda je jednoduchá samostatná aplikácia. Dodávame konštruktor, ktorý vykonáva všetku skutočnú prácu pre triedu, a hlavný()
metóda, ktorá to vlastne spustí.
public ChatServer (int port) hodí IOException {ServerSocket server = nový ServerSocket (port); while (true) {Socket client = server.accept (); System.out.println ("Prijaté z" + client.getInetAddress ()); ChatHandler c = nový ChatHandler (klient); c.start (); }}
Tento konštruktor, ktorý vykonáva všetku prácu servera, je pomerne jednoduchý. Vytvárame a ServerSocket
a potom sedieť v slučke a prijímať klientov s súhlasiť()
metóda ServerSocket
. Pre každé pripojenie vytvoríme novú inštanciu súboru ChatHandler
triedy, absolvovanie novej Zásuvka
ako parameter. Potom, čo sme vytvorili tento obslužný program, začneme ho jeho štart ()
metóda. Spustí sa nové vlákno na spracovanie pripojenia, aby naša hlavná slučka servera mohla naďalej čakať na nové pripojenia.
public static void main (String args []) hodí IOException {if (args.length! = 1) hodí novú RuntimeException ("Syntax: ChatServer"); nový ChatServer (Integer.parseInt (args [0])); }
The hlavný()
metóda vytvorí inštanciu súboru ChatServer
, odovzdávajúci port príkazového riadku ako parameter. Toto je port, ku ktorému sa budú klienti pripájať.
Trieda ChatHandler Táto trieda sa zaoberá vybavovaním jednotlivých pripojení. Musíme dostávať správy od klienta a znova ich posielať na všetky ostatné spojenia. Udržiavame zoznam pripojení v a statický
Vektor
.
import java.net. *; import java.io. *; import java.util. *; public class ChatHandler extends Thread {// public ChatHandler (Socket s) throws IOException ... // public void run () ...}
Predĺžime Závit
triedy, aby umožnilo samostatnému vláknu spracovať asociovaného klienta. Konštruktér akceptuje a Zásuvka
ku ktorej sa pripájame; the run ()
metóda nazývaná novým vláknom, vykonáva skutočné spracovanie klienta.
chránené zásuvky; chránený DataInputStream i; chránený DataOutputStream o; public ChatHandler (Socket s) hodí IOException {this.s = s; i = nový DataInputStream (nový BufferedInputStream (s.getInputStream ())); o = nový DataOutputStream (nový BufferedOutputStream (s.getOutputStream ())); }
Konštruktor uchováva odkaz na soket klienta a otvára vstupný a výstupný prúd. Opäť používame dátové toky vo vyrovnávacej pamäti; tieto nám poskytujú efektívne I / O a metódy na komunikáciu dátových typov na vysokej úrovni - v tomto prípade String
s.
chránené statické obslužné rutiny Vector = nový Vector (); public void run () {try {handlers.addElement (this); while (true) {Reťazec msg = i.readUTF (); vysielanie (správa); }} catch (IOException ex) {ex.printStackTrace (); } konečne {handlers.removeElement (this); skus {s.close (); } catch (IOException ex) {ex.printStackTrace (); }}} // chránené vysielanie statickej neplatnosti (reťazcová správa) ...
The run ()
metóda je miesto, kde vstupuje naše vlákno. Najskôr pridáme naše vlákno do Vektor
z ChatHandler
s manipulátormi. Pracovníci Vektor
vedie zoznam všetkých súčasných obslužných programov. Je to statický
premenná, a tak existuje jedna inštancia súboru Vektor
pre celok ChatHandler
triedy a všetkých jej inštancií. Teda všetky ChatHandler
s môže pristupovať k zoznamu aktuálnych pripojení.
Upozorňujeme, že je veľmi dôležité, aby sme sa neskôr odstránili z tohto zoznamu, ak zlyhá naše pripojenie; inak sa nám všetci ostatní spracovatelia pokúsia napísať, keď vysielajú informácie. Tento typ situácie, keď je nevyhnutné, aby sa po dokončení časti kódu uskutočnila určitá akcia, je hlavným využitím skúste ... konečne
konštrukt; preto vykonávame všetku svoju prácu v rámci a skúste ... chytiť ... konečne
konštrukt.
Telo tejto metódy prijíma správy od klienta a vysiela ich ďalej všetkým ostatným klientom pomocou protokolu vysielanie ()
metóda. Po ukončení slučky, či už z dôvodu načítania výnimky z klienta, alebo z dôvodu zastavenia tohto vlákna, sa zobrazí ikona konečne
doložka je zaručene vykonaná. V tejto klauzule odstránime naše vlákno zo zoznamu manipulátorov a zatvoríme soket.
chránené vysielanie statickej prázdnoty (textová správa) {synchronized (handlers) {Enumeration e = handlers.elements (); while (e.hasMoreElements ()) {ChatHandler c = (ChatHandler) e.nextElement (); skúsiť {synchronized (c.o) {c.o.writeUTF (správa); } sp.č.o. } catch (IOException ex) {c.stop (); }}}}
Táto metóda vysiela správu všetkým klientom. Najskôr synchronizujeme zoznam manipulátorov. Nechceme, aby sa ľudia pripájali alebo odchádzali, keď sa nachádzame v slučke, v prípade, že sa pokúsime vysielať niekomu, kto už neexistuje; toto núti klientov čakať, kým synchronizáciu neskončíme. Ak server musí zvládnuť obzvlášť veľké zaťaženie, potom by sme mohli zabezpečiť jemnejšiu synchronizáciu.
V rámci tohto synchronizovaného bloku dostaneme Vymenovanie
súčasných manipulantov. The Vymenovanie
trieda poskytuje pohodlný spôsob iterácie cez všetky prvky a Vektor
. Naša slučka jednoducho napíše správu každému prvku v Vymenovanie
. Upozorňujeme, že ak počas zápisu do a. Dôjde k výnimke ChatClient
, potom zavoláme klientovi stop ()
metóda; tým sa zastaví vlákno klienta, a preto sa vykoná príslušné vyčistenie vrátane odstránenia klienta z obslužných programov.