Java Networking meistern: Robuste Netzwerkanwendungen mit java.net erstellen
Einführung in Network Programming
Das java.net-Paket der J2SE-APIs enthält eine Sammlung von Klassen und Interfaces, die die Low-Level-Kommunikationsdetails bereitstellen, sodass Sie Programme schreiben können, die sich auf die Lösung des eigentlichen Problems konzentrieren.
Das java.net-Paket bietet Unterstützung für die zwei gängigen Netzwerkprotokolle:
- TCP – TCP steht für Transmission Control Protocol, das eine zuverlässige Kommunikation zwischen zwei Applikationen ermöglicht. TCP wird typischerweise über das Internet Protocol verwendet, was als TCP/IP bezeichnet wird.
- UDP – UDP steht für User Datagram Protocol, ein verbindungsloses Protokoll, das den Datenaustausch in Paketen zwischen Applikationen ermöglicht.
Dieses Kapitel vermittelt ein gutes Verständnis der folgenden zwei Themen:
- Socket Programming – Dies ist das am weitesten verbreitete Konzept im Networking und wird sehr detailliert erklärt.
- URL Processing – Dies wird separat behandelt. Klicken Sie hier, um mehr über URL Processing in Java zu erfahren.
Socket Programming
Sockets stellen den Kommunikationsmechanismus zwischen zwei Computern mithilfe von TCP bereit. Ein Client-Programm erstellt einen Socket an seinem Ende der Kommunikation und versucht, diesen Socket mit einem Server zu verbinden.
Wenn die Verbindung hergestellt ist, erstellt der Server ein Socket-Objekt an seinem Ende der Kommunikation. Der Client und der Server können nun kommunizieren, indem sie in den Socket schreiben und daraus lesen.
Die Klasse java.net.Socket repräsentiert einen Socket, und die Klasse java.net.ServerSocket bietet einen Mechanismus für das Serverprogramm, um auf Clients zu lauschen und Verbindungen mit ihnen herzustellen.
Die folgenden Schritte treten auf, wenn eine TCP-Verbindung zwischen zwei Computern mithilfe von Sockets hergestellt wird:
- Der Server instanziiert ein
ServerSocket-Objekt, das angibt, an welchem Portnummer die Kommunikation stattfinden soll. - Der Server ruft die
accept()-Methode der ServerSocket-Klasse auf. Diese Methode wartet, bis ein Client sich auf dem angegebenen Port mit dem Server verbindet. - Nachdem der Server wartet, instanziiert ein Client ein
Socket-Objekt, wobei der Servername und die Portnummer angegeben werden, zu der verbunden werden soll. - Der Konstruktor der Socket-Klasse versucht, den Client mit dem angegebenen Server und der Portnummer zu verbinden. Wenn die Kommunikation hergestellt ist, hat der Client nun ein Socket-Objekt, das mit dem Server kommunizieren kann.
- Auf der Serverseite gibt die
accept()-Methode eine Referenz auf einen neuen Socket auf dem Server zurück, der mit dem Socket des Clients verbunden ist.
Nachdem die Verbindungen hergestellt sind, kann die Kommunikation mithilfe von I/O-Streams erfolgen. Jeder Socket hat sowohl einen OutputStream als auch einen InputStream. Der OutputStream des Clients ist mit dem InputStream des Servers verbunden, und der InputStream des Clients ist mit dem OutputStream des Servers verbunden.
TCP ist ein Zweiwege-Kommunikationsprotokoll, daher können Daten gleichzeitig über beide Streams gesendet werden. Folgende nützliche Klassen bieten einen vollständigen Satz von Methoden zur Implementierung von Sockets.
ServerSocket Klassenmethoden
Die Klasse java.net.ServerSocket wird von Server-Applikationen verwendet, um einen Port zu erhalten und auf Client-Anfragen zu lauschen.
Die ServerSocket-Klasse hat vier Konstruktoren:
| Nr. | Methode & Beschreibung |
|---|---|
| 1 | public ServerSocket(int port) throws IOException – Versucht, einen Server-Socket an den angegebenen Port zu binden. Eine Exception tritt auf, wenn der Port bereits von einer anderen Applikation gebunden ist. |
| 2 | public ServerSocket(int port, int backlog) throws IOException – Ähnlich wie der vorherige Konstruktor; der backlog-Parameter spezifiziert, wie viele eingehende Clients in einer Warteschlange gespeichert werden sollen. |
| 3 | public ServerSocket(int port, int backlog, InetAddress address) throws IOException – Ähnlich wie der vorherige Konstruktor; der InetAddress-Parameter spezifiziert die lokale IP-Adresse, an die gebunden werden soll. InetAddress wird für Server verwendet, die mehrere IP-Adressen haben können, sodass der Server angeben kann, welche seiner IP-Adressen Client-Anfragen akzeptieren soll. |
| 4 | public ServerSocket() throws IOException – Erstellt einen ungebundenen Server-Socket. Wenn Sie diesen Konstruktor verwenden, nutzen Sie die bind()-Methode, wenn Sie bereit sind, den Server-Socket zu binden. |
Wenn der ServerSocket-Konstruktor keine Exception wirft, bedeutet dies, dass Ihre Applikation erfolgreich an den angegebenen Port gebunden wurde und für Client-Anfragen bereit ist.
Folgend sind einige der gängigen Methoden der ServerSocket-Klasse:
| Nr. | Methode & Beschreibung |
|---|---|
| 1 | public int getLocalPort() – Gibt den Port zurück, auf dem der Server-Socket lauscht. Diese Methode ist nützlich, wenn Sie 0 als Portnummer in einem Konstruktor übergeben haben und den Server einen Port für Sie finden ließen. |
| 2 | public Socket accept() throws IOException – Wartet auf einen eingehenden Client. Diese Methode blockiert, bis entweder ein Client sich auf dem angegebenen Port mit dem Server verbindet oder der Socket ein Timeout hat, vorausgesetzt, der Timeout-Wert wurde mit der setSoTimeout()-Methode gesetzt. Andernfalls blockiert diese Methode unendlich. |
| 3 | public void setSoTimeout(int timeout) – Setzt den Timeout-Wert dafür, wie lange der Server-Socket auf einen Client während des accept() wartet. |
| 4 | public void bind(SocketAddress host, int backlog) – Bindet den Socket an den angegebenen Server und Port im SocketAddress-Objekt. Verwenden Sie diese Methode, wenn Sie den ServerSocket mit dem no-argument-Konstruktor instanziiert haben. |
Wenn der ServerSocket accept() aufruft, gibt die Methode nicht zurück, bis ein Client sich verbindet. Nachdem ein Client sich verbunden hat, erstellt der ServerSocket einen neuen Socket an einem nicht spezifizierten Port und gibt eine Referenz auf diesen neuen Socket zurück. Eine TCP-Verbindung besteht nun zwischen Client und Server, und die Kommunikation kann beginnen.
Socket Klassenmethoden
Die Klasse java.net.Socket repräsentiert den Socket, den sowohl der Client als auch der Server verwenden, um miteinander zu kommunizieren. Der Client erhält ein Socket-Objekt, indem er eines instanziiert, während der Server ein Socket-Objekt vom Rückgabewert der accept()-Methode erhält.
Die Socket-Klasse hat fünf Konstruktoren, die ein Client verwendet, um sich mit einem Server zu verbinden:
| Nr. | Methode & Beschreibung |
|---|---|
| 1 | public Socket(String host, int port) throws UnknownHostException, IOException – Versucht, eine Verbindung zum angegebenen Server am angegebenen Port herzustellen. Wenn dieser Konstruktor keine Exception wirft, ist die Verbindung erfolgreich und der Client ist mit dem Server verbunden. |
| 2 | public Socket(InetAddress host, int port) throws IOException – Identisch zum vorherigen Konstruktor, außer dass der Host durch ein InetAddress-Objekt bezeichnet wird. |
| 3 | public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException – Verbindet mit dem angegebenen Host und Port, erstellt einen Socket auf dem lokalen Host an der angegebenen Adresse und Port. |
| 4 | public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException – Identisch zum vorherigen Konstruktor, außer dass der Host durch ein InetAddress-Objekt anstelle eines Strings bezeichnet wird. |
| 5 | public Socket() – Erstellt einen unverbundenen Socket. Verwenden Sie die connect()-Methode, um diesen Socket mit einem Server zu verbinden. |
Wenn der Socket-Konstruktor zurückkehrt, instanziiert er nicht einfach ein Socket-Objekt, sondern versucht tatsächlich, eine Verbindung zum angegebenen Server und Port herzustellen.
Einige interessante Methoden in der Socket-Klasse sind hier aufgelistet. Beachten Sie, dass sowohl der Client als auch der Server ein Socket-Objekt haben, sodass diese Methoden sowohl vom Client als auch vom Server aufgerufen werden können.
| Nr. | Methode & Beschreibung |
|---|---|
| 1 | public void connect(SocketAddress host, int timeout) throws IOException – Verbindet den Socket mit dem angegebenen Host. Diese Methode wird nur benötigt, wenn Sie den Socket mit dem no-argument-Konstruktor instanziieren. |
| 2 | public InetAddress getInetAddress() – Gibt die Adresse des anderen Computers zurück, mit dem dieser Socket verbunden ist. |
| 3 | public int getPort() – Gibt den Port zurück, an den der Socket auf dem entfernten Rechner gebunden ist. |
| 4 | public int getLocalPort() – Gibt den Port zurück, an den der Socket auf dem lokalen Rechner gebunden ist. |
| 5 | public SocketAddress getRemoteSocketAddress() – Gibt die Adresse des entfernten Sockets zurück. |
| 6 | public InputStream getInputStream() throws IOException – Gibt den Eingabestream des Sockets zurück. Der Eingabestream ist mit dem Ausgabestream des entfernten Sockets verbunden. |
| 7 | public OutputStream getOutputStream() throws IOException – Gibt den Ausgabestream des Sockets zurück. Der Ausgabestream ist mit dem Eingabestream des entfernten Sockets verbunden. |
| 8 | public void close() throws IOException – Schließt den Socket, wodurch dieses Socket-Objekt nicht mehr in der Lage ist, sich wieder mit einem Server zu verbinden. |
InetAddress Klassenmethoden
Diese Klasse repräsentiert eine Internet Protocol (IP) Adresse. Hier sind folgende nützliche Methoden, die Sie bei der Socket-Programmierung benötigen würden:
| Nr. | Methode & Beschreibung |
|---|---|
| 1 | static InetAddress getByAddress(byte[] addr) – Gibt ein InetAddress-Objekt bei gegebener roher IP-Adresse zurück. |
| 2 | static InetAddress getByAddress(String host, byte[] addr) – Erstellt ein InetAddress basierend auf dem bereitgestellten Hostnamen und der IP-Adresse. |
| 3 | static InetAddress getByName(String host) – Bestimmt die IP-Adresse eines Hosts, gegeben den Hostnamen. |
| 4 | String getHostAddress() – Gibt die IP-Adresse als String in textueller Darstellung zurück. |
| 5 | String getHostName() – Ermittelt den Hostnamen für diese IP-Adresse. |
| 6 | static InetAddress getLocalHost() – Gibt den lokalen Host zurück. |
| 7 | String toString() – Konvertiert diese IP-Adresse in einen String. |
Socket Client Beispiel
Das folgende GreetingClient ist ein Client-Programm, das sich über einen Socket mit einem Server verbindet, einen Gruß sendet und dann auf eine Antwort wartet.
Beispiel
// Dateiname: GreetingClient.java
import java.net.*;
import java.io.*;
public class GreetingClient {
public static void main(String[] args) {
String serverName = args[0];
int port = Integer.parseInt(args[1]);
try {
System.out.println("Connecting to " + serverName + " on port " + port);
Socket client = new Socket(serverName, port);
System.out.println("Just connected to " + client.getRemoteSocketAddress());
OutputStream outToServer = client.getOutputStream();
DataOutputStream out = new DataOutputStream(outToServer);
out.writeUTF("Hello from " + client.getLocalSocketAddress());
InputStream inFromServer = client.getInputStream();
DataInputStream in = new DataInputStream(inFromServer);
System.out.println("Server says " + in.readUTF());
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Socket Server Beispiel
Das folgende GreetingServer-Programm ist ein Beispiel für eine Server-Applikation, die die Socket-Klasse verwendet, um auf Clients an einer Portnummer zu lauschen, die als Kommandozeilenargument angegeben wird:
Beispiel
// Dateiname: GreetingServer.java
import java.net.*;
import java.io.*;
public class GreetingServer extends Thread {
private ServerSocket serverSocket;
public GreetingServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(10000);
}
public void run() {
while (true) {
try {
System.out.println("Waiting for client on port " + serverSocket.getLocalPort() + "...");
Socket server = serverSocket.accept();
System.out.println("Just connected to " + server.getRemoteSocketAddress());
DataInputStream in = new DataInputStream(server.getInputStream());
System.out.println(in.readUTF());
DataOutputStream out = new DataOutputStream(server.getOutputStream());
out.writeUTF("Thank you for connecting to " + server.getLocalSocketAddress() + "\nGoodbye!");
server.close();
} catch (SocketTimeoutException s) {
System.out.println("Socket timed out!");
break;
} catch (IOException e) {
e.printStackTrace();
break;
}
}
}
public static void main(String[] args) {
int port = Integer.parseInt(args[0]);
try {
Thread t = new GreetingServer(port);
t.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Kompilieren Sie Client und Server und starten Sie dann den Server wie folgt:
$ java GreetingServer 6066
Waiting for client on port 6066...
Testen Sie das Client-Programm wie folgt:
Ausgabe
$ java GreetingClient localhost 6066
Connecting to localhost on port 6066
Just connected to localhost/127.0.0.1:6066
Server says Thank you for connecting to /127.0.0.1:6066
Goodbye!
Java
- Java Operatoren meistern: Typen, Syntax und praktische Beispiele
- Java Kommentare: Typen, Verwendung und Best Practices
- Java Enhanced For-Schleife meistern: Syntax, Beispiele und Best Practices
- Java Strings meistern: Erstellung, Methoden und Best Practices
- Java-Schnittstellen meistern: Konzepte, Implementierung und Best Practices
- Java anonyme innere Klassen meistern: Definition, Syntax und praktische Beispiele
- Java Try-With-Resources meistern: Automatisches Ressourcenmanagement erklärt
- Java Annotations erklärt: Typen, Platzierung und praktische Beispiele
- Java Assertions: Praktischer Leitfaden zur Fehlererkennung und Code-Reliabilität
- Java Vector verstehen: Verwendung, Funktionen und Vergleich mit ArrayList