Alıstırma 4 - Da˘gıtık Sistemler Alıstırma 1

advertisement
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.
Download