Programavimas

Interneto pokalbių sistemos kūrimas

Galbūt matėte vieną iš daugelio „Java“ pagrindu veikiančių pokalbių sistemų, pasirodžiusių internete. Perskaitę šį straipsnį suprasite, kaip jie veikia, ir žinosite, kaip sukurti paprastą savo pokalbių sistemą.

Šis paprastas kliento / serverio sistemos pavyzdys skirtas parodyti, kaip kurti programas naudojant tik standartiniame API pasiekiamus srautus. Pokalbis naudoja TCP / IP lizdus, ​​kad galėtų bendrauti, ir jį galima lengvai įterpti į tinklalapį. Kaip pavyzdį pateikiame šoninę juostą, paaiškinančią „Java“ tinklo programavimo komponentus, kurie yra svarbūs šiai programai. Jei vis dar įsibėgėjate, pirmiausia pažvelkite į šoninę juostą. Vis dėlto, jei jau gerai išmanote „Java“, galite pereiti į dešinę ir tiesiog kreiptis į šoninę juostą.

Pokalbių kliento kūrimas

Pradedame nuo paprasto grafinio pokalbio kliento. Norint prisijungti, reikia dviejų komandinės eilutės parametrų - serverio pavadinimo ir prievado numerio. Jis užmezga lizdą ir tada atidaro langą su dideliu išvesties regionu ir mažu įvesties regionu.

„ChatClient“ sąsaja

Vartotojui įvedus tekstą į įvesties sritį ir paspaudus „Return“, tekstas perduodamas į serverį. Serveris atkartoja viską, ką siunčia klientas. Klientas rodo viską, ką gavo iš serverio išvesties regione. Kai prie vieno serverio prisijungia keli klientai, turime paprastą pokalbių sistemą.

„Class ChatClient“

Ši klasė įgyvendina pokalbio klientą, kaip aprašyta. Tai apima pagrindinės vartotojo sąsajos nustatymą, vartotojo sąveikos tvarkymą ir pranešimų iš serverio gavimą.

importuoti java.net. *; importuoti java.io. *; importuoti java.awt. *; viešosios klasės „ChatClient“ praplečia „Frame“ įrankius „Runnable“ {// public ChatClient (String title, InputStream i, OutputStream o) ... // public void run () ... // public Boolean handleEvent (Event e) ... // public static void main (String args []) meta IOException ...} 

„ChatClient“ klasė tęsiasi Rėmas; tai būdinga grafinei programai. Mes įgyvendiname Bėgama sąsają, kad galėtume pradėti a Siūlas kuris gauna pranešimus iš serverio. Konstruktorius atlieka pagrindinę GUI sąranką paleisti () metodas gauna pranešimus iš serverio, „rankEvent“ () metodas tvarko vartotojo sąveiką ir pagrindinis () metodas atlieka pradinį tinklo ryšį.

 saugoma „DataInputStream i“; apsaugotas „DataOutputStream o“; saugoma „TextArea“ išvestis; apsaugotas „TextField“ įvestis; saugoma gijų klausytoja; public ChatClient (String title, InputStream i, OutputStream o) {super (pavadinimas); this.i = nauja „DataInputStream“ (nauja „BufferedInputStream“ (i)); this.o = nauja „DataOutputStream“ (nauja „BufferedOutputStream“ (o)); „setLayout“ (naujas „BorderLayout“ ()); add ("Center", output = new TextArea ()); output.setEditable (false); add ("South", input = new TextField ()); paketas (); Rodyti (); input.requestFocus (); klausytojas = nauja gija (tai); klausytojas.pradėti (); } 

Konstruktorius imasi trijų parametrų: lango pavadinimo, įvesties srauto ir išvesties srauto. „ChatClient“ bendrauja per nurodytus srautus; mes kuriame buferinius duomenų srautus i ir o, kad šiose srautuose galėtume teikti efektyvias aukštesnio lygio ryšio priemones. Tada mes nustatėme savo paprastą vartotojo sąsają, susidedančią iš „TextArea“ produkcija ir Teksto laukas įvestis. Mes išdėstome ir parodome langą, ir pradedame Siūlas klausytojas, priimantis pranešimus iš serverio.

public void run () {try {while (true) {String line = i.readUTF (); output.appendText (eilutė + "\ n"); }} gaudyti (IOException ex) {ex.printStackTrace (); } pagaliau {klausytojas = niekinis; įvestis.slėpti (); patvirtinti (); pabandykite {o.close (); } gaudyti (IOException ex) {ex.printStackTrace (); }}} 

Kai klausytojo gija įveda paleidimo metodą, mes sėdime begaliniame cikle Stygoss iš įvesties srauto. Kada Stygos atvyksta, mes pridedame jį prie išvesties srities ir pakartojame kilpą. An IOException gali atsirasti, jei nutrūksta ryšys su serveriu. Tokiu atveju išspausdiname išimtį ir atliekame valymą. Atminkite, kad apie tai praneš signalas EOF išimtis nuo readUTF () metodas.

Norėdami išvalyti, pirmiausia priskiriame tai klausytojo nuorodai Siūlas į niekinis; tai likusiam kodui rodo, kad gija nutrūko. Tada paslepiame įvesties lauką ir paskambiname patvirtinti () kad sąsaja būtų vėl išdėstyta, ir uždarykite „OutputStream“ o užtikrinti, kad jungtis būtų uždaryta.

Atkreipkite dėmesį, kad mes atliekame visą valymą a pagaliau sąlyga, taigi tai įvyks ar IOException įvyksta čia arba siūlas priverstinai sustabdomas. Mes neuždarome lango iš karto; daroma prielaida, kad vartotojas gali norėti perskaityti sesiją net ir nutrūkus ryšiui.

public Boolean handleEvent (e įvykis) {if ((e.target == įvestis) && (e.id == Įvykis.ACTION_EVENT)) {pabandykite {o.writeUTF ((String) e.arg); o.skalauti (); } pagauti (IOException ex) {ex.printStackTrace (); klausytojas.stop (); } input.setText (""); grįžti tiesa; } else if ((e.target == tai) && (e.id == Įvykis.WINDOW_DESTROY)) {if (klausytojas! = null) klausytojas.stop (); slėpti (); grįžti tiesa; } return super.handleEvent (e); } 

Viduje konors „rankEvent“ () metodas, turime patikrinti du reikšmingus vartotojo sąsajos įvykius:

Pirmasis yra veiksmo įvykis Teksto laukas, o tai reiškia, kad vartotojas paspaudė grįžimo klavišą. Pagavę šį įvykį, mes parašome pranešimą į išvesties srautą, tada paskambiname nuleisti () užtikrinti, kad jis būtų nedelsiant išsiųstas. Išvesties srautas yra a „DataOutputStream“, todėl galime naudoti writeUTF () siųsti a Stygos. Jei an IOException įvyksta ryšys turi nepavykti, todėl mes sustabdome klausytojo giją; tai automatiškai atliks visą būtiną valymą.

Antrasis įvykis yra vartotojas, bandantis uždaryti langą. Programuotojas turi pasirūpinti šia užduotimi; sustabdome klausytojo giją ir paslepiame Rėmas.

public static void main (String args []) išmeta IOException {if (args.length! = 2) meta naują RuntimeException ("Sintaksė: ChatClient"); „Socket s“ = naujas „Socket“ (args [0], Integer.parseInt (args [1])); naujas „ChatClient“ („Chat“ + args [0] + “:„ + args [1], s.getInputStream (), s.getOutputStream ()); } 

pagrindinis () metodas paleidžia klientą; užtikriname, kad buvo pateiktas teisingas argumentų skaičius, atidarome a Lizdas į nurodytą pagrindinį kompiuterį ir prievadą, ir mes sukuriame „ChatClient“ prijungtas prie lizdo srautų. Sukūrus lizdą, gali atsirasti išimtis, kuri pašalins šį metodą ir bus rodoma.

Daugialypio serverio kūrimas

Dabar mes kuriame pokalbių serverį, kuris gali priimti kelis ryšius ir kuris transliuos viską, ką jis skaito iš bet kurio kliento. Tai yra laidus skaityti ir rašyti Stygoss UTF formatu.

Šioje programoje yra dvi klasės: pagrindinė, „ChatServer“, yra serveris, kuris priima ryšius iš klientų ir priskiria juos naujiems ryšio tvarkymo objektams. „ChatHandler“ klasė iš tikrųjų dirba klausydamasi žinučių ir transliuodama jas visiems prisijungusiems klientams. Vienas siūlas (pagrindinis siūlas) tvarko naujas jungtis, o yra siūlas ( „ChatHandler“ klasė) kiekvienam klientui.

Kiekvienas naujas „ChatClient“ prisijungs prie „ChatServer“; tai „ChatServer“ perduos ryšį su nauju „ChatHandler“ klasė, kuri gaus pranešimus iš naujojo kliento. per „ChatHandler“ klasėje, tvarkomas dabartinių tvarkytojų sąrašas; transliacija () metodas naudoja šį sąrašą perduoti pranešimą visiems prisijungusiems „ChatClient“s.

„Class ChatServer“

Ši klasė yra susijusi su priėmimu iš klientų ir paleidimo tvarkyklių paleidimu, kad juos apdorotų.

importuoti java.net. *; importuoti java.io. *; importuoti java.util. *; viešosios klasės „ChatServer“ {// viešasis „ChatServer“ (int portas) išmeta „IOException“ ... // „public static void main“ (eilutės argumentai []) meta „IOException“ 

Ši klasė yra paprasta atskira programa. Mes tiekiame konstruktorių, kuris atlieka visą faktinį klasės darbą, ir a pagrindinis () metodas, kuris jį iš tikrųjų pradeda.

 viešasis „ChatServer“ („int port“) išmeta „IOException“ {ServerSocket server = naujas ServerSocket (prievadas); while (tiesa) {Socket klientas = serveris.accept (); System.out.println ("Priimta iš" + client.getInetAddress ()); „ChatHandler“ c = naujas „ChatHandler“ (klientas); c. pradžia (); }} 

Šis konstruktorius, atliekantis visą serverio darbą, yra gana paprastas. Mes kuriame a „ServerSocket“ ir tada sėdėti kilpa priimant klientus su priimti() metodas „ServerSocket“. Kiekvienam ryšiui sukuriame naują „ChatHandler“ klasę, praeina naują Lizdas kaip parametrą. Sukūrę šį tvarkytuvą, mes jį pradedame pradžia () metodas. Tai pradeda naują giją, skirtą valdyti ryšį, kad mūsų pagrindinė serverio kilpa galėtų toliau laukti naujų ryšių.

public static void main (String args []) meta IOException {if (args.length! = 1) mesti naują RuntimeException ("Sintaksė: ChatServer"); naujas „ChatServer“ („Integer.parseInt“ (argumentai [0])); } 

pagrindinis () metodas sukuria „ChatServer“, perduodamas komandinės eilutės prievadą kaip parametrą. Tai yra prievadas, prie kurio prisijungs klientai.

Klasės „ChatHandler“

Ši klasė yra susijusi su atskirų ryšių tvarkymu. Turime gauti pranešimus iš kliento ir juos vėl išsiųsti į visus kitus ryšius. Mes palaikome jungčių sąrašą a

statinis

Vektorius.

importuoti java.net. *; importuoti java.io. *; importuoti java.util. *; viešoji klasė „ChatHandler“ pratęsia giją {// viešoji „ChatHandler“ („Socket s“) išmeta „IOException“ ... // „public void run“ () ...} 

Mes pratęsiame Siūlas klasę, kad leistų atskirai gijai apdoroti susietą klientą. Konstruktorius priima a Lizdas prie kurios mes prisirišame; paleisti () metodas, vadinamas nauju siūlu, atlieka faktinį kliento apdorojimą.

 apsaugoti „Socket s“; saugoma „DataInputStream i“; apsaugotas „DataOutputStream o“; viešasis „ChatHandler“ („Socket s“) išmeta „IOException“ {this.s = s; i = new DataInputStream (nauja BufferedInputStream (s.getInputStream ())); o = nauja „DataOutputStream“ (nauja „BufferedOutputStream“ (s.getOutputStream ())); } 

Konstruktorius saugo nuorodą į kliento lizdą ir atidaro įvesties ir išvesties srautą. Vėlgi, mes naudojame buferinius duomenų srautus; tai suteikia mums veiksmingą įvestį / išvestį ir metodus, kaip perduoti aukšto lygio duomenų tipus - šiuo atveju Stygoss.

saugomi statinių vektorių tvarkytuvai = naujas vektorius (); public void run () {try {handlers.addElement (this); while (tiesa) {String msg = i.readUTF (); transliacija (msg); }} gaudyti (IOException ex) {ex.printStackTrace (); } pagaliau {handlers.removeElement (this); pabandykite {s.close (); } pagauti (IOException ex) {ex.printStackTrace (); }}} // saugoma statinė negaliojanti transliacija (eilutės pranešimas) ... 

paleisti () metodas yra tai, kur patenka mūsų siūlai. Pirmiausia pridedame savo siūlą prie Vektorius apie „ChatHandler“s tvarkytojai. Tvarkytojai Vektorius veda visų esamų tvarkytojų sąrašą. Tai yra statinis kintamasis, taigi yra vienas Vektorius visumai „ChatHandler“ klasę ir visas jos instancijas. Taigi, visi „ChatHandler“s gali pasiekti esamų ryšių sąrašą.

Atkreipkite dėmesį, kad mums labai svarbu vėliau pašalinti save iš šio sąrašo, jei nepavyksta prisijungti; priešingu atveju visi kiti tvarkytojai bandys mums rašyti, kai transliuos informaciją. Tokio tipo situacija, kai būtina atlikti veiksmus baigus kodo dalį, yra pagrindinis pabandyk ... pagaliau sukonstruoti; todėl mes visą savo darbą atliekame per pabandyk ... pagauti ... pagaliau sukonstruoti.

Šio metodo turinys gauna pranešimus iš kliento ir persiunčia juos visiems kitiems klientams naudodamas transliacija () metodas. Baigus kilpą dėl kliento skaitomos išimties ar dėl to, kad ši gija sustabdyta, pagaliau garantuojama, kad sąlyga bus įvykdyta. Šiame punkte mes pašaliname savo siūlą iš tvarkytojų sąrašo ir uždarome lizdą.

apsaugota statinė tuštuma transliacija (eilutės pranešimas) {sinchronizuota (tvarkytojai) {Surašymas e = tvarkytojai.elementai (); while (e.hasMoreElements ()) {ChatHandler c = (ChatHandler) e.nextElement (); pabandykite {sinchronizuoti (c.o) {c.o.writeUTF (pranešimas); } c.o.flush (); } pagauti (IOException ex) {c.stop (); }}}} 

Šis metodas transliuoja pranešimą visiems klientams. Pirmiausia sinchronizuojame tvarkytojų sąraše. Mes nenorime, kad žmonės prisijungtų ar išeitų, kol mes tęsiamės, tuo atveju, jei bandysime transliuoti asmenį, kurio nebėra; tai priverčia klientus laukti, kol baigsime sinchronizuoti. Jei serveris turi tvarkyti ypač dideles apkrovas, galime pateikti tikslesnę sinchronizaciją.

Šiame sinchronizuotame bloke gauname Surašymas dabartinių tvarkytojų. Surašymas klasėje yra patogus būdas kartoti visus a elementus Vektorius. Mūsų kilpa tiesiog parašo pranešimą kiekvienam elementui Surašymas. Atminkite, kad jei rašant į a, įvyksta išimtis „ChatClient“, tada paskambiname kliento sustabdyti() metodas; tai sustabdo kliento giją ir todėl atlieka tinkamą valymą, įskaitant kliento pašalinimą iš tvarkytuvų.