Ohjelmointi

Internet-chat-järjestelmän rakentaminen

Olet ehkä nähnyt yhden monista Java-pohjaisista chat-järjestelmistä, jotka ovat tulleet esiin verkossa. Kun olet lukenut tämän artikkelin, ymmärrät heidän toimintansa - ja osaat rakentaa oman yksinkertaisen chat-järjestelmän.

Tämä yksinkertainen esimerkki asiakas- / palvelinjärjestelmästä on tarkoitettu osoittamaan, kuinka sovelluksia voidaan rakentaa vain vakiosovellusliittymässä käytettävissä olevista virroista. Keskustelu käyttää TCP / IP-liitäntöjä viestintään, ja se voidaan upottaa helposti verkkosivulle. Viitteenä tarjoamme sivupalkin, joka selittää Java-verkko-ohjelmointikomponentit, jotka ovat merkityksellisiä tälle sovellukselle. Jos jatkat vauhtia, katso ensin sivupalkki. Jos olet jo perehtynyt Java-ohjelmaan, voit hypätä suoraan sisään ja katsoa viittausta sivupalkkiin.

Chat-asiakkaan rakentaminen

Aloitamme yksinkertaisella graafisella chat-asiakasohjelmalla. Yhteyden muodostaminen vaatii kaksi komentoriviparametriä - palvelimen nimen ja portin numeron. Se muodostaa pistorasiayhteyden ja avaa sitten ikkunan, jossa on suuri lähtöalue ja pieni syöttöalue.

ChatClient-käyttöliittymä

Kun käyttäjä kirjoittaa tekstin syöttöalueelle ja osuu Return, teksti lähetetään palvelimelle. Palvelin toistaa kaiken, mitä asiakas lähettää. Asiakas näyttää kaiken palvelimelta vastaanotetun lähtöalueella. Kun useita asiakkaita muodostaa yhteyden yhteen palvelimeen, meillä on yksinkertainen chat-järjestelmä.

Luokan ChatClient

Tämä luokka toteuttaa chat-asiakkaan kuvatulla tavalla. Tähän sisältyy peruskäyttöliittymän määrittäminen, käyttäjien vuorovaikutuksen käsitteleminen ja viestien vastaanottaminen palvelimelta.

tuo java.net. *; tuo java.io. *; tuo java.awt. *; public class ChatClient laajentaa kehystoteutuksia Runnable {// public ChatClient (String title, InputStream i, OutputStream o) ... // public void run () ... // public boolean handleEvent (Event e) ... // public staattinen void main (String args []) heittää IOExceptionin ...} 

ChatClient luokka jatkuu Kehys; tämä on tyypillistä graafiselle sovellukselle. Toteutamme Ajettava käyttöliittymä, jotta voimme aloittaa a Lanka joka vastaanottaa viestejä palvelimelta. Rakentaja suorittaa graafisen käyttöliittymän perusasetukset juosta() - menetelmä vastaanottaa viestejä palvelimelta, kahvaEvent () menetelmä käsittelee käyttäjän vuorovaikutusta, ja main () menetelmä suorittaa ensimmäisen verkkoyhteyden.

 suojattu DataInputStream i; suojattu DataOutputStream o; suojattu TextArea-ulostulo; suojattu TextField-syöte; suojattu langankuuntelija; julkinen ChatClient (String title, InputStream i, OutputStream o) {super (title); this.i = uusi DataInputStream (uusi BufferedInputStream (i)); this.o = uusi DataOutputStream (uusi BufferedOutputStream (o)); setLayout (uusi BorderLayout ()); add ("Center", output = new TextArea ()); output.setEditable (väärä); add ("South", input = new TextField ()); pakkaus (); näytä (); input.requestFocus (); kuuntelija = uusi säie (tämä); kuuntelija.aloitus (); } 

Rakentaja ottaa kolme parametria: otsikon ikkunalle, tulovirran ja lähtövirran. ChatClient kommunikoi määriteltyjen virtojen kautta; Luomme puskuroituja datavirtoja i ja o tehokkaiden korkeamman tason viestintätilojen tarjoamiseksi näiden virtojen yli. Sitten perustettiin yksinkertainen käyttöliittymä, joka koostuu Tekstialue tuotos ja Tekstikenttä tulo. Asettelemme ja näytämme ikkunan ja aloitamme a Lanka kuuntelija, joka hyväksyy viestit palvelimelta.

public void run () {try {while (true) {String line = i.readUTF (); output.appendText (rivi + "\ n"); }} catch (IOException ex) {ex.printStackTrace (); } lopuksi {kuuntelija = tyhjä; input.hide (); vahvista (); kokeile {o.close (); } catch (IOException ex) {ex.printStackTrace (); }}} 

Kun kuuntelijan säie siirtyy ajomenetelmään, istumme äärettömään silmukan lukemiseen Merkkijonos tulovirrasta. Kun Merkkijono saapuu, liitämme sen lähtöalueeseen ja toistamme silmukan. An IOException voi tapahtua, jos yhteys palvelimeen on katkennut. Siinä tapauksessa tulostamme poikkeuksen ja suoritamme siivouksen. Huomaa, että siitä ilmoittaa EOFException alkaen readUTF () menetelmä.

Puhdistamiseksi annamme ensin kuuntelijalle viitteen tähän Lanka että tyhjä; tämä osoittaa muulle koodille, että ketju on päättynyt. Piilotamme sitten syöttökentän ja soitamme vahvista () niin, että liitäntä asetetaan uudelleen, ja sulje OutputStream o varmistaa, että yhteys on suljettu.

Huomaa, että suoritamme kaiken siivouksen kohdassa a lopulta lauseke, joten tämä tapahtuu, onko IOException tapahtuu tässä tai lanka lopetetaan väkisin. Emme sulje ikkunaa heti; oletuksena on, että käyttäjä saattaa haluta lukea istunnon silloinkin, kun yhteys on katkennut.

public boolean handleEvent (Tapahtuma e) {if ((e.target == input) && (e.id == Tapahtuma.ACTION_EVENT)) {kokeile {o.writeUTF ((String) e.arg); o huuhtele (); } catch (IOException ex) {ex.printStackTrace (); kuuntelija.stop (); } input.setText (""); palaa tosi; } else if ((e.kohde == tämä) && (e.id == Tapahtuma.WINDOW_DESTROY)) {if (kuuntelija! = null) kuuntelija.stop (); piilottaa (); palaa tosi; } return super.handleEvent (e); } 

vuonna kahvaEvent () menetelmällä meidän on tarkistettava kaksi merkittävää käyttöliittymän tapahtumaa:

Ensimmäinen on toimintatapahtuma Tekstikenttä, mikä tarkoittaa, että käyttäjä on napsauttanut Return-näppäintä. Kun saamme tämän tapahtuman, kirjoitamme viestin lähtövirtaan ja soitamme sitten huuhtele () sen varmistamiseksi, että se lähetetään välittömästi. Lähtövirta on a DataOutputStream, jotta voimme käyttää writeUTF () lähettää a Merkkijono. Jos IOException tapahtuu yhteyden on oltava epäonnistunut, joten lopetamme kuunteluketjun; tämä suorittaa kaikki tarvittavat puhdistukset automaattisesti.

Toinen tapahtuma on käyttäjä, joka yrittää sulkea ikkunan. Ohjelmoijan tehtävänä on hoitaa tämä tehtävä; lopetamme kuuntelulangan ja piilotamme Kehys.

public static void main (String args []) heittää IOExceptionin {if (args.length! = 2) heittää uuden RuntimeExceptionin ("Syntaksi: ChatClient"); Socket s = uusi Socket (args [0], Integer.parseInt (args [1])); uusi ChatClient ("Chat" + args [0] + ":" + args [1], s.getInputStream (), s.getOutputStream ()); } 

main () menetelmä käynnistää asiakkaan; varmistamme, että oikea määrä argumentteja on annettu, avataan a Pistoke määritettyyn isäntään ja porttiin, ja luomme a ChatClient liitetty pistorasiaan. Pistorasian luominen saattaa aiheuttaa poikkeuksen, joka sulkee tämän menetelmän ja tulee näkyviin.

Monisäikeisen palvelimen rakentaminen

Kehitämme nyt chat-palvelinta, joka voi hyväksyä useita yhteyksiä ja lähettää kaiken lukemansa miltä tahansa asiakkaalta. Se on langallinen lukemaan ja kirjoittamaan Merkkijonos UTF-muodossa.

Tässä ohjelmassa on kaksi luokkaa: pääluokka, ChatServer, on palvelin, joka hyväksyy yhteydet asiakkailta ja määrittää ne uusille yhteyskäsittelijän objekteille. ChatHandler luokka tosiasiallisesti työskentelee kuuntelemalla viestejä ja lähettämällä ne kaikille yhteydessä oleville asiakkaille. Yksi lanka (pääkierre) hoitaa uudet liitännät, ja on lanka ( ChatHandler luokka) jokaiselle asiakkaalle.

Jokainen uusi ChatClient muodostaa yhteyden ChatServer; Tämä ChatServer antaa yhteyden uuteen ChatHandler luokka, joka vastaanottaa viestejä uudelta asiakkaalta. Sisällä ChatHandler luokassa ylläpidetään luetteloa nykyisistä käsittelijöistä; lähettää() method käyttää tätä luetteloa viestin lähettämiseen kaikille liitetyille ChatClients.

Luokan ChatServer

Tämä luokka koskee asiakkaiden yhteyksien hyväksymistä ja käsittelijöiden ketjujen käynnistämistä niiden käsittelemiseksi.

tuo java.net. *; tuo java.io. *; tuo java.util. *; public class ChatServer {// public chatServer (int port) heittää IOException ... // julkinen staattinen void main (String args []) heittää IOException ...} 

Tämä luokka on yksinkertainen itsenäinen sovellus. Toimitamme rakentajan, joka suorittaa kaikki luokan todelliset työt, ja a main () menetelmä, joka todella käynnistää sen.

 julkinen ChatServer (int-portti) heittää IOException {ServerSocket-palvelin = uusi ServerSocket (portti); while (true) {Socket-asiakas = palvelin.accept (); System.out.println ("Hyväksytty kohteesta" + client.getInetAddress ()); ChatHandler c = uusi ChatHandler (asiakas); c. alku (); }} 

Tämä rakentaja, joka suorittaa kaiken palvelimen työn, on melko yksinkertainen. Luomme ServerSocket ja sitten istua silmukassa hyväksymällä asiakkaita hyväksyä() menetelmä ServerSocket. Kullekin yhteydelle luomme uuden ilmentymän ChatHandler luokassa, ohittaa uuden Pistoke parametrina. Kun olemme luoneet tämän käsittelijän, aloitamme sen sen kanssa alkaa() menetelmä. Tämä aloittaa uuden ketjun yhteyden käsittelemiseksi, jotta pääpalvelinsilmukka voi odottaa edelleen uusia yhteyksiä.

public static void main (String args []) heittää IOExceptionin {if (args.length! = 1) heittää uuden RuntimeException ("Syntaksi: ChatServer"); uusi ChatServer (Kokonaisluku.parseInt (argumentit [0])); } 

main () - menetelmä luo ChatServer, ohittaa komentoriviportin parametrina. Tämä on portti, johon asiakkaat muodostavat yhteyden.

Luokan ChatHandler

Tämä luokka koskee yksittäisten yhteyksien käsittelyä. Meidän on vastaanotettava viestejä asiakkaalta ja lähetettävä ne uudelleen kaikille muille yhteyksille. Ylläpidämme luetteloa a

staattinen

Vektori.

tuo java.net. *; tuo java.io. *; tuo java.util. *; julkisen luokan ChatHandler laajentaa säiettä {// public ChatHandler (Socket s) heittää IOException ... // public void run () ...} 

Pidennämme Lanka luokka sallia erillisen ketjun käsitellä liittyvää asiakasta. Rakentaja hyväksyy a Pistoke johon kiinnitämme; juosta() menetelmä, jota uusi ketju kutsuu, suorittaa varsinaisen asiakaskäsittelyn.

 suojatut pistorasiat; suojattu DataInputStream i; suojattu DataOutputStream o; public ChatHandler (Socket s) heittää IOException {this.s = s; i = uusi DataInputStream (uusi BufferedInputStream (s.getInputStream ())); o = uusi DataOutputStream (uusi BufferedOutputStream (s.getOutputStream ())); } 

Rakentaja pitää viittauksen asiakkaan liitäntään ja avaa tulo- ja lähtövirran. Jälleen käytämme puskuroituja datavirtoja; nämä tarjoavat meille tehokkaan I / O: n ja menetelmät korkean tason tietotyyppien - tässä tapauksessa Merkkijonos.

suojatut staattiset vektorinkäsittelijät = uusi vektori (); public void run () {try {käsittelijät.addElement (tämä); while (true) {Merkkijono msg = i.readUTF (); lähetys (msg); }} catch (IOException ex) {ex.printStackTrace (); } lopuksi {handlers.removeElement (tämä); kokeile {s.close (); } catch (IOException ex) {ex.printStackTrace (); }}} // suojattu staattinen virheellinen lähetys (merkkijono) ... 

juosta() menetelmä on, mihin säie tulee. Ensin lisätään säikeemme Vektori / ChatHandlers käsittelijät. Käsittelijät Vektori pitää luetteloa kaikista nykyisistä käsittelijöistä. Se on staattinen muuttuja ja niin on yksi esiintymä Vektori koko ChatHandler luokka ja kaikki sen esiintymät. Siten kaikki ChatHandlers voi käyttää luetteloa nykyisistä yhteyksistä.

Huomaa, että on erittäin tärkeää poistaa itsemme tästä luettelosta jälkeenpäin, jos yhteys epäonnistuu; muuten kaikki muut käsittelijät yrittävät kirjoittaa meille, kun he lähettävät tietoa. Tämän tyyppinen tilanne, jossa on välttämätöntä, että toiminta tapahtuu koodiosan valmistuttua, on yritä ... lopulta rakentaa; Siksi teemme kaiken työmme a yritä ... kiinni ... vihdoin rakentaa.

Tämän menetelmän runko vastaanottaa viestejä asiakkaalta ja lähettää ne uudelleen kaikille muille asiakkaille lähettää() menetelmä. Kun silmukka poistuu, joko asiakkaasta luettavan poikkeuksen vuoksi tai tämän säikeen pysäyttämisen vuoksi, lopulta lauseke on taattu. Tässä lausekkeessa poistamme säikeemme käsittelijöiden luettelosta ja suljet kannan.

suojattu staattinen tyhjä lähetys (merkkijono-viesti) {synkronoitu (käsittelijät) {Luettelo e = käsittelijät.elementit (); while (e.hasMoreElements ()) {ChatHandler c = (ChatHandler) e.nextElement (); kokeile {synkronoitu (c.o) {c.o.writeUTF (viesti); } c.o. huuhtelu (); } catch (IOException ex) {c.stop (); }}}} 

Tämä menetelmä lähettää viestin kaikille asiakkaille. Synkronoidaan ensin käsittelijöiden luettelossa. Emme halua ihmisten liittymistä tai poistumista silmukan aikana, jos yritämme lähettää jollekulle, jota ei enää ole; tämä pakottaa asiakkaita odottamaan, kunnes synkronointi on valmis. Jos palvelimen on käsiteltävä erityisen raskaita kuormia, voimme tarjota tarkempaa synkronointia.

Tämän synkronoidun lohkon sisällä saamme Luettelointi nykyisistä käsittelijöistä. Luettelointi luokka tarjoaa kätevän tavan toistaa kaikki a-elementit Vektori. Silmukka yksinkertaisesti kirjoittaa viestin Luettelointi. Huomaa, että jos a: lle kirjoitettaessa tapahtuu poikkeus ChatClient, sitten soitamme asiakkaan lopettaa() menetelmä; tämä pysäyttää asiakkaan ketjun ja suorittaa siten asianmukaisen puhdistuksen, mukaan lukien asiakkaan poistaminen käsittelijöistä.