Programiranje

Preprosto upravljanje omrežnih časovnih omejitev

Mnogi programerji se bojijo razmišljanja o ravnanju z omrežnimi časovnimi omejitvami. Pogost strah je, da bo preprost enonitni omrežni odjemalec brez podpore za časovno omejitev prešel v zapleteno večnitno nočno moro z ločenimi nitmi, potrebnimi za zaznavanje omrežnih časovnih omejitev, in nekakšnim postopkom obveščanja med blokirano niti in glavno aplikacijo. Čeprav je to ena možnost za razvijalce, ni edina. Obravnavanje omrežnih časovnih omejitev ne sme biti težka naloga in v mnogih primerih se lahko popolnoma izognete pisanju kode za dodatne niti.

Pri delu z omrežnimi povezavami ali katero koli vrsto V / I naprave obstajata dve klasifikaciji operacij:

  • Blokiranje operacij: Branje ali pisanje stojnic, operacija čaka, dokler ni pripravljena I / O naprava
  • Neblokiranje: Poskus branja ali pisanja je prekinjen, če V / I naprava ni pripravljena

Omrežje Java je privzeto oblika blokiranja V / I. Ko torej mrežna aplikacija Java bere iz vtičnice, običajno čaka neomejeno, če ni takojšnjega odziva. Če podatki niso na voljo, bo program še naprej čakal in nadaljnjega dela ni mogoče opraviti. Ena od rešitev, ki reši težavo, vendar vnese nekaj dodatne zapletenosti, je, da operacijo izvede druga nit; na ta način, če se druga nit blokira, se lahko aplikacija še vedno odziva na uporabniške ukaze ali po potrebi ustavi zaustavljeno nit.

Ta rešitev se pogosto uporablja, vendar obstaja veliko preprostejša alternativa. Java podpira tudi neblokirajoči omrežni V / I, ki ga je mogoče aktivirati na katerem koli Vtičnica, ServerSocket, ali DatagramSocket. Preden vrnete nadzor nazaj v aplikacijo, lahko določite največji čas, ko se bo operacija branja ali pisanja zaustavila. Za omrežne odjemalce je to najlažja rešitev in ponuja preprostejšo in bolj obvladljivo kodo.

Edina pomanjkljivost neblokirajočega omrežnega V / I pod Javo je ta, da zahteva obstoječo vtičnico. Čeprav je ta metoda kot nalašč za običajne operacije branja ali pisanja, lahko operacija povezovanja zastane veliko daljše obdobje, saj ni metode za določanje obdobja zakasnitve za operacije povezovanja. Številne aplikacije zahtevajo to sposobnost; lahko pa se zlahka izognete dodatnemu pisanju dodatne kode. Napisal sem majhen razred, ki vam omogoča, da določite vrednost časovne omejitve za povezavo. Uporablja drugo nit, notranje podrobnosti pa so povzete. Ta pristop deluje dobro, saj zagotavlja neblokirajoči vhodno-izhodni vmesnik, podrobnosti druge niti pa so skrite pred očmi.

Neblokirajoči omrežni V / I

Najpreprostejši način, kako nekaj narediti, se pogosto izkaže za najboljši. Čeprav je včasih treba uporabiti niti in blokirati V / I, je v večini primerov neblokiranje V / I veliko jasnejša in elegantnejša rešitev. Z le nekaj vrsticami kode lahko vključite podporo za časovno omejitev za katero koli aplikacijo vtičnice. Ne verjameš mi? Beri naprej.

Ko je bila izdana Java 1.1, je vključevala spremembe API-ja v java.net paket, ki je programerjem omogočal določitev možnosti vtičnice. Te možnosti dajejo programerjem večji nadzor nad vtičnico. Zlasti ena možnost, SO_TIMEOUT, je izredno uporaben, saj programerjem omogoča, da določijo, koliko časa bo operacija branja blokirala. Določimo lahko kratek zamik ali pa ga sploh ne, in onemogočimo blokiranje naše mrežne kode.

Oglejmo si, kako to deluje. Nova metoda, setSoTimeout (int) je bil dodan v naslednje razrede vtičnic:

  • java.net.Vtičnica
  • java.net.DatagramSocket
  • java.net.ServerSocket

Ta metoda nam omogoča, da v milisekundah določimo največjo dolžino časovne omejitve, ki jo bodo blokirale naslednje omrežne operacije:

  • ServerSocket.accept ()
  • SocketInputStream.read ()
  • DatagramSocket.receive ()

Vsakič, ko pokličete enega od teh načinov, začne ura teči. Če operacija ni blokirana, se ponastavi in ​​znova zažene šele, ko se ponovno pokliče ena od teh metod; posledično ne more priti do časovne omejitve, če ne izvedete omrežne V / I operacije. Naslednji primer prikazuje, kako enostavno je obvladovati časovne omejitve, ne da bi se zatekli k več izvedbenim nitim:

// Ustvari vtičnico za datagrame na vratih 2000 za poslušanje dohodnih paketov UDP DatagramSocket dgramSocket = new DatagramSocket (2000); // Onemogoči blokiranje V / I operacij, tako da določite petsekundoutout dgramSocket.setSoTimeout (5000); 

Dodelitev vrednosti časovne omejitve preprečuje blokiranje naših omrežnih operacij za nedoločen čas. Na tej točki se verjetno sprašujete, kaj se bo zgodilo, ko omrežna operacija poteče. Namesto da bi vrnil kodo napake, ki je razvijalci morda ne bodo vedno preverili, a java.io.InterruptedIOException je vržen. Obravnava izjem je odličen način za obvladovanje napak in nam omogoča, da ločimo običajno kodo od kode za obdelavo napak. Poleg tega, kdo religiozno preveri vsako vrnjeno vrednost za ničelno referenco? Z vrnitvijo izjeme so razvijalci prisiljeni zagotoviti nadzornik ulova za časovne omejitve.

Naslednji delček kode prikazuje, kako ravnati s časovno omejitvijo pri branju iz vtičnice TCP:

// Nastavite časovno omejitev vtičnice za deset sekund connection.setSoTimeout (10000); poskusite {// Ustvari DataInputStream za branje iz vtičnice DataInputStream din = new DataInputStream (connection.getInputStream ()); // Branje podatkov do konca podatkov za (;;) {String line = din.readLine (); if (vrstica! = null) System.out.println (vrstica); sicer počitek; }} // Izjema, vržena, ko pride do zakasnitve omrežja (InterruptIOException iioe) {System.err.println ("Časovna omejitev oddaljenega gostitelja je potekla med operacijo branja"); } // Izjema, vržena, ko pride do splošne napake I / O omrežja catch (IOException ioe) {System.err.println ("Napaka I / O omrežja -" + ioe); } 

Z le nekaj dodatnimi vrsticami kode za poskusite {} catch block, izredno enostavno je ujeti omrežne časovne omejitve. Nato se aplikacija lahko odzove na situacijo, ne da bi se sama zaustavila. Na primer, lahko se začne z obvestilom uporabnika ali poskusom vzpostavitve nove povezave. Pri uporabi vtičnic datagrama, ki pošiljajo pakete informacij, ne da bi zagotovili dostavo, se lahko aplikacija odzove na omrežno časovno omejitev tako, da pošlje paket, ki je bil izgubljen med prenosom. Izvajanje te podpore za časovno omejitev traja zelo malo časa in vodi do zelo čiste rešitve. Deblokiranje V / I-ja edino takrat ni najboljša rešitev, kadar morate zaznati tudi časovne omejitve pri operacijah povezovanja ali ko vaše ciljno okolje ne podpira Jave 1.1.

Obdelava časovne omejitve pri operacijah povezovanja

Če je vaš cilj doseči popolno zaznavanje in ravnanje s časovno omejitvijo, boste morali razmisliti o operacijah povezovanja. Pri ustvarjanju primerka java.net.Vtičnica, poskusite vzpostaviti povezavo. Če je gostiteljska naprava aktivna, vendar se na vratih, ki so določena v java.net.Vtičnica konstruktor, a ConnectionException bo vržen in nadzor se bo vrnil v aplikacijo. Če pa naprava ne deluje ali če do tega gostitelja ni poti, se povezava z vtičnico sčasoma izteče precej pozneje. Medtem vaša aplikacija ostane zamrznjena in vrednosti časovne omejitve ni mogoče spremeniti.

Čeprav se bo klic konstruktorja vtičnice sčasoma vrnil, to povzroča znatno zamudo. Eden od načinov za reševanje te težave je uporaba druge niti, ki bo izvedla potencialno blokirajočo povezavo, in nenehno anketiranje te niti, da bi ugotovila, ali je povezava vzpostavljena.

Vendar to ne vodi vedno do elegantne rešitve. Da, svoje omrežne odjemalce bi lahko pretvorili v večnitne programe, vendar je veliko dodatnega dela, ki je potrebno za to, pogosto previsoko. Kodo naredi bolj zapleteno in pri pisanju le preproste omrežne aplikacije je zahteven napor težko upravičiti. Če pišete veliko omrežnih aplikacij, boste pogosto izumljali kolo. Obstaja pa preprostejša rešitev.

Napisal sem preprost razred za večkratno uporabo, ki ga lahko uporabljate v svojih aplikacijah. Razred generira povezavo vtičnice TCP, ne da bi se dolgo zavlačeval. Preprosto pokličete a getSocket metoda, ki določa ime gostitelja, vrata in zakasnitev, in prejme vtičnico. Naslednji primer prikazuje zahtevo za povezavo:

// Povežite se z oddaljenim strežnikom z imenom gostitelja s štiri sekundno povezavo s časovno omejitvijo vtičnice = TimedSocket.getSocket ("server.my-network.net", 23, 4000); 

Če bo vse v redu, se vtičnica vrne, tako kot običajno java.net.Vtičnica konstruktorji. Če povezave ni mogoče vzpostaviti, preden nastopi določena časovna omejitev, se metoda ustavi in ​​vrže java.io.InterruptedIOException, tako kot druge operacije branja vtičnice, ko je bil z uporabo a setSoTimeout metoda. Precej enostavno, kaj?

Kapsuliranje večnitne omrežne kode v en razred

Medtem ko TimedSocket razred je koristen sestavni del sam po sebi, je tudi zelo dober pripomoček za razumevanje, kako ravnati z blokiranjem V / I. Ko se izvede blokirna operacija, se aplikacija z enim navojem neomejeno blokira. Če je uporabljenih več izvedbenih niti, pa le ena nit potrebuje zastoj; druga nit se lahko še naprej izvaja. Oglejmo si, kako TimedSocket razredna dela.

Ko se mora aplikacija povezati z oddaljenim strežnikom, prikliče datoteko TimedSocket.getSocket () metoda in posreduje podrobnosti oddaljenega gostitelja in vrat. The getSocket () metoda je preobremenjena, kar omogoča tako a Vrvica ime gostitelja in InetAddress treba določiti. Ta obseg parametrov bi moral zadoščati za večino operacij vtičnic, čeprav bi bilo mogoče dodati posebne preobremenitve za posebne izvedbe. Znotraj getSocket () metoda, se ustvari druga nit.

Domiselno imenovan SocketThread bo ustvaril primerek java.net.Vtičnica, ki lahko potencialno dolgo blokira. Ponuja dostopne metode za ugotavljanje, ali je bila povezava vzpostavljena ali je prišlo do napake (na primer, če java.net.SocketException je bil vržen med povezavo).

Medtem ko je povezava vzpostavljena, primarna nit čaka, dokler se povezava ne vzpostavi, da pride do napake ali omrežne časovne omejitve. Vsakih sto milisekund se preveri, ali je druga nit dosegla povezavo. Če to preverjanje ne uspe, je treba z drugim preveriti, ali je prišlo do napake v povezavi. V nasprotnem primeru se poskus povezave še vedno nadaljuje, časovnik se poveča in po majhnem spanju bo povezava spet anketirana.

Ta metoda pogosto uporablja obdelavo izjem. Če pride do napake, bo ta izjema prebrana iz SocketThread in bo spet vržen. Če pride do omrežne časovne omejitve, bo metoda vrgla a java.io.InterruptedIOException.

Naslednji delček kode prikazuje mehanizem glasovanja in kodo za obdelavo napak.

for (;;) {// Preverite, ali je povezava vzpostavljena, če (st.isConnected ()) {// Da ... dodeli spremenljivki nogavice in prekinite zanko sock = st.getSocket (); odmor; } else {// Preverite, ali je prišlo do napake, če (st.isError ()) {// Povezave ni bilo mogoče vzpostaviti throw (st.getException ()); } poskusite {// Kratek čas spanja Thread.sleep (POLL_DELAY); } catch (InterruptedException ie) {} // Časovnik prirastka časovnika + = POLL_DELAY; // Preverite, ali je časovna omejitev presežena, če (timer> delay) {// Ne morem vzpostaviti povezave s strežnikom in vrniti novega InterruptedIOException ("Povezave ni bilo mogoče vzpostaviti za" + zakasnitev + "milisekunde"); }}} 

Znotraj blokirane niti

Medtem ko je povezava redno anketirana, druga nit poskuša ustvariti nov primerek java.net.Vtičnica. Na voljo so metode dostopa za določitev stanja povezave in za pridobitev končne povezave z vtičnico. The SocketThread.isConnected () vrne logično vrednost, ki označuje, ali je bila povezava vzpostavljena, in SocketThread.getSocket () metoda vrne a Vtičnica. Podobne metode so na voljo za ugotavljanje, ali je prišlo do napake, in za dostop do izjeme, ki je bila ujeta.

Vse te metode zagotavljajo nadzorovan vmesnik za SocketThread primerka, ne da bi dovolil zunanje spreminjanje spremenljivk zasebnega člana. Naslednji primer kode prikazuje nit teči () metoda. Kdaj in če konstruktor vtičnice vrne a Vtičnica, bo dodeljena spremenljivki zasebnega člana, do katere dostopne metode omogočajo dostop. Naslednjič, ko bo stanje povezave poizvedeno s pomočjo SocketThread.isConnected () metoda, bo vtičnica na voljo za uporabo. Ista tehnika se uporablja za odkrivanje napak; če java.io.IOException je ujeta, bo shranjena v zasebnem članu, do katerega je mogoče dostopati prek isError () in getException () metode dostopa.

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