3. Netzwerk-Programmierung


Mehrprozessorsysteme ohne gemeinsamen Hauptspeicher benötigen spezielle Leitungen zur Kommunikation.

Das Schema des Nachrichtenaustauschs ist in Abbildung 2 dargestellt.

Abbildung 2: Schema des Nachrichtenaustauschs


3.1 Sockets

Spezifikation von ServerSocket:

   public ServerSocket(int port) throws IOException 
   public Socket accept() throws IOException

Spezifikationen von Socket

   public Socket(String host, int port)
          throws UnknownHostException, IOException
   public InputStream getInputStream() throws IOException
   public OutputStream getOutputStream() throws IOException

Abbildung E: UML Socket
UML Socket

Zuordnung von passenden Datenströmen:

Spezifikation der benötigten Konstruktoren und Methoden

   public ObjectOutputStream(OutputStream out) throws IOException 
   public void flush() throws IOException
   public ObjectInputStream(InputStream in)
          throws IOException, StreamCorruptedException

Abbildung F: UML Serialization
UML Serialization

Datenübertragung mit Send-Operation ( send) und Empfangs-Operation ( recieve).

Spezifikation aus ObjectOutputStream

   public final void writeObject(Object obj)
          throws IOException

Spezifikation aus ObjectInputStream

   public final Object readObject()
          throws OptionalDataException, 
                 ClassNotFoundException, IOException

3.2 Beispiel ObjectChannel

Der Konstruktor nimmt einen Socket als Eingabe und setzt die Objekt-Ströme aus den entsprechenden Strömen auf.

Die Methode close() dient zum schließen des Kanals.

send()- und receive()-Methoden dienen der Datenübertragung

 public class ObjectChannel {

    ObjectOutputStream out;
    ObjectInputStream in;

    public ObjectChannel(Socket s) throws IOException {
        out = new ObjectOutputStream( s.getOutputStream() );
        in = new ObjectInputStream( s.getInputStream() );
    }

    public void close()	{
    	try {
            out.close();
            in.close();
    	} catch(IOException e) {
            e.printStackTrace();
    	}
    }

    public void send(Object o) throws IOException {
    	synchronized(out) {
            out.writeObject(o);
        }
    }

    public Object receive() throws IOException, ClassNotFoundException {
    	Object o = null;
    	synchronized(in) {
            o = in.readObject();
    	}
    	return o;
    }	
 }

Die Sende- und Empfangs-Methoden sind wie folgt.

     public void send(Object v) throws IOException {
         out.writeObject(v);
     }
Im Fehlerfall wird eine IOException ausgelöst, die dann vom Aufrufer behandelt werden muß.

     public Object receive() 
                   throws IOException, 
                          ClassNotFoundException {
       return in.readObject();
     }
Neben einer IOException kann auch noch eine ClassNotFoundException ausgelöst werden, falls das Objekt zu einer dem Empfänger unbekannten Klasse gehört.

3.2a Beispiel HelloWorld mit SocketChannel / ObjectChannel

 

ExCon

Abbildung A1:HelloWorld

Implementierung: HelloWorldClient.java HelloWorldServ.java SocketChannel.java ObjectChannel.java

3.3 Beispiel Kanalfabrik

Zwei Grundideen zur Lösung

Abbildung G: UML ChannelFactory
UML ChannelFactory

Der Konstruktor der Klasse ChannelFactory erzeugt einen Server-Socket und startet sich dann selbst als Thread.

   import java.io.*;
   import java.net.*;
   
   public class ChannelFactory extends Thread {
     
     public final static int DEFAULT_PORT = 4711;
     private int port;
   
     private BoundedBuffer buf = new BoundedBuffer(10);
   
     private ServerSocket srv = null;
   
     public ChannelFactory(int p) {
       if (p<=0) { port = DEFAULT_PORT; } 
          else { port = p; }
       try {
           srv = new ServerSocket(port);
           this.start();
           System.out.println(
                  "server started on port "+port);
       } catch (IOException e) 
               { System.out.println(e); }
     }
   
     public ChannelFactory() {
       this(DEFAULT_PORT); 
     }
     public void run() {
       while (true) {
         try {
             System.out.println(
                 "waiting for connection");
             Socket s = srv.accept();
             System.out.println("connection accepted");
             ObjectChannel c = new ObjectChannel(s);
             buf.put( (Object) c );
         } catch (IOException e) {
           System.out.println(e);
         }
       }
     }
   
     public ObjectChannel getChannel() 
            throws IOException {
       return (ObjectChannel)buf.get();
     }

     public ObjectChannel getChannel(String h, int p) 
            throws IOException {
       if (p<=0) { p = port; } 
       ObjectChannel c = null;
       System.out.println("connecting to "+h);
       while (c == null) {
           try { c = new ObjectChannel( 
                     new Socket(h, p) );
           } catch (IOException e) {
             System.out.println(e);
             // wait server ready
             System.out.println("Server on "+h+
                    " not ready");
             try {
                 Thread.currentThread().sleep(
                        (int)(5000));
             } catch (InterruptedException e1) { } 
           }
       }
       System.out.println("connected");
       return c;
     }
   }

ChannelFactory löst das Reihenfolge-Problem und das Deadlock-Problem.

Das Reihenfolge-Problem wird durch Warten und Neuversuch in der Methode getChannel(String h, int p) gelöst.

Die Lösung des Deadlock-Problems wie folgt.

Wir setzen voraus, daß für jeden Prozeß genau einmal der Konstruktor ChannelFactory() und für jeden Kanal genau einmal die Methode getChannel() und die Methode getChannel(String h, int p) aufgerufen wird.

Betrachten wir die drei Programmzustände:
  1. Aufruf der Konstruktoren ChannelFactory(). Damit wird ein Thread gestartet, der Verbindungsanfragen vom Server-Socket entgegennimmt und speichert.

  2. Aufruf von getChannel(String h, int p) auf allen Client-Seiten der Kanäle. Dabei wird die Verbindung zu Sockets aus Punkt 1 hergestellt. Falls diese noch nicht bereit sind, wird gewartet.

  3. Aufruf von allen getChannel() auf den Server-Seiten der Kanäle. Dabei werden nur noch die gespeicherten Verbindungen zurückgegeben. Falls noch keine gespeichert sind, wird gewartet.

Kompliziertere Verbindungsstrukturen mit virtuellen Kanälen oder PVM oder MPI.

3.3a Beispiel HelloWorld mit ChannelFactory

 

ExCon

Abbildung A1:HelloWorld

Implementierung: HelloWorldClientCF.java HelloWorldServCF.java ChannelFactory.java


© Universität Mannheim, Rechenzentrum, 2000-2014.

Last modified: Mon Jan 20 23:53:47 CET 2014