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 unPrintStream
astfel încât să ne putem ușor a scrie textul cu metode, cum ar fiprintln()
., Pentru un alt exemplu, am putea încheiaInputStream
cu unBufferedReader
, printr-unInputStreamReader
, în scopul de a citi cu ușurință textul cu metode, cum ar fireadLine()
.descarcacodul 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:
- creați o priză pe serverul web care ascultă pe portul 80.
- Obține un
PrintStream
la server si trimite cerereaGET PATH HTTP/1.0
, undePATH
este solicitat de resurse de pe server., De exemplu, dacă am vrut să deschidem rădăcina unui site web, atunci calea ar fi/
. - se Obține un
InputStream
la server, înveliți-l cu unBufferedReader
ș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
Socket
care indică către server și specifică în mod explicit portul80
. 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 fiGET
,POST
,PUT
,DELETE
,HEAD
sauOPTI/ONS
. Fiecare metodă are propria sintaxă., Așa cum se arată în codul de mai sus snips,GET
metodă necesită o cale de urmat deHTTP/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-unPrintStream
așa că am putea să execute mai ușor textul nostru pe baza de comenzi., Codul nostru a obținut unInputStream
, înfășurat într-unInputStreamReader
, care a transformat intr-unReader
, și apoi înfășurat într-unBufferedReader
. Am folositPrintStream
să execute nostruGET
metoda și apoi folositBufferedReader
pentru a citi răspunsul linie cu linie până când am primit unnull
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:
- creați un
ServerSocket
, specificând un port pentru a asculta. - Invoca
ServerSocket
‘saccept()
metoda pentru a asculta pe portul configurat pentru o conexiune client. - atunci Când un client se conectează la server,
accept()
metoda returnează unSocket
prin care serverul poate comunica cu clientul., Aceasta este aceeașiSocket
clasa pe care am folosit pentru clientul nostru, astfel încât procesul este același: obținerea unuiInputStream
pentru a citi de la client și unOutputStream
scrie la client. - 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. - apelați din nou metoda
ServerSocket
‘saccept()
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 unServerSocket
prin care trece un port pentru a asculta pe (mai multe despreServerSocketFactory
s î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 deoareceSimpleSocketServer
extendsThread
pentru a crea un thread nou să se ocupe de blocareaccept()
apel pe care le vedeți înread()
metoda. Metodarun()
se află într-o buclă care acceptă cererile clientului și creează fireRequestHandler
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 unPrintStream
pentru a facilita scrie și, în mod similar, împachetăriInputStream
cu unBufferedReader
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ă șiRequestHandler
Î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:
- Scrie date într-un buffer
- Apel tampon e
flip()
metodă să-l pregătească pentru lectură - Citește datele din buffer
- Apel tampon e
clear()
saucompact()
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 șijava.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 ‘ sAsynchronousServerSocketChannel
.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 apoibind()
într-un port specific. Apoi, vom executa metodaaccept()
, trecând la ea o clasă care implementează interfațaCompletionHandler
. 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 invocaaccept()
să-l spun pentru a asculta pentru conexiuni, trecerea de la un customCompletionHandler
exemplu. Când invocămaccept()
, se întoarce imediat., Rețineți că acest exemplu este diferit deServerSocket
clasa în Listă 1; întrucâtaccept()
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 acompleted()
șifailed()
metode.,completed()
metoda este apelată atunci cândAsynchronousServerSocketChannel
primește o conexiune de la un client și include unAsynchronousSocketChannel
clientului.completed()
metoda primul acceptă conexiunea de laAsynchronousServerSocketChannel
ș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 laByteBuffer.wrap()
pentru a construi unByteBuffer
.,ByteBuffer
poate fi trecut apoiAsynchronousSocketChannel
‘swrite()
metoda.Pentru a citi de la client, vom crea un nou
ByteBuffer
prin invocarea acesteiaallocate(4096)
(care creează un 4K tampon), apoi vom invocaAsynchronousSocketChannel
‘sread()
metoda.read()
returnează unFuture<Integer>
pe care o putem invocaget()
pentru a prelua numărul de octeți citiți din partea clientului., În acest exemplu, vom treceget()
un timeout valoare de 20 de secunde: dacă nu vom primi un răspuns în 20 de secunde, apoiget()
metoda va arunca unTimeoutException
. 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 numimByteBuffer
‘sflip()
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 invocaByteBuffer
‘sget()
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 invocareaAsynchronousSocketChannel
‘swrite()
metoda. Acum amclear()
ByteBuffer
, care amintesc înseamnă că se repoziționeazăposition
la zero și puneByteBuffer
în modul de scriere, apoi am citit următoarea linie de la client.,singurul lucru de care trebuie să știți este că metodamain()
, care creează serverul, stabilește, de asemenea, un cronometru de 60 de secunde pentru a menține aplicația în funcțiune. DeoareceAsynchronousSocketChannel
‘saccept()
metoda returneaza imediat, dacă nu avemThread.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.