ez a bemutató a socket programozás bevezetése Java-ban, kezdve egy egyszerű kliens-szerver példával, amely bemutatja a Java I/O alapvető jellemzőit.bevezetésre kerül mind az eredeti
java.io
csomag és NIO, A nem blokkoló I/O (java.nio
) API-k Java 1.4-ben. Végül megjelenik egy példa, amely bemutatja a Java hálózati implementált Java 7 forward, a NIO.2.,
Socket programozás csapódik le, hogy két rendszer kommunikál egymással. Általában a hálózati kommunikáció kétféleképpen jön létre: Transport Control Protocol (TCP) és User Datagram Protocol (UDP). A TCP-t és az UDP-t különböző célokra használják, és mindkettőnek egyedi korlátai vannak:
- a TCP viszonylag egyszerű és megbízható protokoll, amely lehetővé teszi az ügyfél számára, hogy kapcsolatot létesítsen egy szerverrel, és a két rendszer kommunikálni tudjon. A TCP-ben minden entitás tudja, hogy kommunikációs terhelés érkezett.,
- az UDP egy kapcsolat nélküli protokoll-jó forgatókönyvek, ahol nem feltétlenül kell minden csomag megérkezik a célállomásra, mint a médiaadatfolyam-továbbítás.
a TCP és az UDP közötti különbség értékeléséhez fontolja meg, mi történne, ha a videót a kedvenc webhelyéről streamelné, és a kereteket eldobná. Szeretné, ha az ügyfél lelassítaná a filmet, hogy megkapja a hiányzó kereteket, vagy inkább azt szeretné, hogy a videó továbbra is lejátszódjon? A Video streaming protokollok általában az UDP-t használják., Mivel a TCP garantálja a kézbesítést, ez a HTTP, FTP, SMTP, POP3 stb.
ebben a bemutatóban bemutatom a socket programozást Java-ban. Bemutatok egy sor kliens-szerver példát, amelyek bemutatják az eredeti Java I/O keretrendszer funkcióit, majd fokozatosan továbblépnek a NIO-ban bevezetett funkciók használatához.2.
Old-school Java sockets
a NIO előtti implementációkban a Java TCP kliens socket kódot a java.net.Socket
osztály kezeli., A következő kód megnyit egy kapcsolatot egy szerverrel:
Socket socket = new Socket( server, port );
amint socket
példányunk csatlakozik a szerverhez, elkezdhetjük a bemeneti és kimeneti adatfolyamok beszerzését a sever-hez. A bemeneti adatfolyamok a kiszolgálóról származó adatok olvasására szolgálnak, míg a kimeneti adatfolyamok az adatok kiszolgálóra történő írására szolgálnak., Tudjuk végrehajtani, hogy a következő módszerek megszerzése bemeneti, illetve kimeneti stream:
InputStream in = socket.getInputStream();OutputStream out = socket.getOutputStream();
Mert ezek a hétköznapi patakok, ugyanaz a patakok, hogy mi lenne használni, hogy olvasni, írni, hogy egy fájlt, tudjuk átalakítani, hogy azt a formáját, amely a legjobban szolgálja a használati eset. Például a OutputStream
– t egy PrintStream
– val becsomagolhatjuk, hogy könnyen írhatunk szöveget olyan módszerekkel, mint például a println()
., Egy másik példában a InputStream
BufferedReader
InputStreamReader
segítségével a InputStreamReader
szöveg könnyen olvasható olyan módszerekkel, mint a readLine()
.
Java Socket Client example
dolgozzunk egy rövid példán keresztül, amely HTTP GET-t hajt végre egy HTTP szerver ellen., A HTTP sokkal kifinomultabb, mint a mi példánk lehetővé teszi, de a legegyszerűbb eset kezelésére írhatunk kliens kódot: kérhetünk egy erőforrást a szerverről, a szerver pedig visszaadja a választ, majd bezárja a streamet. Ez az eset a következő lépéseket igényli:
- hozzon létre egy aljzatot a 80-as porton hallgatott webszerverhez.
- szerezzen be egy
PrintStream
– t a szerverre, és küldje el aGET PATH HTTP/1.0
kérést, aholPATH
a kért erőforrás a kiszolgálón., Például, ha meg akarjuk nyitni egy webhely gyökerét, akkor az útvonal/
lenne. - szerezzen be egy
InputStream
-t a szerverhez, csomagolja be egyBufferedReader
-val, majd olvassa el a válaszvonalat soronként.
az 1. lista a példa forráskódját mutatja.
lista 1. SimpleSocketClientExample.a java
Listing 1 két parancssori argumentumot fogad el: a kapcsolódni kívánt kiszolgálót (feltételezve, hogy a 80-as porton csatlakozunk a szerverhez), valamint a lekérendő erőforrást., Létrehoz egy Socket
– t, amely a kiszolgálóra mutat, és kifejezetten meghatározza a 80
portot. Ezután végrehajtja a következő parancsot:
GET PATH HTTP/1.0
például:
GET / HTTP/1.0
mi történt?
amikor egy webkiszolgálóról lekér egy weboldalt, mint például a , A HTTP kliens DNS-kiszolgálókat használ a kiszolgáló címének megkereséséhez: a
com
domain, ahol a hiteles domain-név szerver a ., Ezután azt kéri, hogy a domain-név szerver az IP-cím (vagy címek) a
. Ezután megnyit egy aljzatot a 80-as porton lévő kiszolgálóhoz. (Vagy ha egy másik portot szeretne meghatározni, akkor ezt megteheti egy kettőspont hozzáadásával, amelyet a portszám követ, például:
:8080
.) Végül a HTTP kliens végrehajtja a megadott HTTP módszert, mint például a GET
, POST
, PUT
, DELETE
, HEAD
, vagy OPTI/ONS
. Minden módszernek saját szintaxisa van., Amint az a fenti kód snips, a GET
metódus igényel utat, majd HTTP/version number
és egy üres sort. Ha HTTP fejléceket akartunk hozzáadni, akkor ezt megtehettük volna, mielőtt belépnénk az új sorba.
A Tőzsdei 1, visszaszereztük egy OutputStream
, majd becsomagolta egy PrintStream
, így mi is könnyebben végre a szöveges parancsok., A kód kapott egy InputStream
, becsomagolt, hogy egy InputStreamReader
, ami átváltott egy Reader
, majd becsomagolva, hogy a BufferedReader
. APrintStream
-ot használtukGET
módszerünk végrehajtásához, majd aBufferedReader
-ot használtuk a Válaszvonal olvasásához, amíg megkaptuk anull
választ, jelezve, hogy az aljzat zárva volt.,
most hajtsa végre ezt az osztályt, és adja át a következő érveket:
java com.geekcap.javaworld.simplesocketclient.SimpleSocketClientExample www.javaworld.com /
az alábbiakhoz hasonló kimenetet kell látnia:
Ez a kimenet egy tesztoldalt mutat a JavaWorld webhelyén. Azt válaszolta vissza, hogy beszél HTTP Verzió 1.1 és a válasz 200 OK
.
Java socket server example
lefedtük a kliens oldalt, szerencsére a szerver oldal kommunikációs aspektusa ugyanolyan egyszerű., Leegyszerűsítve, a folyamat a következő:
- hozzon létre egy
ServerSocket
, megadva a port hallgatni. - hívja fel a
ServerSocket
‘saccept()
módszert, hogy meghallgassa a konfigurált portot egy ügyfélkapcsolathoz. - amikor egy ügyfél csatlakozik a szerverhez, a
accept()
metódus egySocket
metódust ad vissza, amelyen keresztül a szerver képes kommunikálni az ügyféllel., Ez ugyanaz aSocket
class hogy használják a kliens, így a folyamat ugyanaz: szerezze be egyInputStream
hogy olvassa el a kliens, valamint egyOutputStream
írj az ügyfél. - ha a kiszolgálónak skálázhatónak kell lennie, akkor át kell adnia a
Socket
fájlt egy másik feldolgozandó szálra, hogy a kiszolgáló továbbra is hallgathasson további kapcsolatokat. - hívja a
ServerSocket
‘saccept()
módszer ismét hallgatni egy másik kapcsolat.,
Amint hamarosan látni fogja, a NIO kezelése ebben a forgatókönyvben kissé más lenne., Most azonban, hogy mi közvetlenül létrehozhat egy ServerSocket
átadásával egy port, hogy figyelj a (többet ServerSocketFactory
s a következő fejezetet):
ServerSocket serverSocket = new ServerSocket( port );
most el tudjuk fogadni a bejövő kapcsolatok keresztül a accept()
módszer:
Socket socket = serverSocket.accept();// Handle the connection ...
Többszálú programozás Java aljzatok
Tőzsdei 2, az alábbiakban teszi a szerver kódot eddig össze egy kissé erőteljesebb példa, hogy használja a szálak, hogy kezelni több kéri., A megjelenített szerver egy echo szerver, ami azt jelenti, hogy visszhangozza az általa fogadott üzeneteket.
míg a példa a Listing 2 nem bonyolult, ez nem előre néhány, hogy mi jön a következő részben NIO. Különös figyelmet kell fordítani a menetkész kód mennyiségére, amelyet meg kell írnunk annak érdekében, hogy olyan kiszolgálót építsünk, amely több egyidejű kérést képes kezelni.
lista 2. SimpleSocketServer.java
a 2. listában létrehozunk egy új SimpleSocketServer
példányt, majd elindítjuk a kiszolgálót., Erre azért van szükség, mert a SimpleSocketServer
kiterjeszti Thread
egy új szál létrehozásához a blokkolás kezeléséhez accept()
hívja, amelyet a read()
módszerben lát. Arun()
metódus az ügyfélkéréseket elfogadó hurokban helyezkedik el, és a kérés feldolgozásáhozRequestHandler
szálakat hoz létre. Ismét ez viszonylag egyszerű kód, de magában foglalja a tisztességes mennyiségű menetes programozás.,
Megjegyzés is, hogy a RequestHandler
kezeli a kliens kommunikáció ugyanúgy, mint a kód a Tőzsdei 1: befejeződik a OutputStream
egy PrintStream
megkönnyítése könnyű írja meg, hasonlóképpen, pakolások a InputStream
egy BufferedReader
a könnyű olvas. Ami a kiszolgálót illeti, a kliensből vonalakat olvas, és visszhang a klienshez. Ha az ügyfél üres sort küld, akkor a beszélgetés véget ér, a RequestHandler
bezárja az aljzatot.
NIO és NIO.,2
sok alkalmazáshoz elegendő az általunk feltárt alap Java socket programozási modell. Az intenzívebb I / O vagy aszinkron bemenetet / kimenetet magában foglaló alkalmazásokhoz ismernie kell a Java NIO-ban és NIO-ban bevezetett nem blokkoló API-kat.2.
a JDK 1.4 NIO csomag a következő főbb funkciókat kínálja:
- a csatornákat úgy tervezték, hogy támogassák az egyik NIO pufferről a másikra történő tömeges átutalásokat.
- a pufferek egy összefüggő memóriablokkot képviselnek, amelyet egy egyszerű műveletkészlet köt össze.,
- a nem blokkoló bemenet / kimenet olyan osztályok halmaza, amelyek a csatornákat közös I/O forrásoknak, például fájloknak vagy aljzatoknak teszik ki.
NIO-val történő programozáskor megnyit egy csatornát a célállomásra, majd az adatokat a célállomás pufferébe olvassa, írja az adatokat egy pufferbe, majd küldje el azt a rendeltetési helyre., Majd belevetik magukat beállítása egy socket megszerzése a csatorna, hogy hamarosan, de először nézzük át a folyamat, amelynek során egy puffer:
- adatokat Írni a buffer
- Hívás a puffer
flip()
módszer, hogy készítse el a olvasást - Olvassa el az adatokat a buffer
- Hívás a puffer
clear()
vagycompact()
a módszer előkészítése úgy, hogy többet kapnak, adatok
Ha az van írva a bufferbe, a puffer tudja, hogy a mennyiségű adatot írtak bele., Fenntartja három tulajdonság, amelynek jelentése különböznek, ha a puffer olvasási mód, vagy írjon mód:
- Pozíció: A write mode, a kezdeti pozíció 0 tartja a jelenlegi helyzetben, hogy írt, hogy a puffer; után feldobunk egy puffer, hogy tegye olvasható módban van, akkor visszaállítja a helyzetben, hogy 0 tartja a jelenlegi helyzetben a puffer olvas,
- Kapacitás: A rögzített méret a buffer
- Limit: Az írási mód, a limit határozza meg, hogy mennyi adatot lehet írni a puffer; olvasási mód, a limit határozza meg, hogy mennyi adatot lehet olvasni a pufferből.,
Java I / O demo: Echo server with NIO.2
NIO.Az 2, amelyet a JDK 7-ben vezettek be, kiterjeszti a Java nem blokkoló I / O könyvtárait, hogy támogatást nyújtson a fájlrendszer feladatokhoz, mint például a java.nio.file
csomag és a java.nio.file.Path
osztály, és kiteszi az új fájlrendszer API-t. Ezt a hátteret szem előtt tartva írjunk egy új Echo szervert a NIO használatával.2 ‘s AsynchronousServerSocketChannel
.
a AsynchronousServerSocketChannel
nem blokkoló aszinkron csatornát biztosít a stream-orientált hallgatási aljzatokhoz., Annak érdekében, hogy használni, először végre a statikus open()
módszer, majd bind()
azt egy adott port. Ezután végrehajtjuk a accept()
metódust, átadva neki egy osztályt, amely végrehajtja a CompletionHandler
interfészt. Leggyakrabban, megtalálja azt a kezelőt, amelyet névtelen belső osztályként hoztak létre.
a 3. lista az új aszinkron Echo szerver forráskódját mutatja.
lista 3. SimpleSocketServer.,java
a 3.felsorolásban először létrehozunk egy új AsynchronousServerSocketChannel
majd kötjük az 5000-es porthoz:
final AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(5000));
ebből a AsynchronousServerSocketChannel
accept()
mondani, hogy elkezd hallgatni a kapcsolatokat, átadva neki egy egyéni CompletionHandler
példány. Amikor accept()
hivatkozunk, azonnal visszatér., Vegye figyelembe, hogy ez a példa eltér a ServerSocket
osztálytól az 1.listában; mivel a módszer blokkolva van, amíg egy ügyfél csatlakozik hozzá, a AsynchronousServerSocketChannel
accept()
metódus kezeli számunkra.
a befejezési kezelő
a következő feladatunk egy CompletionHandler
osztály létrehozása, valamint a completed()
és failed()
módszerek megvalósítása., Acompleted()
metódus akkor kerül meghívásra, amikor aAsynchronousServerSocketChannel
kapcsolatot kap egy ügyféltől, és tartalmaz egyAsynchronousSocketChannel
– t az ügyfélnek. Acompleted()
módszer először elfogadja a kapcsolatot aAsynchronousServerSocketChannel
, majd elkezdi kommunikálni az ügyféllel. Az első dolog, amit csinál, egy “Hello” üzenet írása: egy karakterláncot épít, átalakítja egy bájt tömbbe, majd átadja a ByteBuffer.wrap()
– nak egy ByteBuffer
létrehozásához., A ByteBuffer
ezután átadható AsynchronousSocketChannel
‘s write()
metódus.
az ügyféltől való olvasáshoz létrehozunk egy új ByteBuffer
– t a allocate(4096)
(amely 4k puffert hoz létre) hivatkozással, majd a AsynchronousSocketChannel
‘s read()
módszer. Aread()
visszaadja aFuture<Integer>
értéket, amelyre hivatkozhatunkget()
a kliensből olvasott bájtok számának lekéréséhez., Ebben a példában átadjuk a get()
20 másodperces időtúllépési értéket: ha 20 másodperc alatt nem kapunk választ, akkor a get()
metódus TimeoutException
. Az echo szerverre vonatkozó szabályunk az, hogy ha 20 másodperc csendet figyelünk meg, akkor befejezzük a beszélgetést.
ezután ellenőrizzük a puffer helyzetét, amely az ügyféltől kapott utolsó bájt helye lesz. Ha az ügyfél üres sort küld, akkor két bájtot kapunk: egy kocsi visszatérését és egy sorjelzést., Az ellenőrzés biztosítja, hogy ha az ügyfél egy üres sort küld, akkor azt jelzi, hogy az ügyfél befejezte a beszélgetést. Ha értelmes adataink vannak, akkor aByteBuffer
‘sflip()
módszert hívjuk az olvasáshoz. Létrehozunk egy ideiglenes byte tömböt, hogy megtartsuk az ügyfél által olvasott bájtok számát, majd meghívjuk a ByteBuffer
‘s get()
az adatok betöltéséhez a byte tömbbe. Végül konvertáljuk a bájt tömböt karakterláncra egy új String
példány létrehozásával., Visszhang a sor vissza a kliens konvertálásával a karakterlánc egy byte tömb, átadva, hogy a ByteBuffer.wrap()
módszer, és hivatkozva a AsynchronousSocketChannel
‘s write()
módszer. Most clear()
the ByteBuffer
, ami azt jelenti, hogy a position
nullára állítja a ByteBuffer
írási módba, majd a következő sort az ügyféltől olvasjuk.,
az egyetlen dolog, amit tudnia kell, hogy amain()
módszer, amely létrehozza a szervert, 60 másodperces időzítőt is beállíthat az alkalmazás futtatásához. Mivel a AsynchronousSocketChannel
‘s accept()
metódus azonnal visszatér, ha nincs meg a Thread.sleep()
akkor alkalmazásunk azonnal leáll.,
ennek teszteléséhez indítsa el a szervert, majd csatlakozzon hozzá egy telnet kliens segítségével:
telnet localhost 5000
küldjön néhány karakterláncot a szerverre, vegye figyelembe, hogy visszatükrözik Önt, majd küldjön egy üres sort a beszélgetés befejezéséhez.
összefoglalva
ebben a cikkben már bemutatott két megközelítés socket programozás Java: a hagyományos megközelítés bevezetett Java 1.0 és az újabb, nem blokkoló NIO és NIO.2 megközelítés a Java 1.4-ben, illetve a Java 7-ben., A Java Socket kliens több iterációját és egy Java socket server példáját láthatta, bemutatva mind az alapvető Java I / O segédprogramot, mind néhány olyan forgatókönyvet, ahol a nem blokkoló I/O javítja a Java socket programozási modellt. A nem blokkoló I / O használatával programozhatja a Java hálózati alkalmazásokat több egyidejű kapcsolat kezelésére anélkül, hogy több szálgyűjteményt kellene kezelnie. Kihasználhatja a NIO-ba és NIO-ba beépített új szerver skálázhatóságát is.2.