Das Schreiben von Code, der auf einem bestimmten Gerät ausgeführt wird, ist sehr zufriedenstellend. Aber das Schreiben von Code, der auf mehreren Geräten ausgeführt wird, die miteinander kommunizieren, ist einfach lebensbejahend. In diesem Artikel erfahren Sie, wie Sie mit dem Transmission Control Protocol (TCP) Nachrichten über ein Netzwerk verbinden und austauschen.
In diesem Artikel richten Sie eine Anwendung ein, die Ihren Computer mit sich selbst verbindet und ihn im Wesentlichen verrückt macht - mit sich selbst sprechen. Sie lernen auch den Unterschied zwischen den beiden am häufigsten verwendeten Streams für die Vernetzung in Java und ihre Funktionsweise kennen.
Daten- und Objektströme
Bevor Sie in den Code eintauchen, muss der Unterschied zwischen den beiden im Artikel verwendeten Streams unterschieden werden.
Datenströme
Datenströme verarbeiten primitive Datentypen und Strings. Über Datenströme gesendete Daten müssen manuell serialisiert und deserialisiert werden, was die Übertragung komplexer Daten erschwert. Datenströme können jedoch mit Servern und Clients kommunizieren, die in anderen Sprachen als Java geschrieben sind. Rohdatenströme ähneln in dieser Hinsicht Datenströmen, aber Datenströme stellen sicher, dass die Daten plattformunabhängig formatiert werden, was von Vorteil ist, da beide Parteien die gesendeten Daten lesen können.
Objektströme
Objektströme verarbeiten primitive Datentypen und Objekte, die implementieren
Serialisierbar
Schnittstelle. Über Objektströme gesendete Daten werden automatisch serialisiert und deserialisiert, was die Übertragung komplexer Daten erleichtert. Objektströme können jedoch nur mit in Java geschriebenen Servern und Clients kommunizieren. Ebenfalls,
ObjectOutputStream
sendet bei der Initialisierung einen Header an den
Eingabestrom
der anderen Partei, die bei der Initialisierung die Ausführung blockiert, bis der Header empfangen wird.
Schritte
Schritt 1. Erstellen Sie eine Klasse
Erstellen Sie eine Klasse und benennen Sie sie nach Belieben. In diesem Artikel wird es benannt
NetzwerkAppBeispiel
öffentliche Klasse NetworkAppExample { }
Schritt 2. Erstellen Sie eine Hauptmethode
Erstellen Sie eine Hauptmethode und deklarieren Sie, dass sie Ausnahmen von auslösen könnte
Ausnahme
Typ und jede Unterklasse davon - alle Ausnahmen. Dies gilt als schlechte Praxis, ist aber für Barebone-Beispiele akzeptabel.
public class NetworkAppExample { public static void main(String args) wirft Ausnahme {}}
Schritt 3. Deklarieren Sie die Serveradresse
In diesem Beispiel wird die lokale Hostadresse und eine beliebige Portnummer verwendet. Die Portnummer muss im Bereich von 0 bis 65535 (einschließlich) liegen. Zu vermeidende Portnummern reichen jedoch von 0 bis 1023 (einschließlich), da es sich um reservierte Systemports handelt.
public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; } }
Schritt 4. Erstellen Sie einen Server
Server ist an Adresse und Port gebunden und wartet auf eingehende Verbindungen. Auf Java,
ServerSocket
stellt den serverseitigen Endpunkt dar und seine Funktion akzeptiert neue Verbindungen.
ServerSocket
hat keine Streams zum Lesen und Senden von Daten, da es keine Verbindung zwischen einem Server und einem Client darstellt.
java.net. InetAddress importieren; java.net. ServerSocket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); } }
Schritt 5. Serverbeginn protokollieren
Drucken Sie zu Protokollierungszwecken an die Konsole, dass der Server gestartet wurde.
java.net. InetAddress importieren; java.net. ServerSocket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); } }
Schritt 6. Erstellen Sie einen Kunden
Der Client ist an die Adresse und den Port eines Servers gebunden und wartet nach dem Verbindungsaufbau auf Pakete (Nachrichten). Auf Java,
Steckdose
stellt entweder einen clientseitigen Endpunkt dar, der mit dem Server verbunden ist, oder eine Verbindung (vom Server) zum Client und wird verwendet, um mit dem Teilnehmer am anderen Ende zu kommunizieren.
java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); } }
Schritt 7. Verbindungsversuch protokollieren
Drucken Sie zu Protokollierungszwecken an die Konsole, dass eine Verbindung versucht wurde.
java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); } }
Schritt 8. Verbindung herstellen
Clients werden nie eine Verbindung herstellen, es sei denn, der Server lauscht und akzeptiert, d. h. stellt Verbindungen her. In Java werden Verbindungen hergestellt mit
annehmen()
Methode von
ServerSocket
Klasse. Die Methode blockiert die Ausführung, bis ein Client eine Verbindung herstellt.
java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); } }
Schritt 9. Protokollieren Sie die hergestellte Verbindung
Drucken Sie zu Protokollierungszwecken an die Konsole, dass die Verbindung zwischen Server und Client hergestellt wurde.
java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); } }
Schritt 10. Bereiten Sie Kommunikationsströme vor
Die Kommunikation erfolgt über Streams, und in dieser Anwendung müssen Rohdatenströme von (Verbindung vom) Server (zum Client) und Client entweder mit Daten- oder Objektströmen verkettet werden. Denken Sie daran, dass beide Parteien denselben Streamtyp verwenden müssen.
-
Datenströme
import java.io. DataInputStream; import java.io. DataOutputStream; java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); DataOutputStream clientOut = new DataOutputStream(client.getOutputStream()); DataInputStream clientIn = new DataInputStream(client.getInputStream()); DataOutputStream serverOut = new DataOutputStream(connection.getOutputStream()); DataInputStream serverIn = new DataInputStream(connection.getInputStream()); } }
-
Objektströme
Wenn mehrere Objektströme verwendet werden, müssen die Eingabeströme in der gleichen Reihenfolge wie die Ausgabeströme initialisiert werden, da
ObjectOutputStream
sendet einen Header an die andere Partei und
ObjectInputStream
blockiert die Ausführung, bis der Header gelesen wird.
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); ObjectOutputStream clientOut = new ObjectOutputStream(client.getOutputStream()); ObjectOutputStream serverOut = new ObjectOutputStream(connection.getOutputStream()); ObjectInputStream clientIn = new ObjectInputStream(client.getInputStream()); ObjectInputStream serverIn = new ObjectInputStream(connection.getInputStream()); } }
Die im obigen Code angegebene Reihenfolge ist möglicherweise einfacher zu merken - initialisieren Sie zuerst die Ausgabestreams und dann die Eingabestreams in derselben Reihenfolge. Eine andere Reihenfolge für die Initialisierung von Objektströmen ist jedoch die folgende:
ObjectOutputStream clientOut = new ObjectOutputStream(client.getOutputStream()); ObjectInputStream serverIn = new ObjectInputStream(connection.getInputStream()); ObjectOutputStream serverOut = new ObjectOutputStream(connection.getOutputStream()); ObjectInputStream clientIn = new ObjectInputStream(client.getInputStream());
Schritt 11. Protokollieren Sie, dass die Kommunikation bereit ist
Drucken Sie zu Protokollierungszwecken an die Konsole, dass die Kommunikation bereit ist.
// Code weggelassen import java.net. InetAddress; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); // Code weggelassen System.out.println("Kommunikation ist bereit."); } }
Schritt 12. Erstellen Sie eine Nachricht
In dieser Anwendung
Hallo Welt
Text wird an den Server gesendet entweder als
Byte
oder
Zeichenfolge
. Deklarieren Sie eine Variable des Typs, der vom verwendeten Stream abhängt. Verwenden
Byte
für Datenströme und
Zeichenfolge
für Objektströme.
-
Datenströme
Unter Verwendung von Datenströmen erfolgt die Serialisierung durch die Umwandlung von Objekten in primitive Datentypen oder a
Zeichenfolge
. In diesem Fall,
Zeichenfolge
wird umgewandelt in
Byte
statt geschrieben mit
writeBytes()
-Methode, um zu zeigen, wie dies mit anderen Objekten wie Bildern oder anderen Dateien geschehen würde.
import java.io. DataInputStream; import java.io. DataOutputStream; java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); DataOutputStream clientOut = new DataOutputStream(client.getOutputStream()); DataInputStream clientIn = new DataInputStream(client.getInputStream()); DataOutputStream serverOut = new DataOutputStream(connection.getOutputStream()); DataInputStream serverIn = new DataInputStream(connection.getInputStream()); System.out.println("Kommunikation ist bereit."); byte messageOut = "Hallo Welt".getBytes(); } }
-
Objektströme
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); ObjectOutputStream clientOut = new ObjectOutputStream(client.getOutputStream()); ObjectOutputStream serverOut = new ObjectOutputStream(connection.getOutputStream()); ObjectInputStream clientIn = new ObjectInputStream(client.getInputStream()); ObjectInputStream serverIn = new ObjectInputStream(connection.getInputStream()); System.out.println("Kommunikation ist bereit."); String messageOut = "Hallo Welt"; } }
Schritt 13. Senden Sie die Nachricht
Schreiben Sie Daten in den Ausgabestream und leeren Sie den Stream, um sicherzustellen, dass die Daten vollständig geschrieben wurden.
-
Datenströme
Die Länge einer Nachricht muss zuerst gesendet werden, damit die andere Partei weiß, wie viele Bytes sie lesen muss. Nachdem die Länge als primitiver Integer-Typ gesendet wurde, können Bytes gesendet werden.
import java.io. DataInputStream; import java.io. DataOutputStream; java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); DataOutputStream clientOut = new DataOutputStream(client.getOutputStream()); DataInputStream clientIn = new DataInputStream(client.getInputStream()); DataOutputStream serverOut = new DataOutputStream(connection.getOutputStream()); DataInputStream serverIn = new DataInputStream(connection.getInputStream()); System.out.println("Kommunikation ist bereit."); byte messageOut = "Hallo Welt".getBytes(); clientOut.writeInt(messageOut.length); clientOut.write (messageOut); clientOut.flush(); } }
-
Objektströme
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); ObjectOutputStream clientOut = new ObjectOutputStream(client.getOutputStream()); ObjectOutputStream serverOut = new ObjectOutputStream(connection.getOutputStream()); ObjectInputStream clientIn = new ObjectInputStream(client.getInputStream()); ObjectInputStream serverIn = new ObjectInputStream(connection.getInputStream()); System.out.println("Kommunikation ist bereit."); String messageOut = "Hallo Welt"; clientOut.writeObject(messageOut); clientOut.flush(); } }
Schritt 14. Gesendete Nachricht protokollieren
Drucken Sie zu Protokollierungszwecken an die Konsole, dass die Nachricht gesendet wurde.
-
Datenströme
import java.io. DataInputStream; import java.io. DataOutputStream; java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); DataOutputStream clientOut = new DataOutputStream(client.getOutputStream()); DataInputStream clientIn = new DataInputStream(client.getInputStream()); DataOutputStream serverOut = new DataOutputStream(connection.getOutputStream()); DataInputStream serverIn = new DataInputStream(connection.getInputStream()); System.out.println("Kommunikation ist bereit."); byte messageOut = "Hallo Welt".getBytes(); clientOut.writeInt(messageOut.length); clientOut.write (messageOut); clientOut.flush(); System.out.println("Nachricht an Server gesendet: " + new String(messageOut)); } }
-
Objektströme
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); ObjectOutputStream clientOut = new ObjectOutputStream(client.getOutputStream()); ObjectOutputStream serverOut = new ObjectOutputStream(connection.getOutputStream()); ObjectInputStream clientIn = new ObjectInputStream(client.getInputStream()); ObjectInputStream serverIn = new ObjectInputStream(connection.getInputStream()); System.out.println("Kommunikation ist bereit."); String messageOut = "Hallo Welt"; clientOut.writeObject(messageOut); clientOut.flush(); System.out.println("Nachricht an Server gesendet: " + messageOut); } }
Schritt 15. Lesen Sie die Nachricht
Lesen Sie Daten aus dem Eingabestream und konvertieren Sie sie. Da wir die Art der gesendeten Daten genau kennen, erstellen wir entweder eine
Zeichenfolge
von
Byte
oder werfen
Objekt
zu
Zeichenfolge
ohne Prüfung, je nach verwendetem Stream.
-
Datenströme
Da zuerst die Länge und danach die Bytes gesendet wurden, muss das Lesen in der gleichen Reihenfolge erfolgen. Falls length null ist, gibt es nichts zu lesen. Das Objekt wird deserialisiert, wenn Bytes zurück in eine Instanz konvertiert werden, in diesem Fall von
Zeichenfolge
import java.io. DataInputStream; import java.io. DataOutputStream; java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); DataOutputStream clientOut = new DataOutputStream(client.getOutputStream()); DataInputStream clientIn = new DataInputStream(client.getInputStream()); DataOutputStream serverOut = new DataOutputStream(connection.getOutputStream()); DataInputStream serverIn = new DataInputStream(connection.getInputStream()); System.out.println("Kommunikation ist bereit."); byte messageOut = "Hallo Welt".getBytes(); clientOut.writeInt(messageOut.length); clientOut.write (messageOut); clientOut.flush(); System.out.println("Nachricht an Server gesendet: " + new String(messageOut)); int-Länge = serverIn.readInt(); if (Länge > 0) { byte messageIn = new byte[length]; serverIn.readFully(messageIn, 0, messageIn.length); } } }
-
Objektströme
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); ObjectOutputStream clientOut = new ObjectOutputStream(client.getOutputStream()); ObjectOutputStream serverOut = new ObjectOutputStream(connection.getOutputStream()); ObjectInputStream clientIn = new ObjectInputStream(client.getInputStream()); ObjectInputStream serverIn = new ObjectInputStream(connection.getInputStream()); System.out.println("Kommunikation ist bereit."); String messageOut = "Hallo Welt"; clientOut.writeObject(messageOut); clientOut.flush(); System.out.println("Nachricht an Server gesendet: " + messageOut); String messageIn = (String) serverIn.readObject(); } }
Schritt 16. Gelesene Nachricht protokollieren
Drucken Sie zu Protokollierungszwecken die empfangene Nachricht und ihren Inhalt auf der Konsole aus.
-
Datenströme
import java.io. DataInputStream; import java.io. DataOutputStream; java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); DataOutputStream clientOut = new DataOutputStream(client.getOutputStream()); DataInputStream clientIn = new DataInputStream(client.getInputStream()); DataOutputStream serverOut = new DataOutputStream(connection.getOutputStream()); DataInputStream serverIn = new DataInputStream(connection.getInputStream()); System.out.println("Kommunikation ist bereit."); byte messageOut = "Hallo Welt".getBytes(); clientOut.writeInt(messageOut.length); clientOut.write (messageOut); clientOut.flush(); System.out.println("Nachricht an Server gesendet: " + new String(messageOut)); int-Länge = serverIn.readInt(); if (Länge > 0) { byte messageIn = new byte[length]; serverIn.readFully(messageIn, 0, messageIn.length); System.out.println("Nachricht vom Client empfangen: " + new String(messageIn)); } } }
-
Objektströme
import java.io. ObjectInputStream; import java.io. ObjectOutputStream; java.net. InetAddress importieren; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); ObjectOutputStream clientOut = new ObjectOutputStream(client.getOutputStream()); ObjectOutputStream serverOut = new ObjectOutputStream(connection.getOutputStream()); ObjectInputStream clientIn = new ObjectInputStream(client.getInputStream()); ObjectInputStream serverIn = new ObjectInputStream(connection.getInputStream()); System.out.println("Kommunikation ist bereit."); String messageOut = "Hallo Welt"; clientOut.writeObject(messageOut); clientOut.flush(); System.out.println("Nachricht an Server gesendet: " + messageOut); String messageIn = (String) serverIn.readObject(); System.out.println("Nachricht vom Client empfangen: " + messageIn); } }
Schritt 17. Verbindungen trennen
Die Verbindung wird getrennt, wenn eine Partei ihre Streams schließt. In Java werden durch Schließen des Ausgabestreams auch zugehörige Sockets und Eingabestreams geschlossen. Sobald eine Partei am anderen Ende feststellt, dass die Verbindung tot ist, muss sie auch ihren Ausgabestrom schließen, um Speicherlecks zu verhindern.
// Code weggelassen import java.net. InetAddress; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); // Code weggelassen System.out.println("Kommunikation ist bereit."); // Code weggelassen clientOut.close(); serverOut.close(); } }
Schritt 18. Verbindungstrennung protokollieren
Für Protokollierungszwecke wurden die Verbindungen zum Drucken mit der Konsole getrennt.
// Code weggelassen import java.net. InetAddress; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); // Code weggelassen System.out.println("Kommunikation ist bereit."); // Code weggelassen clientOut.close(); serverOut.close(); System.out.println("Verbindungen geschlossen."); } }
Schritt 19. Server beenden
Die Verbindungen werden getrennt, aber der Server läuft noch. Wie
ServerSocket
mit keinem Stream verknüpft ist, muss er explizit durch Aufrufen geschlossen werden
nah dran()
Methode.
// Code weggelassen import java.net. InetAddress; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); // Code weggelassen System.out.println("Kommunikation ist bereit."); // Code weggelassen clientOut.close(); serverOut.close(); System.out.println("Verbindungen geschlossen."); server.close(); } }
Schritt 20. Serverbeendigung protokollieren
Für Protokollierungszwecke wurde der Druck auf den Konsolenserver beendet.
// Code weggelassen import java.net. InetAddress; java.net. ServerSocket importieren; java.net. Socket importieren; public class NetworkAppExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; ServerSocket-Server = neuer ServerSocket(Port, 50, InetAddress.getByName(Host)); System.out.println("Server gestartet."); Socket-Client = neuer Socket (Host, Port); System.out.println("Verbindung zum Server…"); Socket-Verbindung = server.accept(); System.out.println("Verbindung hergestellt."); // Code weggelassen System.out.println("Kommunikation ist bereit."); // Code weggelassen clientOut.close(); serverOut.close(); System.out.println("Verbindungen geschlossen."); server.close(); System.out.println("Server beendet."); } }
Schritt 21. Kompilieren und ausführen
Durch die Protokollierung konnten wir feststellen, ob die Bewerbung erfolgreich war oder nicht. Erwartete Ausgabe:
Server gestartet. Verbindung zum Server wird hergestellt… Verbindung hergestellt. Die Kommunikation ist bereit. Nachricht an Server gesendet: Hello World Nachricht vom Client empfangen: Hello World Verbindungen geschlossen. Server beendet.
Falls Ihre Ausgabe nicht der obigen entspricht, was unwahrscheinlich ist, gibt es einige Lösungen:
-
Wenn die Ausgabe an der Linie stoppt
Verbindung hergestellt.
und Objektströme verwendet werden, spülen Sie jeden
ObjectOutputStream
- unmittelbar nach der Initialisierung, da Header aus irgendeinem Grund nicht gesendet wurden.
-
Wenn die Ausgabe gedruckt wird
java.net. BindException: Adresse wird bereits verwendet
- wählen Sie eine andere Portnummer, da die angegebene bereits verwendet wird.
Tipps
- Die Verbindung zu einem Server in einem anderen Netzwerk wird hergestellt, indem eine Verbindung zur externen IP-Adresse eines Geräts hergestellt wird, auf dem der Server mit einem weitergeleiteten Port ausgeführt wird.
- Die Verbindung zu einem Server im selben Netzwerk erfolgt entweder durch Verbinden mit der privaten IP-Adresse eines Geräts, auf dem der Server ausgeführt wird, oder durch Weiterleiten eines Ports und Verbinden mit der externen IP-Adresse des Geräts.
- Es gibt Software wie Hamachi, die eine Verbindung mit dem Server in einem anderen Netzwerk ohne Weiterleitung eines Ports ermöglicht, jedoch erfordert dies die Installation der Software auf beiden Geräten.
Beispiele
Netzwerkanwendungen, die blockierende Eingabe/Ausgabe verwenden, müssen Threads verwenden. Die folgenden Beispiele zeigen eine minimalistische Server- und Clientimplementierung mit Threads. Der Netzwerkcode ist im Wesentlichen der gleiche wie im Artikel, außer dass einige Schnipsel synchronisiert, in Threads verschoben und Ausnahmen behandelt werden.
Server.java
import java.io. IOException; java.net. InetAddress importieren; java.net. ServerSocket importieren; import java.net. SocketException; import java.net. UnknownHostException; import java.util. ArrayList; java.util. Collections importieren; java.util. List importieren; /** * Die Klasse {@code Server} repräsentiert einen Server-Endpunkt in einem Netzwerk. Sobald {@code Server} an eine bestimmte IP * Adresse und einen Port gebunden ist, stellt er Verbindungen zu Clients her und kann mit ihnen kommunizieren oder sie trennen. *
* Diese Klasse ist threadsicher. * * @version 1.0 * @see Client * @see Connection */ public class Server implementiert Runnable { private ServerSocket server; private Liste
Verbindungen; privater Thread-Thread; privates finales Objekt connectionLock = new Object(); /** * Konstruiert einen {@code Server}, der mit Clients auf dem angegebenen Hostnamen und Port mit der angegebenen * angeforderten maximalen Länge einer Warteschlange eingehender Clients interagiert. * * @param host Zu verwendende Hostadresse. * @param port Zu verwendende Portnummer. * @param backlog Angeforderte maximale Länge der Warteschlange eingehender Clients. * @throws NetworkException Wenn beim Starten eines Servers ein Fehler auftritt. */ public Server(String host, int port, int backlog) wirft NetworkException { try { server = new ServerSocket(port, backlog, InetAddress.getByName(host)); } catch (UnknownHostException e) { throw new NetworkException("Hostname konnte nicht aufgelöst werden: " + host, e); } catch (IllegalArgumentException e) { throw new NetworkException("Portnummer muss zwischen 0 und 65535 (einschließlich): " + port); } catch (IOException e) { throw new NetworkException("Server konnte nicht gestartet werden.", e); } Verbindungen = Collections.synchronizedList(new ArrayList()); Thread = neuer Thread (dieser); thread.start(); } /** * Konstruiert einen {@code Server}, der mit Clients auf dem angegebenen Hostnamen und Port interagiert. * * @param host Hostadresse zum Binden. * @param port Portnummer zum Binden. * @throws NetworkException Wenn beim Starten eines Servers Fehler auftreten. */ public Server(String host, int port) wirft NetworkException { this(host, port, 50); } /** * Hört auf eingehende Verbindungen von Clients, nimmt sie an und registriert sie. */ @Override public void run() { while (!server.isClosed()) { try { connections.add(new Connection(server.accept())); } catch (SocketException e) { if (!e.getMessage().equals("Socket geschlossen")) { e.printStackTrace(); } } catch (NetworkException | IOException e) { e.printStackTrace(); } } } /** * Sendet Daten an alle registrierten Clients. * * @param data Zu sendende Daten. * @throws IllegalStateException Wenn versucht wird, Daten zu schreiben, während der Server offline ist. * @throws IllegalArgumentException Wenn die zu sendenden Daten null sind. */ public void Broadcast(Object data) { if (server.isClosed()) { throw new IllegalStateException("Daten nicht gesendet, Server ist offline."); } if (data == null) { throw new IllegalArgumentException("null data"); } synchronisiert (connectionsLock) { for (Connection connection: connections) { try { connection.send(data); System.out.println("Daten erfolgreich an Client gesendet."); } catch (NetworkException e) { e.printStackTrace(); } } } } /** * Sendet eine Trennungsnachricht und trennt den angegebenen Client. * * @param connection Client zum Trennen. * @throws NetworkException Wenn beim Schließen der Verbindung ein Fehler auftritt. */ public void Disconnect(Connection connection) wirft NetworkException { if (connections.remove(connection)) { connection.close(); } } /** * Sendet eine Trennungsnachricht an alle Clients, trennt sie und beendet den Server. */ public void close() wirft NetworkException { synchronisiert (connectionsLock) { for (Connection connection: connections) { try { connection.close(); } catch (NetworkException e) { e.printStackTrace(); } } } Verbindungen.clear(); versuchen {server.close(); } catch (IOException e) { throw new NetworkException("Fehler beim Schließen des Servers."); } endlich { thread.interrupt(); } } /** * Gibt zurück, ob der Server online ist oder nicht. * * @return True, wenn der Server online ist. Falsch, sonst. */ public boolean isOnline() { return !server.isClosed(); } /** * Gibt ein Array registrierter Clients zurück. */ public Connection getConnections() { synchronisiert (connectionsLock) { return connections.toArray(new Connection[connections.size()]); } } }
Client.java
import java.io. IOException; java.net. Socket importieren; import java.net. UnknownHostException; /** * Die Klasse {@code Client} repräsentiert einen Client-Endpunkt in einem Netzwerk. {@code Client}, sobald eine Verbindung zu einem bestimmten * Server besteht, kann garantiert nur mit dem Server kommunizieren. Ob andere Clients die Daten* erhalten, hängt von der Serverimplementierung ab. *
* Diese Klasse ist threadsicher. * * @version 1.0 * @see Server * @see Connection */ public class Client { private Connection connection; /** * Konstruiert einen {@code Client}, der mit dem Server auf dem angegebenen Host und Port verbunden ist. * * @param host Hostadresse zum Binden. * @param port Portnummer zum Binden. * @throws NetworkException Wenn beim Starten eines Servers ein Fehler auftritt. */ public Client(String host, int port) wirft NetworkException { try { connection = new Connection(new Socket(host, port)); } catch (UnknownHostException e) { throw new NetworkException("Hostname konnte nicht aufgelöst werden: " + host, e); } catch (IllegalArgumentException e) { throw new NetworkException("Portnummer muss zwischen 0 und 65535 (einschließlich) liegen: " + port); } catch (IOException e) { throw new NetworkException("Server konnte nicht gestartet werden.", e); } } /** * Sendet Daten an die andere Partei. * * @param data Zu sendende Daten. * @throws NetworkException Wenn das Schreiben in den Ausgabestream fehlschlägt. * @throws IllegalStateException Wenn versucht wird, Daten zu schreiben, während die Verbindung geschlossen wird. * @throws IllegalArgumentException Wenn die zu sendenden Daten null sind. * @throws UnsupportedOperationException Wenn versucht wird, einen nicht unterstützten Datentyp zu senden. */ public void send(Object data) wirft NetworkException { connection.send(data); } /** * Sendet eine Trennungsnachricht an den Server und schließt die Verbindung mit ihm. */ public void close() wirft NetworkException { connection.close(); } /** * Gibt zurück, ob der Client mit dem Server verbunden ist oder nicht. * * @return True, wenn Client verbunden ist. Falsch, sonst. */ public boolean isOnline() { return connection.isConnected(); } /** * Gibt die {@link Connection}-Instanz des Clients zurück. */ öffentliche Verbindung getConnection() { Verbindung zurückgeben; } }
Verbindung.java
import java.io. DataInputStream; import java.io. DataOutputStream; import java.io. IOException; java.net. Socket importieren; import java.net. SocketException; /** * Die Klasse {@code Connection} repräsentiert entweder eine Verbindung vom Server zum Client oder einen Client-Endpunkt in einem Netzwerk * {@code Connection} kann, sobald eine Verbindung besteht, Daten mit anderen Teilnehmern austauschen, je nachdem auf einem Server *-Implementierung. *
* Diese Klasse ist threadsicher. * * @version 1.0 * @see Server * @see Client */ public class Connection implementiert Runnable { private Socket socket; privater DataOutputStream-Ausgang; privater DataInputStream in; privater Thread-Thread; privates finales Objekt writeLock = new Object(); privates finales Objekt readLock = new Object(); /** * Konstruiert {@code Connection} unter Verwendung von Streams eines angegebenen {@link Socket}. * * @param socket Socket zum Abrufen der Streams.*/ public Connection(Socket socket) throws NetworkException { if (socket == null) { throw new IllegalArgumentException("null socket"); } this.socket = Socket; try { out = new DataOutputStream(socket.getOutputStream()); } catch (IOException e) { throw new NetworkException("Konnte nicht auf Ausgabestream zugreifen.", e); } try { in = new DataInputStream(socket.getInputStream()); } catch (IOException e) { throw new NetworkException("Konnte nicht auf Eingabestream zugreifen.", e); } Thread = neuer Thread (dieser); thread.start(); } /** * Liest Nachrichten, während die Verbindung mit der anderen Partei besteht. */ @Override public void run() { while (!socket.isClosed()) { try { int bezeichner; Byte Byte; synchronisiert (readLock) { Bezeichner = in.readInt(); int-Länge = in.readInt(); if (Länge > 0) { Byte = neues Byte [Länge]; in.readFully(Bytes, 0, Bytes. Länge); } sonst { weiter; } } switch (identifier) { case Identifier. INTERNAL: String-Befehl = new String (Bytes); if (command.equals("disconnect")) { if (!socket.isClosed()) { System.out.println("Disconnection package Received."); versuchen { schließen(); } catch (NetworkException e) { return; } } } brechen; case Identifier. TEXT: System.out.println("Nachricht empfangen: " + new String(Bytes)); brechen; default: System.out.println("Unerkannte Daten empfangen."); } } catch (SocketException e) { if (!e.getMessage().equals("Socket geschlossen")) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); } } } /** * Sendet Daten an den anderen Teilnehmer. * * @param data Zu sendende Daten. * @throws NetworkException Wenn das Schreiben in den Ausgabestream fehlschlägt. * @throws IllegalStateException Wenn versucht wird, Daten zu schreiben, während die Verbindung geschlossen wird. * @throws IllegalArgumentException Wenn die zu sendenden Daten null sind. * @throws UnsupportedOperationException Wenn versucht wird, einen nicht unterstützten Datentyp zu senden. */ public void send(Object data) throws NetworkException { if (socket.isClosed()) { throw new IllegalStateException("Daten nicht gesendet, Verbindung geschlossen."); } if (data == null) { throw new IllegalArgumentException("null data"); } int-Kennung; Byte Byte; if (Dateninstanz von String) { Bezeichner = Bezeichner. TEXT; bytes = ((String) Daten).getBytes(); } else { throw new UnsupportedOperationException("Nicht unterstützter Datentyp: " + data.getClass()); } try { synchronisiert (writeLock) { out.writeInt (identifier); out.writeInt (Bytes. Länge); out.write (Bytes); out.flush(); } } catch (IOException e) { throw new NetworkException("Daten konnten nicht gesendet werden.", e); } } /** * Sendet eine Trennungsnachricht an den anderen Teilnehmer und schließt die Verbindung mit ihm. */ public void close() throws NetworkException { if (socket.isClosed()) { throw new IllegalStateException("Verbindung ist bereits geschlossen."); } try { byte message = "disconnect".getBytes(); synchronisiert (writeLock) { out.writeInt (Identifier. INTERNAL); out.writeInt (Nachricht. Länge); out.write (Nachricht); out.flush(); } } catch (IOException e) { System.out.println("Disconnection message konnte nicht gesendet werden."); } try { synchronisiert (writeLock) { out.close(); } } catch (IOException e) { throw new NetworkException("Fehler beim Schließen der Verbindung.", e); } endlich { thread.interrupt(); } } /** * Gibt zurück, ob die Verbindung zum anderen Teilnehmer aktiv ist oder nicht. * * @return True, wenn die Verbindung aktiv ist. Falsch, sonst. */ public boolean isConnected() { return !socket.isClosed(); } }
Identifier.java
/** * Die Klasse {@code Identifier} enthält Konstanten, die von {@link Connection} zum Serialisieren und Deserialisieren der * über das Netzwerk gesendeten Daten verwendet werden. * * @version 1.0 * @see Connection */ public final class Identifier { /** * Identifier für interne Nachrichten. */ public static final int INTERNAL = 1; /** * Bezeichner für Textnachrichten. */ public static final int TEXT = 2; }
NetworkException.java
/** * Die Klasse {@code NetworkException} zeigt einen Fehler im Zusammenhang mit dem Netzwerk an. */ public class NetworkException erweitert Exception { /** * Konstruiert eine {@code NetworkException} mit {@code null} als Nachricht. */ public NetworkException() { } /** * Konstruiert eine {@code NetworkException} mit der angegebenen Nachricht. * * @param Nachricht Eine Nachricht zur Fehlerbeschreibung. */ public NetworkException(String-Nachricht) { super(Nachricht); } /** * Konstruiert eine {@code NetworkException} mit der angegebenen Nachricht und Ursache. * * @param Nachricht Eine Nachricht zur Fehlerbeschreibung. * @param Ursache Eine Fehlerursache. */ public NetworkException(String-Nachricht, Throwable Cause) { super(Nachricht, Ursache); } /** * Konstruiert eine {@code NetworkException} mit der angegebenen Ursache. * * @param Ursache Eine Fehlerursache. */ public NetworkException(auslösebarer Grund) { super(Ursache); } }
Verwendungsbeispiel.java
/** * Die Klasse {@code UsageExample} zeigt die Nutzung von {@link Server} und {@link Client}. In diesem Beispiel wird * {@link Thread#sleep(long)} verwendet, um sicherzustellen, dass jedes Segment ausgeführt wird, da ein schnelles Starten und Schließen dazu führt, dass einige * Segmente nicht ausgeführt werden. * * @version 1.0 * @see Server * @see Client */ public class UsageExample { public static void main(String args) wirft Ausnahme { String host = "localhost"; int-Port = 10430; Serverserver = neuer Server (Host, Port); Client-Client = neuer Client (Host, Port); Thread.sleep (100L); client.send("Hallo."); server.broadcast("Hey, Kerl!"); Thread.sleep (100L); server.disconnect(server.getConnections()[0]); // oder client.close() zum Trennen vom clientseitigen server.close(); } }