Psaní kódu, které se spouští na určitém zařízení, je velmi uspokojivé. Ale psaní kódu, který se spouští na několika zařízeních, která spolu komunikují, je prostě život potvrzující. Tento článek vás naučí, jak se připojit a vyměňovat si zprávy přes síť pomocí protokolu TCP (Control Control Protocol).
V tomto článku nastavíte aplikaci, která k vám připojí počítač a v podstatě z něj udělá blázna - mluvte sami se sebou. Dozvíte se také rozdíl mezi dvěma nejpoužívanějšími streamy pro vytváření sítí v Javě a jejich funkcí.
Datové a objektové toky
Před ponořením do kódu je třeba rozlišit rozdíl mezi dvěma proudy použitými v článku.
Datové toky
Datové toky zpracovávají primitivní datové typy a řetězce. Data odesílaná přes datové toky je třeba ručně serializovat a deserializovat, což ztěžuje přenos složitých dat. Datové toky však mohou komunikovat se servery a klienty napsanými v jiných jazycích než v jazyce Java. Surové toky jsou v tomto aspektu podobné datovým proudům, ale datové toky zajišťují, že jsou data formátována nezávisle na platformě, což je výhodné, protože obě strany budou moci číst odeslaná data.
Streamování objektů
Datové toky objektů zpracovávají primitivní datové typy a objekty, které implementují
Serializovatelné
rozhraní. Data odesílaná přes datové proudy objektů jsou automaticky serializována a deserializována, což usnadňuje přenos složitých dat. Objektové streamy však mohou komunikovat pouze se servery a klienty napsanými v jazyce Java. Taky,
ObjectOutputStream
při inicializaci odešle záhlaví do
InputStream
druhé strany, která při inicializaci blokuje provádění, dokud není přijato záhlaví.
Kroky
Krok 1. Vytvořte třídu
Vytvořte třídu a pojmenujte ji, jak chcete. V tomto článku bude pojmenován
NetworkAppExample
veřejná třída NetworkAppExample {}
Krok 2. Vytvořte hlavní metodu
Vytvořte hlavní metodu a deklarujte, že může vyvolat výjimky
Výjimka
typ a jakákoli jeho podtřída - všechny výjimky. To je považováno za špatnou praxi, ale je to přijatelné pro barebone příklady.
public class NetworkAppExample {public static void main (String args) throws Exception {}}
Krok 3. Deklarujte adresu serveru
Tento příklad bude používat adresu místního hostitele a libovolné číslo portu. Číslo portu musí být v rozsahu od 0 do 65535 (včetně). Čísla portů, kterým je třeba se vyhnout, se však pohybují od 0 do 1023 (včetně), protože jde o vyhrazené systémové porty.
public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; }}
Krok 4. Vytvořte server
Server je vázán na adresu a port a poslouchá příchozí připojení. V Javě,
ServerSocket
představuje koncový bod na straně serveru a jeho funkcí je přijímání nových připojení.
ServerSocket
nemá streamy pro čtení a odesílání dat, protože nepředstavuje spojení mezi serverem a klientem.
import java.net. InetAddress; import java.net. ServerSocket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); }}
Krok 5. Počátek přihlášení serveru
Pro účely protokolování vytiskněte na konzolu, že server byl spuštěn.
import java.net. InetAddress; import java.net. ServerSocket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); }}
Krok 6. Vytvořte klienta
Klient je vázán na adresu a port serveru a po navázání připojení poslouchá pakety (zprávy). V Javě,
Zásuvka
představuje buď koncový bod na straně klienta připojený k serveru, nebo připojení (ze serveru) ke klientovi, a slouží ke komunikaci se stranou na druhém konci.
import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = new Socket (host, port); }}
Krok 7. Pokus o připojení
Pro účely protokolování vytiskněte na konzole, že došlo k pokusu o připojení.
import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = nový Socket (hostitel, port); System.out.println ("Připojování k serveru …"); }}
Krok 8. Navažte připojení
Klienti se nikdy nepřipojí, pokud server naslouchá a nepřijímá, jinými slovy navazuje, připojení. V Javě se připojení navazují pomocí
přijmout()
metoda
ServerSocket
třída. Metoda zablokuje provádění, dokud se klient nepřipojí.
import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = nový Socket (hostitel, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); }}
Krok 9. Zaznamenejte navázané připojení
Pro účely protokolování vytiskněte na konzoli, že bylo navázáno spojení mezi serverem a klientem.
import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = new Socket (host, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); }}
Krok 10. Připravte komunikační toky
Komunikace probíhá přes streamy a v této aplikaci je třeba surové toky (připojení z) serveru (ke klientovi) a klientovi zřetězit do datových nebo objektových proudů. Obě strany musí používat stejný typ streamu.
-
Datové toky
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = nový Socket (hostitel, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); DataOutputStream clientOut = nový DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = nový DataInputStream (client.getInputStream ()); DataOutputStream serverOut = nový DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = nový DataInputStream (connection.getInputStream ()); }}
-
Proudy objektů
Pokud je použito více proudů objektů, musí být vstupní toky inicializovány ve stejném pořadí jako výstupní toky, protože
ObjectOutputStream
odešle hlavičku druhé straně a
ObjectInputStream
blokuje provádění, dokud nečte záhlaví.
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = nový Socket (hostitel, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); ObjectOutputStream clientOut = nový ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = nový ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = nový ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = nový ObjectInputStream (connection.getInputStream ()); }}
Pořadí, jak je uvedeno ve výše uvedeném kódu, může být snadněji zapamatovatelné - nejprve inicializujte výstupní toky a poté vstupní toky ve stejném pořadí. Další pořadí pro inicializaci streamů objektů je však následující:
ObjectOutputStream clientOut = nový ObjectOutputStream (client.getOutputStream ()); ObjectInputStream serverIn = nový ObjectInputStream (connection.getInputStream ()); ObjectOutputStream serverOut = nový ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = nový ObjectInputStream (client.getInputStream ());
Krok 11. Zaznamenejte, že komunikace je připravena
Pro účely protokolování vytiskněte na konzoli, že je komunikace připravena.
// kód vynechán import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = new Socket (host, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); // kód vynechán System.out.println ("Komunikace je připravena."); }}
Krok 12. Vytvořte zprávu
V této aplikaci
Ahoj světe
text bude odeslán na server buď jako
byte
nebo
Tětiva
. Deklarujte proměnnou typu, který závisí na použitém streamu. Použití
byte
pro datové toky a
Tětiva
pro toky objektů.
-
Datové toky
Pomocí datových proudů se serializace provádí převodem objektů na primitivní datové typy nebo a
Tětiva
. V tomto případě,
Tětiva
je převeden na
byte
místo písemného použití
writeBytes ()
metoda, která ukazuje, jak by to bylo provedeno s jinými objekty, jako jsou obrázky nebo jiné soubory.
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = nový Socket (hostitel, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); DataOutputStream clientOut = nový DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = nový DataInputStream (client.getInputStream ()); DataOutputStream serverOut = nový DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = nový DataInputStream (connection.getInputStream ()); System.out.println ("Komunikace je připravena."); byte messageOut = "Hello World".getBytes (); }}
-
Proudy objektů
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = nový Socket (hostitel, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); ObjectOutputStream clientOut = nový ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = nový ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = nový ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = nový ObjectInputStream (connection.getInputStream ()); System.out.println („Komunikace je připravena.“); Řetězec messageOut = "Hello World"; }}
Krok 13. Odešlete zprávu
Zapisujte data do výstupního proudu a vypláchněte stream, abyste se ujistili, že data byla zapsána celá.
-
Datové toky
Nejprve je třeba odeslat délku zprávy, aby druhá strana věděla, kolik bajtů potřebuje přečíst. Poté, co je délka odeslána jako primitivní celočíselný typ, lze odeslat bajty.
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = nový Socket (hostitel, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); DataOutputStream clientOut = nový DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = nový DataInputStream (client.getInputStream ()); DataOutputStream serverOut = nový DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = nový DataInputStream (connection.getInputStream ()); System.out.println ("Komunikace je připravena."); byte messageOut = "Hello World".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); }}
-
Proudy objektů
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = nový Socket (hostitel, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); ObjectOutputStream clientOut = nový ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = nový ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = nový ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = nový ObjectInputStream (connection.getInputStream ()); System.out.println ("Komunikace je připravena."); Řetězec messageOut = "Hello World"; clientOut.writeObject (messageOut); clientOut.flush (); }}
Krok 14. Zaznamenat odeslanou zprávu
Pro účely protokolování vytiskněte na konzolu, že byla zpráva odeslána.
-
Datové toky
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = nový Socket (hostitel, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); DataOutputStream clientOut = nový DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = nový DataInputStream (client.getInputStream ()); DataOutputStream serverOut = nový DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = nový DataInputStream (connection.getInputStream ()); System.out.println ("Komunikace je připravena."); byte messageOut = "Hello World".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); System.out.println ("Zpráva odeslaná na server:" + nový řetězec (messageOut)); }}
-
Proudy objektů
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = new Socket (host, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); ObjectOutputStream clientOut = nový ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = nový ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = nový ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = nový ObjectInputStream (connection.getInputStream ()); System.out.println („Komunikace je připravena.“); Řetězec messageOut = "Hello World"; clientOut.writeObject (messageOut); clientOut.flush (); System.out.println ("Zpráva odeslaná na server:" + messageOut); }}
Krok 15. Přečtěte si zprávu
Přečtěte data ze vstupního proudu a převeďte je. Protože přesně známe typ odesílaných dat, vytvoříme buď
Tětiva
z
byte
nebo obsazení
Objekt
na
Tětiva
bez kontroly, v závislosti na použitém streamu.
-
Datové toky
Protože byla odeslána první délka a poté bajty, musí být čtení provedeno ve stejném pořadí. V případě, že je délka nulová, není co číst. Objekt je deserializován, když jsou bajty převedeny zpět na instanci, v tomto případě
Tětiva
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = new Socket (host, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); DataOutputStream clientOut = nový DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = nový DataInputStream (client.getInputStream ()); DataOutputStream serverOut = nový DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = nový DataInputStream (connection.getInputStream ()); System.out.println („Komunikace je připravena.“); byte messageOut = "Hello World".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); System.out.println ("Zpráva odeslaná na server:" + nový řetězec (messageOut)); int délka = serverIn.readInt (); if (délka> 0) {byte messageIn = nový byte [délka]; serverIn.readFully (messageIn, 0, messageIn.length); }}}
-
Proudy objektů
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = new Socket (host, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); ObjectOutputStream clientOut = nový ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = nový ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = nový ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = nový ObjectInputStream (connection.getInputStream ()); System.out.println („Komunikace je připravena.“); Řetězec messageOut = "Hello World"; clientOut.writeObject (messageOut); clientOut.flush (); System.out.println ("Zpráva odeslaná na server:" + messageOut); Řetězec messageIn = (Řetězec) serverIn.readObject (); }}
Krok 16. Protokol přečtené zprávy
Pro účely protokolování vytiskněte na konzolu, že zpráva byla přijata, a vytiskněte její obsah.
-
Datové toky
import java.io. DataInputStream; import java.io. DataOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = new Socket (host, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); DataOutputStream clientOut = nový DataOutputStream (client.getOutputStream ()); DataInputStream clientIn = nový DataInputStream (client.getInputStream ()); DataOutputStream serverOut = nový DataOutputStream (connection.getOutputStream ()); DataInputStream serverIn = nový DataInputStream (connection.getInputStream ()); System.out.println („Komunikace je připravena.“); byte messageOut = "Hello World".getBytes (); clientOut.writeInt (messageOut.length); clientOut.write (messageOut); clientOut.flush (); System.out.println ("Zpráva odeslaná na server:" + nový řetězec (messageOut)); int délka = serverIn.readInt (); if (délka> 0) {byte messageIn = nový byte [délka]; serverIn.readFully (messageIn, 0, messageIn.length); System.out.println ("Zpráva přijatá od klienta:" + nový řetězec (messageIn)); }}}
-
Proudy objektů
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = new Socket (host, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); ObjectOutputStream clientOut = nový ObjectOutputStream (client.getOutputStream ()); ObjectOutputStream serverOut = nový ObjectOutputStream (connection.getOutputStream ()); ObjectInputStream clientIn = nový ObjectInputStream (client.getInputStream ()); ObjectInputStream serverIn = nový ObjectInputStream (connection.getInputStream ()); System.out.println („Komunikace je připravena.“); Řetězec messageOut = "Hello World"; clientOut.writeObject (messageOut); clientOut.flush (); System.out.println ("Zpráva odeslaná na server:" + messageOut); Řetězec messageIn = (Řetězec) serverIn.readObject (); System.out.println ("Zpráva přijatá od klienta:" + messageIn); }}
Krok 17. Odpojte připojení
Připojení se přeruší, když jedna strana zavře své streamy. V Javě se zavřením výstupního proudu uzavře také související soket a vstupní proud. Jakmile strana na druhém konci zjistí, že je připojení mrtvé, musí také ukončit svůj výstupní proud, aby se zabránilo úniku paměti.
// kód vynechán import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = new Socket (host, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); // kód vynechán System.out.println ("Komunikace je připravena."); // kód vynechán clientOut.close (); serverOut.close (); }}
Krok 18. Odpojení protokolu
Pro účely protokolování byly odpojeny připojení ke konzole tisku.
// kód vynechán import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = new Socket (host, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); // kód vynechán System.out.println ("Komunikace je připravena."); // kód vynechán clientOut.close (); serverOut.close (); System.out.println ("Připojení uzavřeno."); }}
Krok 19. Ukončete server
Připojení jsou odpojena, ale server je stále v provozu. Tak jako
ServerSocket
není spojen s žádným streamem, musí být explicitně uzavřen voláním
zavřít()
metoda.
// kód vynechán import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = new Socket (host, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); // kód vynechán System.out.println ("Komunikace je připravena."); // kód vynechán clientOut.close (); serverOut.close (); System.out.println ("Připojení uzavřeno."); server.close (); }}
Krok 20. Protokolování ukončení serveru
Pro účely protokolování byl tisk na server konzoly ukončen.
// kód vynechán import java.net. InetAddress; import java.net. ServerSocket; import java.net. Socket; public class NetworkAppExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; ServerSocket server = nový ServerSocket (port, 50, InetAddress.getByName (hostitel)); System.out.println ("Server spuštěn."); Socket client = new Socket (host, port); System.out.println ("Připojování k serveru …"); Soketové připojení = server.accept (); System.out.println ("Připojení navázáno."); // kód vynechán System.out.println ("Komunikace je připravena."); // kód vynechán clientOut.close (); serverOut.close (); System.out.println ("Připojení uzavřeno."); server.close (); System.out.println ("Server ukončen."); }}
Krok 21. Zkompilujte a spusťte
Protokolování nám umožnilo zjistit, zda byla aplikace úspěšná nebo ne. Očekávaný výkon:
Server spuštěn. Připojování k serveru … Připojení navázáno. Komunikace je připravena. Zpráva odeslána na server: Hello World Zpráva od klienta přijata: Hello World Connections ukončena. Server ukončen.
V případě, že váš výstup není stejný jako výše, což se pravděpodobně nestane, existuje několik řešení:
-
Pokud se výstup zastaví na řádku
Spojení navázáno.
a používají se toky objektů, vypláchněte každý
ObjectOutputStream
- bezprostředně po inicializaci, protože záhlaví z nějakého důvodu nebyla odeslána.
-
Pokud se tisk vytiskne
java.net. BindException: Adresa se již používá
- zvolte jiné číslo portu, protože zadané je již použito.
Tipy
- Připojení k serveru v jiné síti se provádí připojením k externí IP adrese zařízení se serverem, který má předaný port.
- Připojení k serveru ve stejné síti se provádí buď připojením k soukromé IP adrese zařízení se serverem, nebo přesměrováním portu a připojením k externí IP adrese zařízení.
- Existuje software, například Hamachi, který umožňuje připojení k serveru v jiné síti bez přeposílání portu, ale vyžaduje instalaci softwaru na obě zařízení.
Příklady
Síťové aplikace, které používají blokování vstupu/výstupu, musí používat vlákna. Následující příklady ukazují minimalistickou implementaci serveru a klienta s vlákny. Síťový kód je v zásadě stejný jako v článku kromě toho, že některé úryvky byly synchronizovány, přesunuty do vláken a řeší se výjimky.
Server.java
import java.io. IOException; import java.net. InetAddress; import java.net. ServerSocket; import java.net. SocketException; import java.net. UnknownHostException; import java.util. ArrayList; import java.util. Collections; import java.util. List; /*** Třída {@code Server} představuje koncový bod serveru v síti. {@code Server}, jakmile je vázán na určitou IP * adresu a port, naváže spojení s klienty a je schopen s nimi komunikovat nebo je odpojit. *
* Tato třída je bezpečná pro vlákna. * * @version 1.0 * @see Client * @see Connection */ public class Server implementuje Spustitelný {soukromý server ServerSocket; soukromý seznam
připojení; vlákno soukromého vlákna; private final Object connectionsLock = nový Object (); /** * Konstruuje {@code Server}, který interaguje s klienty na zadaném názvu hostitele a portu se zadanou * požadovanou maximální délkou fronty příchozích klientů. * * @param host Adresa hostitele k použití. * @param port Číslo portu, který se má použít. * @param backlog Požadovaná maximální délka fronty příchozích klientů. * @throws NetworkException Pokud dojde ke chybě při spouštění serveru. */ public Server (String host, int port, int backlog) hodí NetworkException {try {server = new ServerSocket (port, backlog, InetAddress.getByName (host)); } catch (UnknownHostException e) {throw new NetworkException ("Název hostitele nelze přeložit:" + host, e); } catch (IllegalArgumentException e) {throw new NetworkException ("Číslo portu musí být mezi 0 a 65535 (včetně):" + port); } catch (IOException e) {throw new NetworkException ("Server could not be started.", e); } připojení = Collections.synchronizedList (nový ArrayList ()); vlákno = nové vlákno (toto); vlákno.start (); } /*** Konstruuje {@code Server}, který komunikuje s klienty na zadaném názvu hostitele a portu. * * @param host Adresa hostitele pro vazbu. * @param port Číslo portu pro vazbu. * @throws NetworkException Pokud při spouštění serveru dojde k chybě. */ public Server (String host, int port) hodí NetworkException {this (host, port, 50); } /*** Naslouchá, přijímá a registruje příchozí připojení od klientů. */ @Override public void run () {while (! Server.isClosed ()) {try {connections.add (new Connection (server.accept ())); } catch (SocketException e) {if (! e.getMessage (). equals ("Socket closed")) {e.printStackTrace (); }} catch (NetworkException | IOException e) {e.printStackTrace (); }}} /*** Odesílá data všem registrovaným klientům. * * @param data Data k odeslání. * @throws IllegalStateException Pokud dojde k pokusu o zápis dat, když je server offline. * @throws IllegalArgumentException Pokud jsou data k odeslání nulová. */ public void broadcast (Object data) {if (server.isClosed ()) {throw new IllegalStateException ("Data not sent, server is offline."); } if (data == null) {throw new IllegalArgumentException ("null data"); } synchronizováno (connectionsLock) {for (Connection connection: connections) {try {connection.send (data); System.out.println ("Data byla úspěšně odeslána klientovi."); } catch (NetworkException e) {e.printStackTrace (); }}}} /*** Odešle zprávu o odpojení a odpojí zadaného klienta. * * @param připojení Klient k odpojení. * @throws NetworkException Pokud dojde k chybě při zavírání připojení. */ public void disconnect (Connection connection) throws NetworkException {if (connections.remove (connection)) {connection.close (); }} /*** Odešle všem klientům zprávu o odpojení, odpojí je a ukončí server. */ public void close () vyvolá NetworkException {synchronized (connectionsLock) {for (Connection connection: connections) {try {connection.close (); } catch (NetworkException e) {e.printStackTrace (); }}} connections.clear (); zkuste {server.close (); } catch (IOException e) {throw new NetworkException ("Chyba při zavírání serveru."); } konečně {vlákno.interrupt (); }} /*** Vrátí, zda je server online. * * @return True, pokud je server online. Jinak nepravda. */ public boolean isOnline () {return! server.isClosed (); } /*** Vrací řadu registrovaných klientů. */ public Connection getConnections () {synchronized (connectionsLock) {return connections.toArray (new Connection [connections.size ()]); }}}
Client.java
import java.io. IOException; import java.net. Socket; import java.net. UnknownHostException; /*** Třída {@code Client} představuje koncový bod klienta v síti. {@code Client}, po připojení k určitému * serveru, bude zaručeně schopen komunikovat pouze se serverem. Zda ostatní klienti získají data * závisí na implementaci serveru. *
* Tato třída je bezpečná pro vlákna. * * @version 1.0 * @viz server * @viz připojení */ klient veřejné třídy {soukromé připojení; /*** Vytvoří {@code Client} připojeného k serveru na zadaném hostiteli a portu. * * @param host Adresa hostitele pro vazbu. * @param port Číslo portu pro vazbu. * @throws NetworkException Pokud dojde ke chybě při spouštění serveru. */ public Client (String host, int port) hodí NetworkException {try {connection = new Connection (new Socket (host, port)); } catch (UnknownHostException e) {throw new NetworkException ("Název hostitele nelze přeložit:" + host, e); } catch (IllegalArgumentException e) {throw new NetworkException ("Číslo portu musí být mezi 0 a 65535 (včetně):" + port); } catch (IOException e) {throw new NetworkException ("Server could not be started.", e); }} /*** Odesílá data druhé straně. * * @param data Data k odeslání. * @throws NetworkException Pokud zápis do výstupního proudu selže. * @throws IllegalStateException Pokud dojde k pokusu o zápis dat, když je připojení uzavřeno. * @throws IllegalArgumentException Pokud jsou data k odeslání nulová. * @throws UnsupportedOperationException Pokud se pokoušíte odeslat nepodporovaný datový typ. */ public void send (Objektová data) vyvolá NetworkException {connection.send (data); } /*** Odešle zprávu o odpojení na server a ukončí spojení se serverem. */ public void close () hodí NetworkException {connection.close (); } /*** Vrátí, zda je klient připojen k serveru či nikoli. * * @return True, pokud je klient připojen. Jinak nepravda. */ public boolean isOnline () {return connection.isConnected (); } /*** Vrací instanci klienta {@link Connection}. */ public Connection getConnection () {zpětné připojení; }}
Connection.java
import java.io. DataInputStream; import java.io. DataOutputStream; import java.io. IOException; import java.net. Socket; import java.net. SocketException; /** * Třída {@code Connection} představuje buď připojení ze serveru na klienta, nebo koncový bod klienta v síti * {@code Connection}, jakmile je připojen, je schopen vyměňovat si data s jinou stranou nebo stranami v závislosti na na implementaci serveru *. *
* Tato třída je bezpečná pro vlákna. * * @version 1.0 * @see Server * @see Client */ public class Připojení implementuje Spustitelné {private Socket socket; soukromý DataOutputStream ven; soukromý DataInputStream v; vlákno soukromého vlákna; private final Object writeLock = nový Object (); private final Objekt readLock = nový Object (); /*** Vytváří {@code Connection} pomocí streamů zadaného {@link Socket}. * * @param socket Socket pro načtení streamů z.*/ public Connection (Socket socket) hodí NetworkException {if (socket == null) {throw new IllegalArgumentException ("null socket"); } this.socket = socket; zkusit {out = new DataOutputStream (socket.getOutputStream ()); } catch (IOException e) {throw new NetworkException ("Nelze získat přístup k výstupnímu proudu.", e); } zkusit {in = new DataInputStream (socket.getInputStream ()); } catch (IOException e) {throw new NetworkException ("Nelze získat přístup ke vstupnímu proudu.", e); } vlákno = nové vlákno (toto); vlákno.start (); } /*** Čte zprávy, když je spojení s druhou stranou aktivní. */ @Override public void run () {while (! Socket.isClosed ()) {try {int identifier; byte bajtů; synchronizovaný (readLock) {identifier = in.readInt (); int délka = in.readInt (); if (length> 0) {bytes = new byte [length]; in.readFully (bajty, 0, bajty. délka); } else {pokračovat; }} switch (identifier) {case Identifier. INTERNAL: String command = new String (bytes); if (command.equals ("disconnect")) {if (! socket.isClosed ()) {System.out.println ("Disconnection packet prijat."); zkusit {zavřít (); } catch (NetworkException e) {return; } } } přestávka; case Identifier. TEXT: System.out.println ("Přijatá zpráva:" + nový řetězec (bajty)); přestávka; výchozí: System.out.println ("Byla přijata nerozpoznaná data."); }} catch (SocketException e) {if (! e.getMessage (). equals ("Socket closed")) {e.printStackTrace (); }} catch (IOException e) {e.printStackTrace (); }}} /*** Odesílá data druhé straně. * * @param data Data k odeslání. * @throws NetworkException Pokud zápis do výstupního proudu selže. * @throws IllegalStateException Pokud dojde k pokusu o zápis dat, když je připojení uzavřeno. * @throws IllegalArgumentException Pokud jsou data k odeslání nulová. * @throws UnsupportedOperationException Pokud se pokoušíte odeslat nepodporovaný datový typ. */ public void send (Object data) throws NetworkException {if (socket.isClosed ()) {throw new IllegalStateException ("Data not sent, connection is closed."); } if (data == null) {throw new IllegalArgumentException ("null data"); } identifikátor int; byte bajtů; if (datová instance řetězce) {identifier = Identifier. TEXT; bajty = ((String) data).getBytes (); } else {throw new UnsupportedOperationException ("Unsupported data type:" + data.getClass ()); } zkusit {synchronized (writeLock) {out.writeInt (identifier); out.writeInt (bytes.length); out.write (bajty); out.flush (); }} catch (IOException e) {throw new NetworkException ("Data could not be sent.", e); }} /*** Odešle zprávu o odpojení druhé straně a uzavře s ní spojení. */ public void close () hodí NetworkException {if (socket.isClosed ()) {throw new IllegalStateException ("Připojení je již uzavřeno."); } zkuste {byte message = "odpojit".getBytes (); synchronizované (writeLock) {out.writeInt (Identifier. INTERNAL); out.writeInt (message.length); out.write (zpráva); out.flush (); }} catch (IOException e) {System.out.println ("Zprávu o odpojení nelze odeslat."); } zkusit {synchronized (writeLock) {out.close (); }} catch (IOException e) {throw new NetworkException ("Chyba při zavírání připojení.", e); } konečně {vlákno.interrupt (); }} /*** Vrátí, zda je připojení k druhé straně aktivní. * * @return True, pokud je připojení aktivní. Jinak nepravda. */ public boolean isConnected () {return! socket.isClosed (); }}
Identifier.java
/** * Třída {@code Identifier} obsahuje konstanty používané {@link Connection} pro serializaci a deserializaci dat * odesílaných po síti. * * @version 1.0 * @viz Připojení * / veřejný identifikátor konečné třídy { / ** * Identifikátor pro interní zprávy. */ public static final int INTERNAL = 1; /*** Identifikátor pro textové zprávy. */ public static final int TEXT = 2; }
NetworkException.java
/*** Třída {@code NetworkException} označuje chybu související se sítí. * / public class NetworkException rozšiřuje výjimku { / *** Konstruuje {@code NetworkException} se zprávou {@code null}. * / public NetworkException () {} / *** Vytvoří {@code NetworkException} se zadanou zprávou. * * @param message Zpráva popisující chybu. */ public NetworkException (String message) {super (message); } /*** Vytvoří {@code NetworkException} se zadanou zprávou a příčinou. * * @param message Zpráva popisující chybu. * @param příčina Příčina chyby. */ public NetworkException (String message, Throwable cause) {super (zpráva, příčina); } /*** Vytvoří {@code NetworkException} se zadanou příčinou. * * @param příčina Příčina chyby. */ public NetworkException (vyvolávací příčina) {super (příčina); }}
UsageExample.java
/*** Třída {@code UsageExample} ukazuje využití {@link Server} a {@link Client}. Tento příklad používá * {@link Thread#sleep (long)} k zajištění spuštění každého segmentu, protože rychlé spuštění a zavření způsobí, že některé * segmenty nebudou spuštěny. * * @version 1.0 * @see Server * @see Client */ public class UsageExample {public static void main (String args) throws Exception {String host = "localhost"; int port = 10430; Server server = nový Server (hostitel, port); Klient klienta = nový klient (hostitel, port); Thread.sleep (100L); client.send („Dobrý den.“); server.broadcast („Hej, kámo!“); Thread.sleep (100L); server.disconnect (server.getConnections () [0]); // nebo client.close () pro odpojení od klienta server.close (); }}