Programmēšana

Vienkārša tīkla taimauta apstrāde

Daudzi programmētāji baidās domāt par tīkla taimautu apstrādi. Bailes ir tādas, ka vienkāršs, viena pavediena tīkla klients bez noildzes atbalsta kļūs par sarežģītu vairāku pavedienu murgu, ar atsevišķiem pavedieniem, kas nepieciešami tīkla taimautu noteikšanai, un kāda veida paziņošanas procesu darbā starp bloķēto pavedienu un galveno lietojumprogrammu. Lai gan šī ir viena iespēja izstrādātājiem, tā nav vienīgā. Tīkla noildzes novēršanai nav jābūt sarežģītam uzdevumam, un daudzos gadījumos jūs varat pilnībā izvairīties no papildu pavedienu koda rakstīšanas.

Strādājot ar tīkla savienojumiem vai jebkura veida I / O ierīcēm, ir divas operāciju klasifikācijas:

  • Bloķēšanas darbības: Lasīt vai rakstīt stendus, darbība gaida, līdz I / O ierīce ir gatava
  • Darbības bez bloķēšanas: Tiek veikts lasīšanas vai rakstīšanas mēģinājums, darbība tiek pārtraukta, ja I / O ierīce nav gatava

Java tīkls pēc noklusējuma ir I / O bloķēšanas veids. Tādējādi, ja Java tīkla lietojumprogramma nolasās no kontaktligzdas savienojuma, tā parasti gaidīs bezgalīgi, ja nebūs tūlītējas atbildes. Ja dati nav pieejami, programma turpinās gaidīt, un turpmāku darbu nevar veikt. Viens no risinājumiem, kas atrisina problēmu, bet ievieš nedaudz papildu sarežģītību, ir likt operācijai veikt otru pavedienu; Tādā veidā, ja otrais pavediens tiek bloķēts, lietojumprogramma joprojām var reaģēt uz lietotāja komandām vai pat pārtraukt iestrēgušo pavedienu, ja nepieciešams.

Šis risinājums bieži tiek izmantots, taču ir daudz vienkāršāka alternatīva. Java atbalsta arī nebloķējošu tīkla I / O, ko var aktivizēt jebkurā vietā Kontaktligzda, ServerSocketvai Datagrammas ligzda. Pirms vadības atgriešanas atpakaļ lietojumprogrammā ir iespējams norādīt maksimālo laika periodu, kurā lasīšanas vai rakstīšanas darbība apstāsies. Tīkla klientiem tas ir vienkāršākais risinājums un piedāvā vienkāršāku, vieglāk pārvaldāmu kodu.

Vienīgais Java bloķēšanas tīkla I / O trūkums ir tas, ka tam nepieciešama esoša kontaktligzda. Tādējādi, lai gan šī metode ir ideāli piemērota normālām lasīšanas vai rakstīšanas operācijām, savienojuma darbība var apstāties daudz ilgāku laiku, jo savienojuma darbību taimauta perioda norādīšanai nav metodes. Daudzām lietojumprogrammām ir nepieciešama šī spēja; tomēr jūs varat viegli izvairīties no papildu darba, rakstot papildu kodu. Esmu uzrakstījis nelielu klasi, kas ļauj norādīt savienojuma taimauta vērtību. Tas izmanto otro pavedienu, bet iekšējās detaļas tiek abstrahētas. Šī pieeja darbojas labi, jo tā nodrošina nebloķējošu I / O saskarni, un otrā pavediena detaļas netiek paslēptas.

Tīkla I / O nebloķēšana

Vienkāršākais veids, kā kaut ko darīt, bieži izrādās labākais veids. Lai gan dažreiz ir nepieciešams izmantot pavedienus un bloķēt I / O, vairumā gadījumu I / O bloķēšana ir daudz skaidrāks un elegants risinājums. Tikai ar dažām koda rindiņām jūs varat iekļaut taimauta atbalstu jebkurai ligzdas lietojumprogrammai. Netici man? Turpini lasīt.

Kad Java 1.1 tika izlaists, tajā bija iekļautas API izmaiņas java.net pakete, kas ļāva programmētājiem norādīt ligzdu opcijas. Šīs opcijas ļauj programmētājiem labāk kontrolēt kontaktligzdas sakarus. Īpaši viena iespēja, SO_TIMEOUT, ir ārkārtīgi noderīga, jo tas ļauj programmētājiem norādīt laiku, kādu bloķēs lasīšanas darbība. Mēs varam norādīt īsu kavēšanos vai vispār nevienu, un padarīt mūsu tīkla kodu neatbloķētu.

Apskatīsim, kā tas darbojas. Jauna metode, setSoTimeout (int) ir pievienots šādām ligzdu klasēm:

  • java.net.Socket
  • java.net.DatagramSocket
  • java.net.ServerSocket

Šī metode ļauj mums norādīt maksimālo noildzes ilgumu milisekundēs, ko bloķēs šīs tīkla darbības:

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

Ikreiz, kad tiek izsaukta kāda no šīm metodēm, pulkstenis sāk tikties. Ja darbība nav bloķēta, tā tiks atiestatīta un tiks restartēta tikai tad, kad kāda no šīm metodēm tiks izsaukta vēlreiz; rezultātā noildze nekad nevar notikt, ja vien neveicat tīkla I / O darbību. Šis piemērs parāda, cik viegli var apstrādāt taimautus, neizmantojot vairākus izpildes pavedienus:

// Izveidojiet datagrammas ligzdu portā 2000, lai klausītos ienākošās UDP paketes DatagramSocket dgramSocket = new DatagramSocket (2000); // Atspējot I / O darbību bloķēšanu, norādot piecu sekunžu taimautu dgramSocket.setSoTimeout (5000); 

Piešķirot taimauta vērtību, mūsu tīkla darbības tiek bloķētas uz nenoteiktu laiku. Šajā brīdī jūs, iespējams, domājat, kas notiks, kad tiks pārtraukta tīkla darbība. Tā vietā, lai atgrieztu kļūdas kodu, kuru izstrādātāji ne vienmēr var pārbaudīt, a java.io.IztraucētaIOException tiek izmests. Izņēmumu apstrāde ir lielisks veids, kā tikt galā ar kļūdu apstākļiem, un ļauj mums nošķirt parasto kodu no mūsu kļūdu apstrādes koda. Turklāt, kurš reliģiski pārbauda katru atgriešanās vērtību, lai uzzinātu par nulli? Izmetot izņēmumu, izstrādātāji ir spiesti nodrošināt nozvejas apstrādātāju taimautiem.

Šis koda fragments parāda, kā rīkoties taimauta darbībai, lasot no TCP ligzdas:

// Iestatiet kontaktligzdas taimautu desmit sekundes connection.setSoTimeout (10000); mēģiniet {// izveidot DataInputStream lasīšanai no ligzdas DataInputStream din = new DataInputStream (connection.getInputStream ()); // Datu lasīšana līdz (; ;;) {String line = din.readLine (); datu beigām if (līnija! = null) System.out.println (līnija); vēl saplīst; }} // Izņēmums, kas izmests, kad rodas tīkla noildze. Catch (InterruptedIOException iioe) {System.err.println ("Attālās resursdatora noildze lasīšanas operācijas laikā"); } // Izņēmums, kas radies, kad rodas vispārēja tīkla I / O kļūda (IOException ioe) {System.err.println ("Tīkla I / O kļūda -" + ioe); } 

Tikai ar dažām papildu koda rindiņām a izmēģināt {} uztveršanas bloķēšana, ir ārkārtīgi viegli noķert tīkla noildzes. Pēc tam lietojumprogramma var reaģēt uz situāciju, neapturot sevi. Piemēram, to var sākt, paziņojot lietotājam vai mēģinot izveidot jaunu savienojumu. Izmantojot datagrammas ligzdas, kas nosūta informācijas paketes, negarantējot piegādi, lietojumprogramma varētu atbildēt uz tīkla noildzi, nosūtot atkārtoti pakešu laikā pazaudēto paketi. Šī noildzes atbalsta ieviešana prasa ļoti maz laika un rada ļoti tīru risinājumu. Patiešām, vienīgais laiks, kad I / O bloķēšana nav optimāls risinājums, ir tad, kad ir jānosaka arī savienojuma darbību taimauts vai ja mērķa vide neatbalsta Java 1.1.

Taimauta apstrāde savienojuma operācijās

Ja jūsu mērķis ir panākt pilnīgu taimauta noteikšanu un apstrādi, jums jāapsver savienojuma darbības. Veidojot java.net.Socket, tiek mēģināts izveidot savienojumu. Ja resursdators ir aktīvs, bet portā, kas norādīts java.net.Socket konstruktors, a ConnectionException tiks izmests, un kontrole atgriezīsies lietojumprogrammā. Tomēr, ja iekārta nedarbojas vai ja nav maršruta uz šo resursdatoru, kontaktligzdas savienojums galu galā noildzīs daudz vēlāk. Tikmēr jūsu lietojumprogramma paliek iesaldēta, un nav iespējas mainīt taimauta vērtību.

Lai gan kontaktligzdas konstruktora zvans galu galā atgriezīsies, tas ievieš ievērojamu kavēšanos. Viens no veidiem, kā tikt galā ar šo problēmu, ir izmantot otru pavedienu, kas veiks potenciāli bloķējošo savienojumu, un nepārtraukti aptaujāt šo pavedienu, lai redzētu, vai ir izveidots savienojums.

Tomēr tas ne vienmēr rada elegantu risinājumu. Jā, jūs varētu pārvērst savus tīkla klientus par daudzsavienojošām lietojumprogrammām, taču bieži vien tas prasa pārāk daudz papildu darbu, lai to izdarītu. Tas padara kodu sarežģītāku, un, rakstot tikai vienkāršu tīkla lietojumprogrammu, ir grūti attaisnot nepieciešamo piepūli. Ja jūs rakstāt daudz tīkla lietojumprogrammu, jūs bieži pamanāt riteni no jauna. Tomēr ir vienkāršāks risinājums.

Esmu uzrakstījis vienkāršu, atkārtoti lietojamu klasi, kuru varat izmantot savās lietojumprogrammās. Klase ģenerē TCP ligzdas savienojumu, ilgstoši neatrimstot. Jūs vienkārši piezvanāt uz getSocket metodi, norādot resursdatora nosaukumu, portu un taimautu un saņemot kontaktligzdu. Šis piemērs parāda savienojuma pieprasījumu:

// Izveidojiet savienojumu ar attālo serveri, izmantojot resursdatora nosaukumu, ar četru sekunžu taimauta savienojumu Socket = TimedSocket.getSocket ("server.my-network.net", 23, 4000); 

Ja viss izdosies, tiks atgriezta kontaktligzda, tāpat kā standarta java.net.Socket konstruktori. Ja savienojumu nevar izveidot, pirms iestājas norādītais noildze, metode tiks pārtraukta un izmetīs java.io.IztraucētaIOException, tāpat kā citas ligzdas lasīšanas darbības, ja taimauts ir norādīts, izmantojot a setSoTimeout metodi. Diezgan viegli, ja?

Iekļaut vairāku pavedienu tīkla kodu vienā klasē

Kamēr TimedSocket klase pati par sevi ir noderīgs komponents, tas ir arī ļoti labs mācību līdzeklis, lai saprastu, kā rīkoties ar I / O bloķēšanu. Veicot bloķēšanas darbību, viena pavediena programma tiks bloķēta uz nenoteiktu laiku. Tomēr, ja tiek izmantoti vairāki izpildes pavedieni, jāstājas tikai vienam pavedienam; otru pavedienu var turpināt izpildīt. Apskatīsim, kā TimedSocket klases darbi.

Kad lietojumprogrammai ir jāizveido savienojums ar attālo serveri, tā izsauc TimedSocket.getSocket () metodi un nodod informāciju par attālo resursdatoru un portu. The getSocket () metode ir pārslogota, ļaujot gan a Stīga resursdatora nosaukums un InetAddress tiks precizēts. Šim parametru diapazonam vajadzētu būt pietiekamam lielākajai daļai ligzdas darbību, lai gan īpašām ieviešanām varētu pievienot pielāgotu pārslodzi. Iekšpusē getSocket () metodi, tiek izveidots otrais pavediens.

Tēlaini nosaukts SocketThread izveidos java.net.Socket, kas potenciāli var bloķēt ievērojamu laiku. Tas nodrošina piekļuves metodes, lai noteiktu, vai ir izveidots savienojums vai ir notikusi kļūda (piemēram, ja java.net.SocketException tika iemests savienojuma laikā).

Kamēr tiek izveidots savienojums, primārais pavediens gaida, līdz tiek izveidots savienojums, rodas kļūda vai tīkla noildze. Ik pēc simts milisekundēm tiek pārbaudīts, vai otrais pavediens ir sasniedzis savienojumu. Ja šī pārbaude neizdodas, ir jāveic otra pārbaude, lai noteiktu, vai savienojumā radās kļūda. Ja nē, un savienojuma mēģinājums joprojām turpinās, taimeris tiek palielināts, un pēc neliela miega savienojums tiks atkārtoti aptaujāts.

Šī metode intensīvi izmanto izņēmumu apstrādi. Ja rodas kļūda, šis izņēmums tiks nolasīts no SocketThread piemēram, un tas atkal tiks izmests. Ja iestājas tīkla noildze, šī metode metīs a java.io.IztraucētaIOException.

Šis koda fragments parāda vēlēšanu mehānismu un kļūdu apstrādes kodu.

for (;;) {// Pārbaudiet, vai nav izveidots savienojums, ja (st.isConnected ()) {// Jā ... piešķiriet zeķu mainīgajam un izlauzieties no cilpas zeķes = st.getSocket (); pārtraukums; } else {// Pārbaudiet, vai nav notikusi kļūda, ja (st.isError ()) {// Nevarēja izveidot savienojumu mest (st.getException ()); } mēģiniet {// gulēt īsu laika periodu Thread.sleep (POLL_DELAY); } catch (InterruptedException ie) {} // Pieauguma taimera taimeris + = POLL_DELAY; // Pārbaudiet, vai laika ierobežojums nav pārsniegts, ja (taimeris> aizkave) {// Nevar izveidot savienojumu ar serveri, lai izveidotu jaunu InterruptedIOException ("Nevarēja izveidot savienojumu ar" + aizkave + "milisekundes"); }}} 

Bloķētā pavediena iekšpusē

Kamēr savienojums tiek regulāri aptaujāts, otrais pavediens mēģina izveidot jaunu java.net.Socket. Lai noteiktu savienojuma stāvokli, kā arī lai iegūtu gala kontaktligzdas savienojumu, ir paredzētas piekļuves metodes. The SocketThread.isConnected () metode atgriež Būla vērtību, lai norādītu, vai ir izveidots savienojums, un SocketThread.getSocket () metode atgriež a Kontaktligzda. Tiek piedāvātas līdzīgas metodes, lai noteiktu, vai ir notikusi kļūda, un lai piekļūtu pieķertajam izņēmumam.

Visas šīs metodes nodrošina kontrolētu saskarni SocketThread piemēram, nepieļaujot privātu dalībnieku mainīgo ārēju modifikāciju. Šis koda piemērs parāda pavedienu palaist () metodi. Kad un ja ligzdas konstruktors atgriež a Kontaktligzda, tas tiks piešķirts privātā locekļa mainīgajam, kuram piekļuves metodes nodrošina piekļuvi. Nākamreiz, kad tiek vaicāts savienojuma stāvoklis, izmantojot SocketThread.isConnected () metodi, ligzda būs pieejama lietošanai. To pašu paņēmienu izmanto, lai atklātu kļūdas; ja java.io.IOException ir noķerts, tas tiks saglabāts privātā dalībniekā, kuram var piekļūt, izmantojot isError () un getException () piekļuves metodes.

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