Denne opplæringen er en introduksjon til socket programmering i Java, og starter med en enkel klient-server eksempel som viser de grunnleggende funksjonene i Java I/O. vil Du bli introdusert til både den opprinnelige
java.io
pakken og NIO, ikke-blokkering I/O (java.nio
) Api-er innført i Java 1.4. Til slutt, vil du se et eksempel som demonstrerer Java-nettverk som ble implementert fra Java 7 fremover, i NIO.2.,
Socket programmering koker ned til to systemer som kommuniserer med hverandre. Generelt, nettverk kommunikasjon kommer i to smaker: Transport Control Protocol (TCP) og UDP (User Datagram Protocol). TCP og UDP brukes til ulike formål, og begge har unike begrensninger:
- TCP er relativt enkel og pålitelig protokoll som gjør at en klient for å koble til en server og to systemer for å kommunisere. I TCP, hver enhet vet at kommunikasjon av nyttelast som har blitt mottatt.,
- UDP er en tilkoblingsfri-protokollen, og er bra for scenarier der du ikke nødvendigvis trenger hver pakken ankommer sin destinasjon, for eksempel direkteavspilling av medier.
for Å sette pris på forskjellen mellom TCP og UDP, vurdere hva som ville skje hvis du var å streame video fra din favoritt nettsted, og det falt rammer. Ville du foretrekke at klienten bremse ned filmen din for å motta mangler bilder eller ville du foretrekke at videoen fortsette å spille? Video streaming protokoller vanligvis utnytte UDP., Fordi TCP garanterer levering, det er protokollen av valget for HTTP, FTP, SMTP, POP3, og så videre.
I denne opplæringen, vil jeg introdusere deg til å socket programmering i Java. Jeg presenterer en serie av klient-server-eksempler som viser funksjonene fra den opprinnelige Java I/O-rammen, og deretter gradvis forhånd å bruke funksjoner introdusert i NIO.2.
Old-school Java sockets
I implementeringer før NIO, Java TCP-klient socket koden er håndtert av java.net.Socket
klasse., Følgende kode åpner en tilkobling til en server:
Socket socket = new Socket( server, port );
Når vår socket
eksempel er koblet til serveren, kan vi begynne å få input og output strømmer til sever. Input-strømmer brukes til å lese data fra serveren mens output strømmer brukes til å skrive data til serveren., Vi kan utføre følgende metoder for å få input og output strømmer:
InputStream in = socket.getInputStream();OutputStream out = socket.getOutputStream();
Fordi disse er vanlige bekker, den samme bekker som vi vil bruke for å lese fra og skrive til en fil, kan vi konvertere dem til den form som fungerer best i vår bruk tilfelle. For eksempel, vi kan bryte OutputStream
med PrintStream
slik at vi kan enkelt skrive inn tekst med metoder som println()
., For et annet eksempel, vi kan bryte InputStream
med BufferedReader
, via en InputStreamReader
, for å enkelt lese tekst med metoder som readLine()
.
Java socket klient eksempel
La oss jobbe gjennom et kort eksempel som utfører en HTTP GET mot en HTTP-server., HTTP er mer sofistikert enn vårt eksempel tillater det, men vi kan skrive klient kode for å håndtere den enkleste sak: forespørsel om en ressurs fra server og server returnerer svar, og stenger strømmen. Denne saken krever følgende fremgangsmåte:
- Opprette en kontakt til web-serveren lytter på port 80.
- Få tak i en
PrintStream
til serveren og sende forespørselenGET PATH HTTP/1.0
, derPATH
er den forespurte ressursen på serveren., For eksempel, hvis vi ønsket å åpne roten av et nettsted, så banen ville være/
. - Få tak i en
InputStream
til serveren, må du pakke den med enBufferedReader
og lese svaret line-by-line.
Liste 1 viser kildekoden til dette eksemplet.
Listing 1. SimpleSocketClientExample.java
Liste 1 aksepterer to kommandolinje-argumenter: – server til å koble til (forutsatt at vi forsøker å koble til serveren på port 80) og den ressurs for å hente., Det skaper en Socket
som peker til serveren og eksplisitt angir port 80
. Deretter kjører kommandoen:
GET PATH HTTP/1.0
For eksempel slik:
GET / HTTP/1.0
Hva skjedde?
Når du hente en webside fra en web-server, for eksempel HTTP-klient bruker DNS-servere for å finne server adresse: den starter med å spørre top-level domain server for
com
domene der autoritativt domene-navnet på serveren for ., Så det bes om at domene-navn-server for IP-adressen (eller adresser) for
. Neste, det åpner et uttak til at serveren på port 80. (Eller, hvis du ønsker å angi en annen port, kan du gjøre det ved å legge til et kolon etterfulgt av port nummer, for eksempel:
:8080
.) Endelig HTTP klienten utfører den angitte HTTP metode, for eksempel GET
, POST
, PUT
, DELETE
, HEAD
, eller OPTI/ONS
. Hver metode har sin egen syntaks., Som vist i koden ovenfor klipp, GET
metoden krever en bane etterfulgt av HTTP/version number
og en tom linje. Hvis vi ønsket å legge til HTTP-hoder vi kunne ha gjort det før du går inn den nye linjen.
I Liste 1, vi hentet en OutputStream
og svøpte det i en PrintStream
slik at vi kan lettere gjennomføre våre tekstbaserte kommandoer., Våre etiske fått en InputStream
, innpakket som i en InputStreamReader
, som konverteres den til en Reader
, og deretter pakket som i en BufferedReader
. Vi brukte PrintStream
til å gjennomføre våre GET
metode og deretter brukt BufferedReader
for å lese svaret linje-for-linje-inntil vi fikk en null
svar, noe som indikerer at kontakten hadde vært lukket.,
Nå utføre denne klassen, og gi det følgende argumenter:
java com.geekcap.javaworld.simplesocketclient.SimpleSocketClientExample www.javaworld.com /
Du bør se output lik det som er nedenfor:
Denne utgangen viser en test side på JavaWorld hjemmeside. Det svarte tilbake at det taler for HTTP, versjon 1.1 og svaret er 200 OK
.
Java socket server eksempel
Vi har dekket klientsiden og heldigvis kommunikasjon aspekt av server-side er det like enkelt., Fra et enkelt perspektiv, prosessen er som følger:
- Lage en
ServerSocket
, angi en port for å lytte på. - Påberope
ServerSocket
saccept()
metode for å høre på den valgte porten for tilkobling til klienten. - Når en klient kobler seg til serveren,
accept()
metoden returnerer enSocket
gjennom som serveren kan kommunisere med klienten., Dette er det sammeSocket
klasse som vi brukte for vår kunde, så prosessen er den samme: få tak i enInputStream
for å lese fra klienten og enOutputStream
skriv til klienten. - Hvis du server behov for å være skalerbar, vil du ønsker å passere
Socket
til en annen tråd til prosessen, slik at serveren kan fortsette å lytte etter flere tilkoblinger. - Anrop
ServerSocket
saccept()
metode igjen for å lytte til en annen tilkobling.,
Som du vil snart se, NIO s håndtering av dette scenariet ville være litt annerledes., For nå, skjønt, kan vi direkte opprette en ServerSocket
ved å føre det en port til å høre på (mer om ServerSocketFactory
s i neste avsnitt):
ServerSocket serverSocket = new ServerSocket( port );
Og nå kan vi godta innkommende tilkoblinger via accept()
metode:
Socket socket = serverSocket.accept();// Handle the connection ...
Multithreaded med Java sockets
Liste 2, nedenfor, setter alle server-koden så langt sammen til en litt mer robust eksempel som bruker tråder til å håndtere flere forespørsler., Serveren vises et ekko server, noe som betyr at den lyder som et ekko tilbake meldinger det mottar.
Mens eksempel på Liste 2 er ikke komplisert det ikke forutse noe av det som kommer opp i neste avsnitt på NIO. Betale spesiell oppmerksomhet til mengden av threading-koden vi har å skrive for å bygge en server som du kan håndtere flere samtidige forespørsler.
Liste 2. SimpleSocketServer.java
I Liste 2 vi opprette en ny SimpleSocketServer
eksempel og start serveren., Dette er nødvendig fordi SimpleSocketServer
strekker Thread
for å opprette en ny tråd for å håndtere blokkere accept()
anrop som du ser i read()
metode. run()
metode sitter i en loop aksepterer klienten ber om og skape RequestHandler
tråder for å behandle forespørselen. Igjen, dette er relativt enkel kode, men også innebærer en god del av gjenget programmering.,
Merk også at RequestHandler
håndterer klient kommunikasjon mye som kode i Liste 1 gjorde: det wraps OutputStream
med PrintStream
for å forenkle skriver og, på samme måte, wraps InputStream
med BufferedReader
for enkelt leser. Så langt som en server går, leser linjer fra klienten, og gjenspeiler dem tilbake til klienten. Hvis klienten sender en tom linje og deretter samtalen er over, og RequestHandler
lukker kontakten.
NIO og NIO.,2
For mange programmer, base Java socket programmering modell som vi nettopp har utforsket er tilstrekkelig. For søknader som involverer mer intensiv I/O eller asynkron input/output vil du ønsker å bli kjent med ikke-blokkerende Api-er innført i Java NIO og NIO.2.
JDK 1.4 NIO pakken har følgende viktige funksjoner:
- – Tv er utformet for å støtte bulk overføringer fra en NIO buffer til en annen.
- Buffere representerer en sammenhengende blokk med minne grensesnitt med et enkelt sett av operasjoner.,
- Ikke-Blokkerende Input/Output er et sett med klasser som utsettes tv til felles I/O kilder som filer og stikkontakter.
Når du programmerer med NIO, kan du åpne en kanal til din destinasjon og deretter lese data i en buffer mot målet, skrive data til en buffer, og send det til din destinasjon., Vi vil dykke til å sette opp en kontakt og få en kanal til det kort tid, men først la oss gå gjennom prosessen med å bruke en buffer:
– >
- Skriv inn data i en buffer
- Anrop bufferens
flip()
metode for å forberede den for å lese - Les data fra buffer
- Anrop bufferens
clear()
ellercompact()
metoden for å gjøre den klar til å motta mer data
Når data skrives inn i bufferen, den buffer vet mengden av data som skrives inn i det., Det mener tre egenskaper, som har forskjellige betydninger hvis bufferen er i lese-modus eller skrive-modus:
- Posisjon: I skrive modus, vil den første posisjonen er 0, og det holder den gjeldende stilling som blir skrevet i buffer; etter at du snu en buffer for å sette det i lese-modus, tilbakestilles stilling til 0 og holder den nåværende posisjon i buffer leses fra,
- Kapasitet: Den tillatte størrelsen på mellomlageret
- Limit: I skrive modus, grensen angir hvor mye data kan skrives inn i buffer; i lese-modus, grensen angir hvor mye data kan leses fra bufferen.,
Java I/O demo: Echo server med NIO.2
NIO.2, som ble innført i JDK 7, strekker seg i Java er ikke-blokkerende i/O-biblioteker for å legge til støtte for filsystem oppgaver, for eksempel java.nio.file
pakke java.nio.file.Path
klasse og avslører en ny Fil Systemet API. Med bakgrunn i tankene, la oss skrive en ny Echo Server ved hjelp av NIO.2 AsynchronousServerSocketChannel
.
AsynchronousServerSocketChannel
gir en ikke-blokkerende asynkron kanal for strøm-orientert lytte stikkontakter., For å bruke det, må vi først utføre statisk open()
metode og deretter bind()
det til en bestemt port. Neste, vi vil gjennomføre sin accept()
metode, går det en klasse som implementerer CompletionHandler
grensesnitt. Som oftest vil du finne at behandleren er opprettet som en anonym indre klasse.
Liste 3 viser kildekoden til vår nye asynkrone Echo Server.
Liste 3. SimpleSocketServer.,java
I Liste 3 vi først opprette en ny AsynchronousServerSocketChannel
og deretter binde den til port 5000:
final AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(5000));
Fra denne AsynchronousServerSocketChannel
vi påberope accept()
for å fortelle det å lytte for tilkoblinger, pasning til det en tilpasset CompletionHandler
eksempel. Når vi påberope accept()
, går den tilbake umiddelbart., Vær oppmerksom på at dette eksemplet er forskjellig fra ServerSocket
klasse i Liste 1, mens accept()
metode blokkert til en klient som er koblet til den, AsynchronousServerSocketChannel
accept()
metoden håndterer det for oss.
ferdigstillelse behandler
Vår neste ansvar er å lage en CompletionHandler
klassen og gi en implementering av completed()
og failed()
metoder., completed()
metoden kalles da AsynchronousServerSocketChannel
mottar en tilkobling fra en klient og den inneholder en AsynchronousSocketChannel
til klienten. completed()
metoden først godtar du tilkoblingen fra AsynchronousServerSocketChannel
og deretter begynner å kommunisere med klienten. Det første den gjør er å skrive ut et «Hei» – melding: Det bygger seg opp en streng, konverterer den til en byte array, og deretter går det til ByteBuffer.wrap()
for å lage en ByteBuffer
., ByteBuffer
kan da være bestått AsynchronousSocketChannel
s write()
metode.
for Å lese fra klienten, oppretter vi en ny ByteBuffer
ved å ta i bruk sin allocate(4096)
(noe som skaper en 4K-buffer), så vil vi påkalle AsynchronousSocketChannel
s read()
metode. read()
returnerer en Future<Integer>
som vi kan påberope get()
for å hente antall byte som skal leses fra klienten., I dette eksemplet, vi passerer get()
en timeout verdi av 20 sekunder: hvis vi ikke får svar i 20 sekunder get()
metoden vil kaste en TimeoutException
. Vår regel for dette echo server er at hvis vi observerer 20 sekunder med stillhet da vi avslutte samtalen.
Neste vi sjekke posisjonen til buffer, som vil være plasseringen av de siste byte som er mottatt fra kunden. Hvis klienten sender en tom linje så vi får to byte: en vognretur og et linjeskift., Sjekk sikrer at hvis klienten sender en blank linje på at vi tar det som en indikator på at klienten er ferdig med samtalen. Hvis vi skal ha et meningsfylt data da vi ringer ByteBuffer
s flip()
metode for å forberede den for å lese. Vi opprette en midlertidig byte array for å holde antall byte som skal leses fra klienten, og deretter bruke den ByteBuffer
s get()
for å laste data inn i byte array. Til slutt, vi konverterer byte array til en streng ved å opprette en ny String
eksempel., Vi ekko linje tilbake til klienten ved å konvertere streng til en byte array, passerer som til ByteBuffer.wrap()
metode og påkalle AsynchronousSocketChannel
s write()
metode. Nå er vi clear()
ByteBuffer
, som recall betyr at det å reposisjonere position
null, og setter ByteBuffer
i skrive modus, og deretter leser vi den neste linjen fra klienten.,
Den eneste ting å være klar over, er at main()
metode, noe som skaper server, også setter opp en 60 sekunders selvutløser for å holde programmet kjører. Fordi AsynchronousSocketChannel
s accept()
metoden returnerer umiddelbart, hvis vi ikke har den Thread.sleep()
da vår søknad vil stoppe umiddelbart.,
for Å teste dette ut, kan du starte server og koble til ved hjelp av en telnet-klient:
telnet localhost 5000
Send noen strenger til serveren, kan du se at de er tatt med tilbake til deg, og deretter sende en tom linje for å avslutte samtalen.
I konklusjonen
I denne artikkelen har jeg presentert to tilnærminger til å socket programmering med Java: den tradisjonelle tilnærmingen som ble innført med Java 1.0 og nyere, ikke-blokkerende NIO og NIO.2 tilnærminger innført i Java 1.4 og Java 7, henholdsvis., Du har sett flere versjoner av en Java socket klient og en Java socket server eksempel, som viser både bruk av grunnleggende Java-I/O og noen scenarioer der ikke-blokkerende I/O forbedrer Java socket programmering modell. Ved hjelp av ikke-blokkerende I/O, du kan programmere Java nettverk applikasjoner for å håndtere flere samtidige tilkoblinger uten å måtte administrere flere tråden samlinger. Du kan også dra nytte av den nye serveren skalerbarhet som er innebygd i NIO og NIO.2.