1 Prof. Dr. Th. Letschert Çeviri: Turgay Akbaş FB MNI 16. Mai 2013 Alıştırma 4 - Dağıtık Sistemler Alıştırma 1 Ağ Programlama ya da Soket Programlama kavramlarıyla dağıtık uygulamaların geliştirilmesi soketler üzerine ifade edilir. Soketler çeşitli işletim sistemleri tarafından önerilen tek tipli erişim noktaları olarak bilinirler. Soket katmanı ile iletişim protokolleri servislerine erişim mümkün kılınmıştır. Assembly ile kodlanmış hata işleyicileri temel alarak dağıtık uygulamalar yazanlar işletim sistemine rahat entegre oluşundan dolayı işleri kolaylaşmıştır. Sonralarda dağıtık programlama geliştirme zahmetliydi. Bundan dolayı gönderilen mesajı geri gönderen Yankı(Echo)Sunucu örneğini incelemeliyiz. Sunucu: package aufgabe_1; import import import import import java.io.IOException; java.net.DatagramPacket; java.net.DatagramSocket; java.net.InetAddress; java.net.SocketException; public class Server { private static final int bufferPort = 4711; public static void main(String[] args) throws SocketException { DatagramSocket dtgrmSocket = new DatagramSocket(bufferPort); byte[] buf = new byte[256]; DatagramPacket rcvpkt = new DatagramPacket(buf, buf.length); try { while (true) { dtgrmSocket.receive(rcvpkt); System.out.println( "Server hat Daten empfangen: " + new String(rcvpkt.getData(), 0, rcvpkt.getLength()) ); InetAddress clientAdr = rcvpkt.getAddress(); int clientPort = rcvpkt.getPort(); System.out.println("Von Rechner: " + clientAdr.getHostAddress() + " an Port: "+clientPort); DatagramPacket sndpkt = new DatagramPacket(buf, buf.length, clientAdr, clientPort); dtgrmSocket.send(sndpkt); } } catch (IOException e) { System.err.println("FEHLER "+e); System.exit(-1); } 2 dtgrmSocket.close(); } } İstemci: package aufgabe_1; import import import import import import java.io.IOException; java.net.DatagramPacket; java.net.DatagramSocket; java.net.InetAddress; java.net.SocketException; java.net.UnknownHostException; public class Client { public static void main(String[] args) { if (args.length != 2) { System.out.println("Aufruf: client <server> <port>"); System.exit(0); } String host = args[0]; InetAddress serverAddress = null; int port = Integer.valueOf(args[1]).intValue(); DatagramSocket dtgrmSocket = null; try { dtgrmSocket = new DatagramSocket(); } catch (SocketException e) { System.err.println("FEHLER: Kann Socket nicht erzeugen!"); System.exit(-1); } try { serverAddress = InetAddress.getByName(host); } catch (UnknownHostException e) { System.err.println(e); System.exit(-1); } String msg = "Hallo wer da?"; byte buf[] = msg.getBytes(); DatagramPacket pkt = new DatagramPacket( buf, buf.length, serverAddress, port); try { dtgrmSocket.send(pkt); } catch(IOException e) { System.err.println("Sende Fehler " + e); System.exit(-1); } try { dtgrmSocket.receive(pkt); } catch(IOException e) { System.err.println(e); System.exit(-1); } System.out.println("Client hat empfangen: " +new String(pkt.getData())); 3 } } 1. İstemci ve sunucuların farklı platformalar üzerinde çalışabildiklerini kabul ediniz. Örnek olarak bir Mac ve bir PC ve istemci karakter setlerini Non-ASCII formatında gönderiyor. Sunucu üzerindeki output ve istemciye geri gönderilen versiyon nasıl olur: Doğru ya da hasarlı(eksik,yanlış vb.)? Hasarlı ise: Bu nasıl önlenebilir? Alıştırma 2 CharToLine–Algoritması Alıştırmalar–2’de lokal olarak gerçekleştirilmeliydi. (Bütün süreçler aynı JVM içindeydi ve bundan dolayı Threadler kullanıldı.) Pseudocode–versiyonunda verilen çözüm Thread’ler yerine UDP–mesajları ile iletişim kuran işletim sistemi süreçleri olarak gerçekleştirilmedilir. Bu durumda bazı değişiklikler gereklidir. Kanal uygulaması gerçekleştirimi atlanabilir. Bunun yerine veriler UDP-Paketleri olarak gönderilmelidir. Serileştirme ile veri alımı ve gönderimi kolayca gerçekleştirilebilir. UDP iletişimi netlik için bir sınıf içine konmalıdır. Örnek gerçekleştirim: UDP Katmanı package aufgabe_2; import import import import import import import import import import import java.io.ByteArrayInputStream; java.io.ByteArrayOutputStream; java.io.IOException; java.io.ObjectInputStream; java.io.ObjectOutputStream; java.io.Serializable; java.net.DatagramPacket; java.net.DatagramSocket; java.net.InetAddress; java.net.SocketException; java.net.UnknownHostException; public class UDPLayer { private InetAddress remoteAdr; private int remotePort; private int localPort; private DatagramSocket dtgrmSocket; public UDPLayer (String localPort) throws SocketException { this.localPort = Integer.valueOf(localPort).intValue(); dtgrmSocket = new DatagramSocket(this.localPort); } public UDPLayer () throws SocketException { this.localPort = 0; dtgrmSocket = new DatagramSocket(); } public void setDestination (String host, String port) throws SocketException, UnknownHostException { this.remotePort = Integer.valueOf(port).intValue(); remoteAdr = InetAddress.getByName(host); } public void send(Serializable msg) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos; try { oos = new ObjectOutputStream(baos); 4 oos.writeObject(msg); oos.flush(); byte buf[] = baos.toByteArray(); DatagramPacket pkt = new DatagramPacket(buf, buf.length, remoteAdr, remotePort); dtgrmSocket.send(pkt); oos.close(); } catch (IOException e) { e.printStackTrace(); } } public Serializable receive() throws IOException, ClassNotFoundException { byte[] bufRcv = new byte[256]; DatagramPacket pktRcv = new DatagramPacket(bufRcv, bufRcv.length); dtgrmSocket.receive(pktRcv); ByteArrayInputStream bais = new ByteArrayInputStream(bufRcv); ObjectInputStream ois = new ObjectInputStream(bais); Serializable msg = (Serializable) ois.readObject(); bais.close(); ois.close(); return msg; } } Bununla veriler kolayca gönderilip alınabilir: Source: package aufgabe_2; import java.net.SocketException; import java.net.UnknownHostException; public class Source { public static void main(String[] args) throws SocketException, UnknownHostException { UDPLayer udpLayer = new UDPLayer(); udpLayer.setDestination("127.0.0.1", "4711"); int i = 0; char[] c = {’a’,’\n’,’ ’,’b’,’c’,’d’,’\n’,’ ’,’e’,’f’,’g’,’h’,’\n’,’ ’, ’i’,’j’,’k’,’l’,’m’,’n’,’\n’,’ ’,’o’,’p’,’q’,’r’,’s’,’t’,’u’,’\n’,’ ’, ’v’,’w’,’\n’,’ ’,’1’,’2’,’3’,’4’,’5’,’6’,’7’,’8’,’9’,’\n’,’0’}; while (true) { System.out.println("Source will send "+c[(i) % c.length]); udpLayer.send(c[(i++) % c.length]); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } 5 Sink: package aufgabe_2; import java.io.IOException; public class Sink { public static void main(String[] args) throws ClassNotFoundException, IOException { UDPLayer udpLayer = new UDPLayer("4712"); while(true) { System.out.println("Sink received " + udpLayer.receive()); } } } 1. CharToLine sürecini Source ve Sink’e uyacak şekilde ayarlayın. 2. Çözümünüzü test ediniz. Alıştırma 3 CharToLine–örneğini RMI yardımıyla gerçekleştiriniz. RMI ayrık metot çağırımları ile senkron iletişim üzerine kurulmuştur. Örnek metot çağırımları ile senkron iletişime adapte edilmelidir. 1. Source içinde CharToLine istemcisi ve CharToLine içinde bir Sink istemcisi olan ve dağıtık olmayan bir lokal değişken tanımlayınız. Örnek olarak Source ve Sink aşağıda gösterildiği gibi gerçekleştirilebilir: Source: package aufgabe_3_1; public class Source implements Runnable { private CharToLine charToLine; public Source(CharToLine charToLine) { this.charToLine = charToLine; } @Override public void run() { int i = 0; char[] c = {’a’,’\n’,’ ’,’b’,’c’,’d’,’\n’,’ ’,’e’,’f’,’g’,’h’,’\n’,’ ’, ’i’,’j’,’k’,’l’,’m’,’n’,’\n’,’ ’,’o’,’p’,’q’,’r’,’s’,’t’,’u’,’\n’,’ ’, ’v’,’w’,’\n’,’ ’,’1’,’2’,’3’,’4’,’5’,’6’,’7’,’8’,’9’,’\n’,’0’}; while (true) { charToLine.send(c[(i++) % c.length]); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } 6 } Sink: package aufgabe_3_1; public class Sink { public void send(String line) { System.out.println(line); } } CharToLine’ın gerçekleştirimini tamamlayınız ve test ediniz . Source neden aktiftir (Thread), Sink neden değildir? Açıklayınız! CharToLine ile nasıl uyum sağlarlar? 2. Bu versiyonu bir RMI–versiyonuna çeviriniz. Sink –Süreci ile başlayınız: Sink-Interface: package aufgabe_3_2; import java.rmi.Remote; import java.rmi.RemoteException; public interface Sink_I extends Remote { void send(String line) throws RemoteException; } Sink-Gerçekleştirim: package aufgabe_3_2; import import import import java.rmi.RemoteException; java.rmi.registry.LocateRegistry; java.rmi.registry.Registry; java.rmi.server.UnicastRemoteObject; public class SinkRemote implements Sink_I { public void send(String line) { System.out.println(line); } public static void main(String[] args) throws RemoteException { SinkRemote obj = new SinkRemote(); Sink_I stub = (Sink_I) UnicastRemoteObject.exportObject(obj, 0); Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT); registry.rebind("Sink", stub); System.out.println("Sink ready"); } } İlk olarak Sink ’i başlatınız. Bu işlemle Registry yaratılır. Bu CharToLine sürecinden kullanılabilir, sonrasında kendini kayıt(register) etmelidir. CharToLine-Gerçekleştirim: package aufgabe_3_2; import java.net.MalformedURLException; 7 import import import import import import import java.rmi.Naming; java.rmi.NotBoundException; java.rmi.RemoteException; java.rmi.registry.LocateRegistry; java.rmi.registry.Registry; java.rmi.server.UnicastRemoteObject; java.util.concurrent.LinkedBlockingDeque; public class CharToLineRemote implements CharToLine_I, Runnable { private LinkedBlockingDeque<Character> mailbox = new LinkedBlockingDeque<>(); private static final int MAXLINE = 5; @Override public void send(char c) throws RemoteException { try { mailbox.put(c); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void run() { try { Sink_I sink = (Sink_I) Naming.lookup("rmi://127.0.0.1/Sink"); // ????? } catch (InterruptedException | MalformedURLException | RemoteException | NotBoundException e) { e.printStackTrace(); } } public static void main(String[] args) throws RemoteException { CharToLineRemote obj = new CharToLineRemote(); CharToLine_I stub = (CharToLine_I) UnicastRemoteObject.exportObject(obj, 0); Registry registry = LocateRegistry.getRegistry(); registry.rebind("CharToLine", stub); new Thread(obj).start(); System.out.println("CharToLine ready"); } } CharToLineRemote için eksik olan kısımları tamamlayınız ve Source bileşenlerini gerçekleştiriniz. Sistemin tamamını test ediniz.