In der Programmierung unter Unix sind Sockets ein übliches Mittel zur Herstellung von Kommunikationsverbindungen zwischen Prozessen auf möglicherweise verschiedenen Rechnern.
Sockets abstrahieren dabei von dem konkreten Übertragungsprotokoll (TCP/IP, UDP/IP), der Programmierer muss sich lediglich an einen bestimmten Ablauf beim Verbindungsauf- und -abbau halten.
Die wichtigsten Klassen sind InetAddress (Internet-Adresse), ServerSocket (Socket für den Server in einer Client-Server-Verbindung), Socket (Socket für den Client) und URL (Repräsentation eines URL-Objekts).
In einer TCP-Verbindung übernimmt ein Prozess die Rolle des Servers und ein anderer die Rolle des Clients. Der Server öffnet einen Socket (open), bindet eine lokale Adresse (bind), spezifiziert die Anzahl der gleichzeitig zu bearbeitenden Clients (listen) und wartet dann auf Clients (select und accept). Ein Client öffnet ebenfalls einen Socket (open) und verbindet diesen dann mit der Adresse des Servers (connect). Im Erfolgsfall ist nach diesem Procedere zwischen Server und Client eine Verbindung hergestellt, über die sie Nachrichten austauschen können.
public final class ServerSocket extends Object
Konstruktoren
public ServerSocket( int port ) throws IOException
// Lokaler Port, unter dem der Server zu erreichen ist.
public ServerSocket( int port, int count ) throws IOException
// Anzahl akzeptierter Clients.
Auswahl der Methoden
public InetAddress getInetAddress()
// eigene Internet-Adresse
public int getLocalPort()
// eigener Port
public Socket accept() throws IOException
// Akzeptieren von Clients
public void close() throws IOException
// Schliessen der Verbindung
Ein Objekt der Klasse ServerSocket wird in einer Endlosschleife mit accept() auf Clients warten. Meldet sich ein Client an, so kann der Server den von accept() zurückgegebenen Socket zum Nachrichtenaustausch mit dem Client verwenden und dessen Anforderung bearbeiten. Für die Bearbeitung startet der Server im allgemeinen einen eigenen Thread, damit er gleichzeitig auf weitere Clients warten kann.
Die Klasse Socket definiert die eigentliche Übertragungsfunktionalität, indem sie Ein- und Ausgabeströme nach aussen reicht.
public final class Socket extends Object
Auswahl der Konstruktoren
public Socket( String host, int port )
throws UnknownHostException, IOException
// Verbindungsaufbau zu einem Server auf dem Rechner host am Port port
public Socket( InetAddress address, int port ) throws IOException
// Verbindungsaufbau über die Internet-Adresse
Auswahl der Methoden
public InetAddress getInetAddress()
// Internet-Adresse des Servers
public int getPort()
// Port des Servers
public int getLocalPort()
// eigener Port
public InputStream getInputStream() throws IOException
// Eingabestrom für die Datenübertragung
public OutputStream getOutputStream() throws IOException
// Ausgabestrom für die Datenübertragung
public synchronized void close() throws IOException
// Verbindungsabbau
In dem kleinen Beispiel sendet der Server jede vom Client erhaltene Zeile umgehend gespiegelt zurück. Er nutzt dazu die Klasse jf.kapitel3.abschnitt4.StringMirror.
public class EchoServer {
public static void main ( String[] args ) {
new EchoServer().start();
}
public void start() {
ServerSocket serverSocket = null;
Socket clientSocket = null;
try {
serverSocket = new ServerSocket( 5574 );
while (( clientSocket = serverSocket.accept() ) != null )
( new EchoService( clientSocket )).start();
}
Ein Objekt der Klasse EchoService liest Bytes vom Client, wandelt sie in eine Zeichenkette, vertauscht die Reihenfolge der Buchstaben und sendet das Ergebnis als Bytes zurück.
class EchoService extends Thread {
private byte[] buffer = new byte[ 1024 ];
private int readLength = 0;
private Socket socket = null;
private String line = null;
public EchoService( Socket socket ) { this.socket = socket; }
public void run() {
try {
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();
while (( readLength = in.read( buffer )) > 0 ) {
line = new String( buffer, 0, readLength );
line = StringMirror.doMirror( line );
out.write( line.getBytes(), 0, line.length() );
}
}
Der gerade akzeptierte Client wird in einem parallelen Thread bedient. Der Server kann durch die
Verwendung von Threads also mehrere Clients gleichzeitig bearbeiten.
An dieser Stelle sei noch einmal darauf verwiesen, dass Applets in NetScape nur Socket-Verbindungen zu dem Rechner aufbauen dürfen, von dem die Seite geladen wurde.
Der folgende Client funktioniert also nur, wenn der Echo-Server auf demselben Rechner gestartet wird, von dem die HTML-Seite stammt.
Unter linux sind sockets im Normalfall nicht offen. Die Beispiele funktionieren nur, wenn man sie als Superuser startet.
// Datei jf/kapitel5/abschnitt4/EchoClientApplet.java
public class EchoClientApplet extends Applet
implements ActionListener {
private Socket socket = null;
private TextField iTF = null, oTF = null;
private Button button = null;
private InputStream in = null;
private OutputStream out = null;
private String host = null;
private byte[] buffer = new byte[ 1024 ];
public void init() {
Panel bPanel = new Panel();
Panel tPanel = new Panel();
bPanel.setLayout( new GridLayout( 2, 1 ));
bPanel.add( ( button = new Button( \"Sende\" )));
button.addActionListener( this );
bPanel.add( ( new Label( \"Empfangen:\" )));
tPanel.setLayout( new GridLayout( 2, 1 ));
tPanel.add( ( iTF = new TextField( 30 )));
iTF.addActionListener( this );
tPanel.add( ( oTF = new TextField( 30 )));
setLayout( new FlowLayout() );
add( bPanel ); add( tPanel );
Das Applet kann nur Verbindungen zum Rechner der eigenen DocumentBase aufbauen, deshalb
wird host auf diese Weise initialisiert.
host = getDocumentBase().getHost();
if ( host.equals( \"\" )) host = \"localhost\";
}
public void start() {
if ( socket == null )
try {
socket = new Socket( host, 5574 );
in = socket.getInputStream();
out = socket.getOutputStream();
}
catch ( UnknownHostException e ) {
oTF.setText( \"unbekannter Rechner\" );
}
catch ( IOException e ) {
oTF.setText( \"IO-Fehler \" +e );
}
}
public void stop() {
if ( socket != null )
try { socket.close();}
catch ( IOException e ) {}
socket = null; in = null; out = null;
}
public void actionPerformed( ActionEvent actionEvent ) {
if ( socket != null ) {
try {
String line = iTF.getText();
int readLength = 0;
out.write( line.getBytes(), 0, line.length() );
readLength = in.read( buffer );
oTF.setText( new String( buffer, 0, readLength ));
|