Tento kurz je úvodem do zásuvky programování v Java, počínaje s jednoduchým klient-server příklad demonstrující základní funkce Java I/O. Budete být zavedeny do původní
java.io
balíček a NIO, non-blocking I/O (java.nio
) Api zavedena v Java 1.4. Nakonec uvidíte příklad, který demonstruje Java networking, jak je implementován z Java 7 forward, v NIO.2.,
programování zásuvek se scvrkává na dva systémy, které spolu komunikují. Obecně platí, že síťová komunikace přichází ve dvou příchutích: Transport Control Protocol (TCP) a User Datagram Protocol (UDP). TCP a UDP jsou použity pro různé účely, a oba mají jedinečné omezení:
- TCP je relativně jednoduchý a spolehlivý protokol, který umožňuje klienta pro připojení k serveru a dva systémy pro komunikaci. V TCP každý subjekt ví, že jeho komunikační zatížení bylo přijato.,
- UDP je protokol bez připojení a je vhodný pro scénáře, kde nemusíte nutně potřebovat každý paket, aby dorazil na místo určení, jako je streamování médií.
oceníte rozdíl mezi TCP a UDP, zvažte, co by se stalo, pokud jste streamované video z oblíbených stránek a zahozených snímcích. Chtěli byste raději, aby klient zpomalil váš film, aby obdržel chybějící snímky, nebo byste raději, aby video pokračovalo v přehrávání? Protokoly pro streamování videa obvykle využívají UDP., Protože TCP zaručuje doručení, je to protokol volby pro HTTP, FTP, SMTP, POP3 a tak dále.
v tomto tutoriálu vám představím programování soketů v Javě. Představuji řadu příkladů klient-server, které demonstrují funkce z původního Java i / o frameworku, pak postupně postupovat k použití funkcí zavedených v NIO.2.
old-school Java sockets
v implementacích před NIO je Kód klientského soketu Java TCP zpracován třídou java.net.Socket
., Následující kód otevře připojení k serveru:
Socket socket = new Socket( server, port );
Jednou socket
instance je připojen k serveru, můžeme začít získávat vstupní a výstupní proudy na sever. Vstupní toky se používají ke čtení dat ze serveru, zatímco výstupní proudy se používají k zápisu dat na server., Můžeme provést následující metody jak získat vstupní a výstupní proudy:
InputStream in = socket.getInputStream();OutputStream out = socket.getOutputStream();
Protože jsou to obyčejní proudy, stejné proudy, které budeme používat pro čtení a zápis do souboru, můžeme je převést do formy, která nejlépe slouží náš případ použití. Například bychom mohli zabalit OutputStream
pomocí PrintStream
, abychom mohli snadno psát text metodami jako println()
., Pro další příklad můžeme zabalit do InputStream
BufferedReader
, tak prostřednictvím InputStreamReader
, aby se snadno číst text s metodami jako readLine()
.
Java Socket client example
pojďme pracovat přes krátký příklad, který provádí HTTP GET proti HTTP serveru., HTTP je mnohem sofistikovanější než v našem příkladu povoluje, ale můžeme napsat kód klienta zvládnout nejjednodušší případ: žádost o zdroje ze serveru a server vrátí odpověď a zavře datový proud. Tento případ vyžaduje následující kroky:
- Vytvořte zásuvku pro poslech webového serveru na portu 80.
- Získat
PrintStream
na server a odeslat žádostGET PATH HTTP/1.0
, kdePATH
je požadovaný zdroj na serveru., Pokud bychom například chtěli otevřít kořen webu, cesta by byla/
. - získejte
InputStream
na server, zabalte jejBufferedReader
a přečtěte si řádek po řádku odpovědi.
Výpis 1 zobrazuje zdrojový kód pro tento příklad.
seznam 1. SimpleSocketClientExample.java
Výpis 1 přijímá dva argumenty příkazového řádku: server pro připojení k (za předpokladu, že jsme připojení k serveru na port 80) a zdrojů získat., Vytvoří Socket
, který ukazuje na server a explicitně určuje port 80
. To pak provede příkaz:
GET PATH HTTP/1.0
například:
GET / HTTP/1.0
Co se právě stalo?
Při načtení webové stránky z webového serveru, například , HTTP klient používá DNS servery najdete na serveru adresu: začíná tím, že žádá top-level domény serveru pro
com
domény, kde autoritativní domény-jméno serveru je ., Poté požádá server doménového jména pro adresu IP (nebo adresy) pro
. Dále otevře zásuvku na tento server na portu 80. (Nebo, pokud chcete definovat jiný port, můžete tak učinit přidáním dvojtečky následuje číslo portu, například:
:8080
.) A konečně, HTTP klient provede zadaného HTTP metodu, například GET
POST
PUT
DELETE
HEAD
nebo OPTI/ONS
. Každá metoda má svou vlastní syntaxi., Jak je uvedeno ve výše uvedeném kódu snips, metoda GET
vyžaduje cestu následovanou HTTP/version number
a prázdným řádkem. Pokud bychom chtěli přidat záhlaví HTTP, mohli jsme tak učinit před vstupem do nového řádku.
v seznamu 1 jsme získali OutputStream
a zabalili jsme jej do PrintStream
abychom mohli snadněji provádět naše textové příkazy., Náš kód získaný InputStream
, zabalené, že v InputStreamReader
, který převede jej do Reader
, a pak zabalené, že v BufferedReader
. Použili jsme PrintStream
spustit GET
metoda a pak použít BufferedReader
, přečíst odpověď, řádek po řádku, dokud jsme obdrželi null
reakci, což znamená, že zásuvky byly zavřené.,
provedení této třídy a předat následující argumenty:
java com.geekcap.javaworld.simplesocketclient.SimpleSocketClientExample www.javaworld.com /
Byste měli vidět výstup podobný tomu, co je níže:
Tento výstup ukazuje testovací stránku na JavaWorld webové stránky. Odpověděl zpět, že mluví HTTP verze 1.1 a odpověď je 200 OK
.
Java Socket Server example
pokryli jsme klientskou stranu a naštěstí je komunikační aspekt strany serveru stejně snadný., Z zjednodušující perspektivy, proces je následující:
- Vytvořit
ServerSocket
, určení portu pro naslouchání. - vyvolat
ServerSocket
‚saccept()
způsob pro poslech na nakonfigurovaném portu pro připojení klienta. - Když se klient připojí k serveru,
accept()
metoda vracíSocket
přes který server může komunikovat s klientem., To je stejnéSocket
třídy, který jsme použili pro našeho klienta, takže proces je stejný: získatInputStream
přečtěte si od klienta aOutputStream
napište klienta. - pokud je třeba server škálovat, budete chtít přenést
Socket
na jiné vlákno, které bude zpracovávat, aby váš server mohl pokračovat v poslechu dalších připojení. - volejte
ServerSocket
‚saccept()
znovu poslouchat další připojení.,
jak brzy uvidíte, řešení tohoto scénáře NIO by bylo trochu jiné., Teď, když můžeme přímo vytvořit ServerSocket
průchodem port pro naslouchání (více o ServerSocketFactory
y v další části):
ServerSocket serverSocket = new ServerSocket( port );
A teď můžeme přijmout příchozí připojení pomocí accept()
metoda:
Socket socket = serverSocket.accept();// Handle the connection ...
Vícevláknové programování v Java zásuvky
Výpis 2, níže, staví všechny kód serveru spolu tak daleko do mírně robustnější příklad, který používá vlákna pro zpracování více požadavků., Zobrazený server je echo server, což znamená, že odráží zpět jakoukoli zprávu, kterou obdrží.
Zatímco například v Výpis 2 není složité, to se předvídat, co přijde v další části na NIO. Věnujte zvláštní pozornost množství závitů kód musíme napsat s cílem vybudovat server, který dokáže zpracovat více souběžných požadavků.
Seznam 2. SimpleSocketServer.java
ve výpisu 2 vytvoříme novou instanci SimpleSocketServer
a spustíme server., To je nutné, protože SimpleSocketServer
rozšiřuje Thread
vytvořit nové vlákno zvládnout blokování accept()
volání, které vidíte v read()
metoda. run()
metoda sedí ve smyčce přijímá požadavky klientů a vytváří RequestHandler
vláken ke zpracování požadavku. Opět je to relativně jednoduchý kód, ale také zahrnuje značné množství závitového programování.,
Všimněte si, že RequestHandler
zpracovává komunikaci s klientem podobně jako kód ve Výpisu 1: zábaly OutputStream
PrintStream
k usnadnění snadno píše a podobně zábaly InputStream
BufferedReader
pro snadné čtení. Pokud jde o server, čte řádky od klienta a odráží je zpět klientovi. Pokud klient odešle prázdný řádek, konverzace je u konce aRequestHandler
zavře zásuvku.
NIO a NIO.,2
pro mnoho aplikací stačí základní programovací model Java socket, který jsme právě prozkoumali. Pro aplikace zahrnující více intenzivní I/O nebo asynchronní vstup/výstup, budete chtít, aby se seznámili s non-blokování Api zavedena v Java NIO a NIO.2.
balíček JDK 1.4 NIO nabízí následující klíčové vlastnosti:
- kanály jsou navrženy tak, aby podporovaly hromadné přenosy z jedné vyrovnávací paměti NIO do druhé.
- Buffery představují souvislý blok paměti propojený jednoduchou sadou operací.,
- neblokující vstup / výstup je sada tříd, které vystavují kanály běžným zdrojům I / O, jako jsou soubory a zásuvky.
Při programování s NIO, otevřete kanál k cíli, a pak čtení dat do vyrovnávací paměti od destinace, zapisovat data do vyrovnávací paměti, a poslat na místo určení., Budeme ponořit do nastavení zásuvky a získání kanál, aby to brzy, ale nejprve podívejme se na proces, pomocí vyrovnávací paměti:
- Zápis dat do vyrovnávací paměti
- Volat do vyrovnávací paměti
flip()
způsob, jak připravit je pro čtení - Čtení dat z vyrovnávací paměti
- Volat do vyrovnávací paměti
clear()
nebocompact()
způsob, jak připravit ji přijímat více dat
Při zápisu dat do vyrovnávací paměti, vyrovnávací paměti, ví, že množství dat zapsaných do., To udržuje tři vlastnosti, jejichž významy se liší, pokud je vyrovnávací paměť v režimu čtení nebo v režimu zápisu:
- Poloha: V režimu zápisu, počáteční pozice je 0 a drží aktuální pozice je napsán ve vyrovnávací paměti; poté, co jste fanda vyrovnávací paměti, aby ji v režimu čtení, to obnoví pozici 0 a drží aktuální pozice v bufferu čtené z,
- Kapacita: Pevné velikosti vyrovnávací paměti
- Omezení: V režimu zápisu, limit určuje, kolik dat může být zapsáno do vyrovnávací paměti; v režimu čtení, limit určuje, kolik dat lze číst z vyrovnávací paměti.,
Java i / o demo: Echo server s NIO.2
NIO.2, který byl představen v JDK 7, rozšiřuje Java je non-blocking I/O knihovny přidat podporu pro souborový systém úkolů, jako je například java.nio.file
balíček java.nio.file.Path
třídy a odhaluje nový Systém Souborů API. S ohledem na toto pozadí napíšeme nový server Echo pomocí NIO.2 AsynchronousServerSocketChannel
.
AsynchronousServerSocketChannel
poskytuje neblokující asynchronní kanál pro stream orientované na poslech zásuvky., Abychom jej mohli použít, nejprve provedeme jeho statickou metodu open()
a poté bind()
na konkrétní port. Dále provedeme metodu accept()
a předáme jí třídu, která implementuje rozhraní CompletionHandler
. Nejčastěji najdete toho psovoda vytvořeného jako anonymní vnitřní třída.
Výpis 3 zobrazuje zdrojový kód pro náš nový asynchronní Echo Server.
Výpis 3. SimpleSocketServer.,java
Výpis 3 jsme nejprve vytvořit nový AsynchronousServerSocketChannel
a pak svázat ho do port 5000:
final AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(5000));
AsynchronousServerSocketChannel
, vzýváme accept()
říct, je začít naslouchat pro připojení, mimochodem na to vlastní CompletionHandler
instance. Když vyvoláme accept()
, okamžitě se vrátí., Všimněte si, že tento příklad se liší od ServerSocket
třídy ve Výpisu 1; vzhledem k tomu, že accept()
metoda blokován, dokud klient připojen k němu, AsynchronousServerSocketChannel
accept()
metoda zpracovává to pro nás.
dokončení handler
Naším dalším úkolem je vytvořit CompletionHandler
class a poskytují provádění completed()
failed()
metody., completed()
metoda je volána, když AsynchronousServerSocketChannel
přijímá připojení z klienta a zahrnuje AsynchronousSocketChannel
pro klienta. Metoda completed()
nejprve přijme připojení z AsynchronousServerSocketChannel
a poté začne komunikovat s klientem. První věc, kterou dělá, je napsat zprávu“ Hello“: vytvoří řetězec, převede jej na pole bajtů a poté ji předá ByteBuffer.wrap()
a vytvoří ByteBuffer
., ByteBuffer
pak může být předánAsynchronousSocketChannel
‚swrite()
metoda.
přečtěte si od klienta, jsme se vytvořit nový ByteBuffer
vyvoláním jeho allocate(4096)
(který vytváří 4K buffer), pak vzýváme AsynchronousSocketChannel
‚read()
metoda. read()
vrací Future<Integer>
, na které se můžeme odvolávat get()
načíst počet bajtů číst od klienta., V tomto příkladu předámeget()
hodnotu časového limitu 20 sekund: pokud nedostaneme odpověď za 20 sekund, pak metodaget()
hodíTimeoutException
. Naše pravidlo pro tento echo server je, že pokud budeme pozorovat 20 sekund ticha, ukončíme konverzaci.
dále zkontrolujeme polohu vyrovnávací paměti, což bude umístění posledního bajtu přijatého od klienta. Pokud klient odešle prázdný řádek, dostaneme dva bajty: návrat vozíku a posuv linky., Kontrola zajišťuje, že pokud klient odešle prázdný řádek, vezmeme jej jako indikátor, že klient je ukončen konverzací. Pokud máme smysluplná data, nazývámeByteBuffer
‚sflip()
způsob, jak jej připravit ke čtení. Jsme vytvořit dočasný byte pole držet počet bajtů čtení z klienta, a pak vyvolat ByteBuffer
‚get()
načíst data do pole bajtů. Nakonec převedeme pole bajtů na řetězec vytvořením nové instanceString
., Jsme echo na řádku zpět klientovi tím, že převede řetězec na pole bajtů, kolem které ByteBuffer.wrap()
metoda a vyvolání AsynchronousSocketChannel
‚write()
metoda. Nyní jsme clear()
ByteBuffer
, které připomínají znamená, že se to přemístí position
na nulu a staví ByteBuffer
do režimu zápisu, a pak jsme si přečíst další řádek od klienta.,
jediná věc je být si vědom, je, že main()
metoda, která vytvoří server, také zřizuje 60 sekund časovač, aby aplikace běží. Protože AsynchronousSocketChannel
‚accept()
metoda vrátí okamžitě, pokud nemáme Thread.sleep()
pak naše aplikace bude okamžitě zastavit.,
tento test, spustit server a připojit se k němu pomocí telnet klienta:
telnet localhost 5000
Poslat pár nitek na server, pozorovat, že jsou ozvěnou zpátky k vám, a pak poslat prázdný řádek ukončit rozhovor.
závěr
V tomto článku jsem představil dva přístupy k programování socket s Java: tradiční přístup zavedl s Java 1.0 a novější, non-blokování NIO a NIO.2 přístupy zavedené v Java 1.4 a Java 7, resp., Viděli jste několik iterací Java socket client a Java socket server příklad, prokazující, jak nástroj základní Java I/O a některé scénáře, kde non-blocking I/O zlepšuje Java socket programming model. Použití non-blocking I/O, můžete program Java síťových aplikací pro zpracování více souběžných připojení, aniž byste museli spravovat více nití sbírky. Můžete také využít nové škálovatelnosti serveru, která je zabudována do NIO a NIO.2.