Laboratorium 6
Cel:
Obsługa operacji wejścia/wyjścia
Serializacja obiektów
Komunikacja sieciowa
Obsługa operacji wejścia / wyjścia
Do obsługi operacji IO w javie wykorzystywane są klasy z pakietu java.io. Praktycznie większość wołanych metod jest narażona na wygenerowanie sytuacji wyjątkowej, wobec czego operacje te muszą być wykonywane w bloku chronionym try/catch.
Jako przykład wykorzystania strumieni i korzystania z systemu plików można wykorzystać aplikację edytora tekstowego tworzoną w ramach ćwiczenia 4. Operacje dotyczące zapisywania zawartości tekstowej edytora i odczytywania plików nie były wówczas implementowane.
Aby zrealizować operację zapisywania i odczytywania strumieni tekstowych na dysk można posłużyć się poniższym przykładem.
import java.io.*;
public class Test {
public void save(String filename, String text) throws IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter(filename));
bw.write(text, 0, text.length());
bw.close();
}
public String load(String filename) throws IOException {
BufferedReader br = new BufferedReader(new FileReader(filename));
StringBuffer buffer = new StringBuffer();
String line = null;
while ((line = br.readLine()) != null) {
buffer.append(line);
buffer.append(System.getProperty("line.separator"));
}
br.close();
return buffer.toString();
}
public static void main(String args[]) {
Test test = new Test();
try {
test.save("test.txt", "przykładowy plik tekstowy");
System.out.println(test.load("test.txt"));
}
catch (IOException e) {
e.printStackTrace();
}
}
}
Serializacja obiektów
Język java daje programiście niezwykle wygodny sposób składowania stanu wybranego obiektu. Łatwo sobie wyobrazić złożoność problemu zapisania/odczytania złożonej struktury danych występujących w rzeczywistych obiektach. Dzięki technologii serializacji, to JVM zajmuje się budowaniem grafu danych składowanego obiektu.
Serializacji mogą podlegać jedynie obiekty które są instancją interfejsu java.io.Serializable. Jeśli obiekt zawiera składniki referencyjne, one również muszą być instancjami tegoż interfejsu. Serializacji nie mogą podlegać jednak wszystkie rodzaje obiektów. Do nich zalicza się obiekty związane bezpośrednio ze środowiskiem w jakim uruchamiana jest aplikacja jak na przykład klasy z pakietu IO czy klasa Thread. Składniki nie podlegające serializacji wyłącza się z procesu poprzez oznaczenie ich słowem kluczowym transient.
Poniżej przedstawiony jest przykład zapamiętania stanu bieżącego obiektu:
import java.io.*;
import java.util.*;
public class TestSerial implements Serializable {
private Vector data = new Vector();
public void addData(Object value) {
data.addElement(value);
}
public void showData() {
for (int i=0; i<data.size(); i++) {
System.out.println(data.elementAt(i).toString());
}
}
// main
public static void main(String args[]) {
// tworzenie obiektu
TestSerial test = new TestSerial();
test.addData("ppoj");
Hashtable elem = new Hashtable();
elem.put("laboratorium", new Integer(6));
test.addData(elem);
test.showData();
// serializacja
try {
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("data.ser"));
oos.writeObject(test);
oos.close();
}
catch (IOException e) {
e.printStackTrace();
}
test = null;
// deserializacja
try {
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("data.ser"));
test = (TestSerial) ois.readObject();
ois.close();
}
catch (Exception e) {
e.printStackTrace();
}
test.showData();
}
}
Komunikacja sieciowa
W celu dokonania komunikacji pomiędzy dwiema aplikacjami działającymi w sieci, w javie można wykorzystać mechanizm gniazdek sieciowych. Mamy tu do czynienia z dwoma rodzajami gniazdek: Socket i ServerSocket. Do bezpośredniego komunikowania używany jest obiekt typu Socket, natomiast do prowadzenia nasłuchu wykorzystywany jest ServerSocket. Przykład nawiązania komunikacji poniżej:
//serwer
import java.io.*;
import java.net.*;
public class Server {
public static void main(String args[]) throws IOException {
ServerSocket ss = new ServerSocket(4444);
while (true) {
Socket s = ss.accept();
DataInputStream dis = new DataInputStream(s.getInputStream());
String msg = dis.readUTF();
System.out.println(msg);
dis.close();
s.close();
}
}
}
//client
import java.io.*;
import java.net.*;
public class Client {
public static void main(String args[]) throws IOException {
Socket s = new Socket("localhost", 4444);
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
dos.writeUTF("Hello!");
dos.close();
s.close();
}
}
Jako ćwiczenie wykonane samodzielnie w laboratorium należy wykonać prosty serwer plików oferujący co najmniej dwa polecenia: list, exit oraz get <name>, które to mają pokazać aplikacji klienta odpowiednio zawartość katalogu jakim dysponuje serwer, opuścić współpracę z serwerem oraz umożliwić pobranie pliku o określonej nazwie z przestrzeni serwera. Serwer powinien działać wielowątkowo, aby móc obsługiwać wielu klientów jednocześnie.
Wskazówka: można wykorzystać mechanizm serializacji przesyłanych obiektów.
Osoby chętne mogą również zastosować komunikację z wykorzystaniem RMI.
Przykład rozwiązania:
Połączenie poprzez gniazdka
FileClient.java, FileServer.java oraz klasy przesyłające komendę i odpowiedź: Command.java i Response.java
Połączenia poprzez RMI
RMIClient.java, RMIServer.java oraz RMIServerImpl.java