Programovanie

Budovanie internetového chatovacieho systému

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 Strings 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 Strings 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 ChatClients.

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 Strings.

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 ChatHandlers 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 ChatHandlers 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.

$config[zx-auto] not found$config[zx-overlay] not found