Priza de programare în Java: Un tutorial


Acest tutorial este o introducere la priza de programare în Java, începând cu un simplu client-server exemplu care demonstrează caracteristicile de bază ale Java I/O. va fi introdus la ambele originale java.io pachet și NIO, non-blocking I/O (java.nio) APIs introdus în Java 1.4. În cele din urmă, veți vedea un exemplu care demonstrează Java networking implementat de la Java 7 forward, în NIO.2.,

programarea soclurilor se reduce la două sisteme care comunică între ele. În general, comunicarea în rețea are două arome: Transport Control Protocol (TCP) și User Datagram Protocol (UDP). TCP și UDP sunt utilizate în scopuri diferite și ambele au constrângeri unice:

  • TCP este un protocol relativ simplu și fiabil care permite unui client să facă o conexiune la un server și cele două sisteme să comunice. În TCP, fiecare entitate știe că au fost primite sarcini utile de comunicare.,
  • UDP este un protocol fără conexiune și este bun pentru scenarii în care nu aveți nevoie neapărat de fiecare pachet pentru a ajunge la destinație, cum ar fi streaming media.pentru a aprecia diferența dintre TCP și UDP, luați în considerare ce s-ar întâmpla dacă ați transmite video de pe site-ul dvs. preferat și a scăzut cadre. Ați prefera ca clientul să încetinească filmul dvs. pentru a primi cadrele lipsă sau ați prefera ca videoclipul să continue să fie redat? Protocoalele de streaming Video folosesc de obicei UDP., Deoarece TCP garantează livrarea, este protocolul de alegere pentru HTTP, FTP, SMTP, POP3 și așa mai departe.

    în acest tutorial, vă prezint programarea socket în Java. Vă prezint o serie de exemple client-server care demonstrează caracteristici din cadrul i/o Java original, apoi trec treptat la utilizarea funcțiilor introduse în NIO.2.

    Old-school Java sockets

    în implementări înainte de NIO, Java TCP client socket cod este manipulat de java.net.Socket clasa., Următorul cod se deschide o conexiune cu un server:

    Socket socket = new Socket( server, port );

    Odată ce ne socket exemplu este conectat la server putem începe obținerea de intrare și fluxurile de ieșire la sever. Fluxurile de intrare sunt utilizate pentru a citi datele de pe server, în timp ce fluxurile de ieșire sunt utilizate pentru a scrie date pe server., Putem executa următoarele metode pentru a obține fluxuri de intrare și ieșire:

    InputStream in = socket.getInputStream();OutputStream out = socket.getOutputStream();

    deoarece acestea sunt fluxuri obișnuite, aceleași fluxuri pe care le-am folosi pentru a citi și scrie într-un fișier, le putem converti în forma care servește cel mai bine cazului nostru de utilizare. De exemplu, am putea încheia OutputStream cu un PrintStream astfel încât să ne putem ușor a scrie textul cu metode, cum ar fi println()., Pentru un alt exemplu, am putea încheia InputStream cu un BufferedReader, printr-un InputStreamReader, în scopul de a citi cu ușurință textul cu metode, cum ar fi readLine().

    descarca

    codul sursă pentru ” Socket programare în Java: Un tutorial.”Creat de Steven Haines pentru JavaWorld.

    Java socket client example

    să lucrăm printr-un exemplu scurt care execută un HTTP GET împotriva unui server HTTP., HTTP este mai sofisticat decât permite exemplul nostru, dar putem scrie cod client pentru a gestiona cel mai simplu caz: solicitați o resursă de la server și serverul returnează răspunsul și închide fluxul. Acest caz necesită următorii pași:

    1. creați o priză pe serverul web care ascultă pe portul 80.
    2. Obține un PrintStream la server si trimite cererea GET PATH HTTP/1.0, unde PATH este solicitat de resurse de pe server., De exemplu, dacă am vrut să deschidem rădăcina unui site web, atunci calea ar fi /.
    3. se Obține un InputStream la server, înveliți-l cu un BufferedReader și citește răspunsul linie cu linie.

    listarea 1 arată codul sursă pentru acest exemplu.

    Lista 1. Un simplu magazin de bagaje, de exemplu.java

    Listing 1 acceptă două argumente în linia de comandă: serverul la care să ne conectăm (presupunând că ne conectăm la serverul din portul 80) și resursa de recuperat., Se creează un Socketcare indică către server și specifică în mod explicit portul 80. Se execută apoi comanda:

    GET PATH HTTP/1.0

    De exemplu:

    GET / HTTP/1.0

    Ce s-a întâmplat?

    atunci Când preluați o pagină web de pe un server web, cum ar fi , HTTP client utilizează servere DNS pentru a găsi adresă de server: se începe prin a cere top-level domain server pentru com de domeniu în cazul în care o autoritate de domeniu-nume server este pentru ., Apoi se cere ca server de nume de domeniu pentru adresa IP (sau adrese) pentru . Apoi, se deschide o priză la acel server de pe portul 80. (Sau, dacă doriți să definiți un port diferit, puteți face acest lucru adăugând două puncte urmate de numărul portului, de exemplu: :8080.) În cele din urmă, HTTP client execută specificat metoda HTTP, cum ar fi GET, POST, PUT, DELETE, HEAD sau OPTI/ONS. Fiecare metodă are propria sintaxă., Așa cum se arată în codul de mai sus snips, GET metodă necesită o cale de urmat de HTTP/version number și o linie goală. Dacă am fi vrut să adăugăm anteturi HTTP, am fi putut face acest lucru înainte de a intra în noua linie.

    În Listă 1, am recuperat un OutputStream și l-au înfășurat într-un PrintStream așa că am putea să execute mai ușor textul nostru pe baza de comenzi., Codul nostru a obținut un InputStream, înfășurat într-un InputStreamReader, care a transformat intr-un Reader, și apoi înfășurat într-un BufferedReader. Am folosit PrintStream să execute nostru GET metoda și apoi folosit BufferedReader pentru a citi răspunsul linie cu linie până când am primit un null răspuns, indicând faptul că soclu au fost închise.,

    Acum, executa această clasă și-l treacă următoarele argumente:

    java com.geekcap.javaworld.simplesocketclient.SimpleSocketClientExample www.javaworld.com /

    ar trebui să vezi ceva asemănător cu ce e mai jos:

    Acest rezultat arată o pagină de test pe JavaWorld-ul. Acesta a răspuns că vorbește HTTP versiunea 1.1 și răspunsul este 200 OK.

    Java socket server example

    am acoperit partea clientului și, din fericire, aspectul de comunicare al serverului este la fel de ușor., Dintr-o perspectivă simplistă, procesul este următorul:

    1. creați un ServerSocket, specificând un port pentru a asculta.
    2. Invoca ServerSocket‘s accept() metoda pentru a asculta pe portul configurat pentru o conexiune client.
    3. atunci Când un client se conectează la server, accept() metoda returnează un Socket prin care serverul poate comunica cu clientul., Aceasta este aceeași Socket clasa pe care am folosit pentru clientul nostru, astfel încât procesul este același: obținerea unui InputStream pentru a citi de la client și un OutputStream scrie la client.
    4. Dacă ai server trebuie să fie scalabilă, veți dori să treacă Socket la un alt fir la proces, astfel că server-ul dvs. poate continua ascultare pentru conexiuni suplimentare.
    5. apelați din nou metoda ServerSocket‘s accept() pentru a asculta o altă conexiune.,după cum veți vedea în curând, manipularea de către NIO a acestui scenariu ar fi puțin diferită., Acum, însă, putem crea direct un ServerSocket prin care trece un port pentru a asculta pe (mai multe despre ServerSocketFactorys în secțiunea următoare):

      ServerSocket serverSocket = new ServerSocket( port );

      Și acum putem accepta conexiuni de intrare prin intermediul accept() metoda:

      Socket socket = serverSocket.accept();// Handle the connection ...

      Multithreaded de programare cu Java sockets

      Listare 2, de mai jos, pune toate server de cod atât de departe împreună într-un ușor mai robust exemplu care folosește fire să se ocupe de mai multe cereri., Serverul afișat este un server echo, ceea ce înseamnă că răsună înapoi orice mesaj pe care îl primește.

      în timp ce exemplul din lista 2 nu este complicat, anticipează o parte din ceea ce urmează în următoarea secțiune pe NIO. Acordați o atenție deosebită cantității de cod de filetare pe care trebuie să le scriem pentru a construi un server care să poată gestiona mai multe solicitări simultane.

      Lista 2. SimpleSocketServer.java

      în Lista 2 creăm o nouă instanță SimpleSocketServer și pornim serverul., Acest lucru este necesar deoarece SimpleSocketServer extends Thread pentru a crea un thread nou să se ocupe de blocare accept() apel pe care le vedeți în read() metoda. Metoda run()se află într-o buclă care acceptă cererile clientului și creează fire RequestHandler pentru a procesa solicitarea. Din nou, acesta este un cod relativ simplu, dar implică și o cantitate corectă de programare filetată.,

      Notă de asemenea că RequestHandler se ocupă de comunicare client de mult ca codul în Listă 1 v: se înfășoară OutputStream cu un PrintStream pentru a facilita scrie și, în mod similar, împachetări InputStream cu un BufferedReader pentru citește ușor. În măsura în care un server merge, se citește linii de la client și ecouri-le înapoi la client. Dacă clientul trimite o linie goală, conversația se termină și RequestHandler Închide soclul.

      NIO și NIO.,2

      pentru multe aplicații, modelul de bază de programare Java socket pe care tocmai l-am explorat este suficient. Pentru aplicațiile care implică intrări/ieșiri mai intense sau intrări/ieșiri asincrone, veți dori să vă familiarizați cu API-urile care nu blochează introduse în Java NIO și NIO.2.

      pachetul JDK 1.4 NIO oferă următoarele caracteristici cheie:

      • canalele sunt concepute pentru a sprijini transferurile în vrac de la un tampon NIO la altul.
      • tampoanele reprezintă un bloc contiguu de memorie interfațat printr-un set simplu de operații.,
      • Non-Blocking Input / Output este un set de clase care expun canalele la surse comune de I/O, cum ar fi fișiere și prize.

      când programați cu NIO, deschideți un canal către destinație și apoi citiți datele într-un tampon de la destinație, scrieți datele într-un tampon și trimiteți-le la destinație., Vom arunca cu capul în configurarea unui soclu și obținerea unui canal pentru scurt timp, dar mai întâi să trecem în revistă procesul de a folosi un tampon:

      1. Scrie date într-un buffer
      2. Apel tampon e flip() metodă să-l pregătească pentru lectură
      3. Citește datele din buffer
      4. Apel tampon e clear() sau compact() metodă să-l pregătească pentru a primi mai multe date

      atunci Când datele sunt scrise în memoria-tampon, tampon știe cantitatea de date scrise în ea., Aceasta susține trei proprietăți, ale căror semnificații diferă în cazul în care buffer-ul este în modul de citire sau modul de a scrie:

      • Poziția: În modul de scriere, poziția inițială este 0 și deține poziția actuală fiind scris în buffer; după ce răsturna un tampon pentru a pune-l în modul de citire, se resetează poziția 0 și deține poziția curentă din buffer fiind citit din,
      • Capacitate: Fix dimensiunea buffer
      • Limită: În modul de scriere, limita definește cât de mult datele pot fi scrise în memoria-tampon; în modul de citire, limita definește cât de mult datele pot fi citite de la tampon.,

      Java i / o demo: server Echo cu NIO.2

      2, care a fost introdus în JDK 7, se extinde Java non-blocking I/O de biblioteci pentru a adăuga suport pentru sistemul de fișiere sarcini, cum ar fi java.nio.file pachet și java.nio.file.Path clasa și expune un nou Sistem de Fișiere API. Având în vedere acest fundal, să scriem un nou server Echo folosind NIO.2 ‘ s AsynchronousServerSocketChannel.

      AsynchronousServerSocketChannel oferă un canal asincron care nu blochează prizele de ascultare orientate spre flux., Pentru a-l folosi, executăm mai întâi metoda statică open() și apoi bind() într-un port specific. Apoi, vom executa metoda accept(), trecând la ea o clasă care implementează interfața CompletionHandler. Cel mai adesea, veți găsi că handler creat ca o clasă interioară anonimă.

      listarea 3 arată codul sursă pentru noul nostru server Echo asincron.

      Lista 3. SimpleSocketServer.,java

      În Listarea 3 vom crea mai întâi o nouă AsynchronousServerSocketChannel si apoi se leaga la portul 5000:

       final AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(5000));

      La acest lucru AsynchronousServerSocketChannel, vom invoca accept() să-l spun pentru a asculta pentru conexiuni, trecerea de la un custom CompletionHandler exemplu. Când invocăm accept(), se întoarce imediat., Rețineți că acest exemplu este diferit de ServerSocket clasa în Listă 1; întrucât accept() metoda blocat până când un client conectat la acesta, AsynchronousServerSocketChannel accept() metoda mânere pentru noi.

      finalizarea handler

      următoarea Noastră responsabilitate este de a crea un CompletionHandler clasa și de a oferi o punere în aplicare a completed() și failed() metode., completed() metoda este apelată atunci când AsynchronousServerSocketChannel primește o conexiune de la un client și include un AsynchronousSocketChannel clientului. completed() metoda primul acceptă conexiunea de la AsynchronousServerSocketChannel și apoi începe comunicarea cu clientul. Primul lucru pe care îl face este să scrie un mesaj „Hello”: construiește un șir, îl convertește într-o matrice de octeți și apoi îl trece la ByteBuffer.wrap() pentru a construi un ByteBuffer., ByteBuffer poate fi trecut apoi AsynchronousSocketChannel‘s write() metoda.

      Pentru a citi de la client, vom crea un nou ByteBuffer prin invocarea acesteia allocate(4096) (care creează un 4K tampon), apoi vom invoca AsynchronousSocketChannel‘s read() metoda. read() returnează un Future<Integer> pe care o putem invoca get() pentru a prelua numărul de octeți citiți din partea clientului., În acest exemplu, vom trece get() un timeout valoare de 20 de secunde: dacă nu vom primi un răspuns în 20 de secunde, apoi get() metoda va arunca un TimeoutException. Regula noastră pentru acest server echo este că dacă observăm 20 de secunde de tăcere atunci încheiem conversația.apoi verificăm poziția tamponului, care va fi locația ultimului octet primit de la client. Dacă clientul trimite o linie goală, atunci primim doi octeți: un retur de transport și un flux de linie., Verificarea asigură că, dacă clientul trimite o linie goală, o luăm ca indicator că clientul a terminat conversația. Dacă avem date semnificative atunci noi numim ByteBuffer‘s flip() metodă să-l pregătească pentru lectură. Vom crea un temporare matrice octet să dețină numărul de octeți citiți din partea clientului și apoi invoca ByteBuffer‘s get() pentru a încărca datele în care matrice octet. În cele din urmă, convertim matricea de octeți într-un șir creând o nouă instanță String., Am ecou linie înapoi la client prin conversia șir pentru a o matrice octet, care trece ca să ByteBuffer.wrap() metoda și invocarea AsynchronousSocketChannel‘s write() metoda. Acum am clear() ByteBuffer, care amintesc înseamnă că se repoziționează position la zero și pune ByteBuffer în modul de scriere, apoi am citit următoarea linie de la client.,singurul lucru de care trebuie să știți este că metoda main(), care creează serverul, stabilește, de asemenea, un cronometru de 60 de secunde pentru a menține aplicația în funcțiune. Deoarece AsynchronousSocketChannel‘s accept() metoda returneaza imediat, dacă nu avem Thread.sleep() atunci aplicația noastră va opri imediat.,

      Pentru a testa acest lucru, lansa server și se conecteze la acesta, folosind un client de telnet:

      telnet localhost 5000

      Trimite câteva sfori la server, observăm că ele sunt ecou înapoi la tine, și apoi trimite-o linie goală pentru a termina conversația.

      în concluzie

      în acest articol am prezentat două abordări pentru programarea socket cu Java: abordarea tradițională introdusă cu Java 1.0 și cea mai nouă, care nu blochează Nio și NIO.2 abordări introduse în Java 1.4 și Java 7, respectiv., Ați văzut mai multe iterații ale unui client Java socket și ale unui exemplu Java socket server, demonstrând atât utilitatea i/o de bază Java, cât și unele scenarii în care i/o care nu blochează îmbunătățește modelul de programare Java socket. Folosind I / O care nu blochează, puteți programa aplicații în rețea Java pentru a gestiona mai multe conexiuni simultane, fără a fi nevoie să gestionați mai multe colecții de fire. De asemenea, puteți profita de noua scalabilitate a serverului care este încorporată în NIO și NIO.2.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *