Socket programozás Java-ban: a tutorial

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 InputStreamReaderszöveg könnyen olvasható olyan módszerekkel, mint a readLine().

letöltés

forráskód ” Socket programozás Java: a tutorial.”Készítette Steven Haines a JavaWorld számára.

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:

  1. hozzon létre egy aljzatot a 80-as porton hallgatott webszerverhez.
  2. szerezzen be egyPrintStream – 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.
  3. szerezzen be egy InputStream-t a szerverhez, csomagolja be egy BufferedReader-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 80portot. 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ő:

  1. hozzon létre egy ServerSocket, megadva a port hallgatni.
  2. hívja fel aServerSocket‘saccept() módszert, hogy meghallgassa a konfigurált portot egy ügyfélkapcsolathoz.
  3. amikor egy ügyfél csatlakozik a szerverhez, aaccept() metódus egySocket metódust ad vissza, amelyen keresztül a szerver képes kommunikálni az ügyféllel., Ez ugyanaz a Socket class hogy használják a kliens, így a folyamat ugyanaz: szerezze be egy InputStream hogy olvassa el a kliens, valamint egy OutputStream írj az ügyfél.
  4. 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.
  5. hívja aServerSocket‘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 ServerSocketFactorys 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:

  1. adatokat Írni a buffer
  2. Hívás a puffer flip() módszer, hogy készítse el a olvasást
  3. Olvassa el az adatokat a buffer
  4. Hívás a puffer clear() vagy compact() 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 ByteBufferlé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 positionnullá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.

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük