SK Zadanie 5From Łukasz Graczykowski
ZadaniaZadanie 1(Unicast) W parach modyfikujemy podane na stronie przykłady: Klient:
Serwer:
Zadanie 2(Broadcast) Tworzymy program, który pozwala na sprawdzenie listy obecności na zajęciach (widocznej dla każdego użytkownika sieci). W parach tworzymy jeden program, który wysyła i odbiera dane jednocześnie (2 wątki):
Zadanie 3(Multicast) Przerabiamy program z Zadania 2, aby działał na multicast. Protokół UDPUDP (User Datagram Protocol) jest protokołem transmisji danych, który ma następujące własności:
Zastosowania UDP
Na przykład synchronizacja plików między wieloma serwerami w jednej sieci. Jeden z serwerów wysyła te same dane do wszystkich podłączonych klientów.
W protokole TCP usługi, które pisaliśmy wymagały posiadania numeru IP serwera. Jest to całkiem uciążliwe w małych sieciach i sieciach ad-hoc. Chcielimbyśmy, by nasza sieć działała tak, że po podłączeniu nasz system może wykryć wszystkie usługi widoczne w sieci. Tego typu rozwiązania często implementuje się za pomocą UDP. Przykładem może być usługa NetBIOS, będąca częścią systemu sieciowego systemu Microsoft Windows (odpowiednik w systemach Linux nazywa się SAMBA).
Część sieci peer-to-peer korzysta z UDP zamiast z TCP, ponieważ nie potrzebują obsługi błędów (same posiadają sumy kontrolne poszczególnych plików, nie potrzebujemy zatem kontroli na poziomie protokołu).
Kiedy nasz system wysyla wiele krótkich wiadomości do wielu innych systemów. W takim przypadku okazuje się, że narzut na procedurę handshake TCP jest jest zbyt duży i całość działa mało wydajnie. Przykład: protokół ustalania czasu (Network Time Protocol) korzysta z UDP do komunikacji z klientami.
W przypadku streamingu mediów (audio, video), czy oprogramowania gier sieciowych ważne jest, to by opóźnienie przesłanych informacji było minimalne. BroadcastBroadcast jest w zasadzie funkcjonalnością protokołu IP, ale skoro nie można z niego skorzystać z poziomu TCP to mówimy o nim dopiero teraz. Załóżmy że mamy podsieć o masce: Maska podsieci (zapisana binarnie): 11111111 11111111 11111110 00000000 Adres komputera (zapisany binarnie): 11000010 00011101 10101110 01111011 Adres Broadcast (zapisany binarnie): 11000010 00011101 10101111 11111111
MulticastPrzesyłanie informacji od wszystkich użytkowników danej podsieci ma wiele zastosowań, ale ma poważne ograniczenia - wiadomości broadcast generalnie są wycinane przez routery, i nie nadają się do komunikacji poza siecią lokalną. Rozważmy przykładowo telewizję internetową: jeśli korzysta z wiadomości unicast (do jednego odbiorcy) ilość pakietów które wysyła rośnie liniowo z ilością odbiorców - mimo, że każdy odbiorca dostaje taki sam obraz (a w zasadzie: taką samą listę pakietów). By rozwiązać takie problemy stworzono wiadomości multicast, działają one w następujący sposób: Zdefiniowano podsieć
Komunikacja UDP w JavieUnicast/BroadcastKlasa konfiguracyjnaimport java.net.InetAddress; import java.net.UnknownHostException; public class Config { public static final int PORT = 9000; public static final int BUFFER_SIZE = 1024; public static final InetAddress MULTICAST_ADDRESS; public static final int MULTICAST_PORT = 9000; static { try{ MULTICAST_ADDRESS = InetAddress.getByName("239.255.42.99"); }catch (UnknownHostException e){ throw new RuntimeException(e); } } } Serwerimport java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UTPServer { public static void main(String[] args) throws Exception{ //Otwarcie gniazda z okreslonym portem DatagramSocket datagramSocket = new DatagramSocket(Config.PORT); byte[] byteResponse = "OK".getBytes("utf8"); while (true){ DatagramPacket reclievedPacket = new DatagramPacket( new byte[Config.BUFFER_SIZE], Config.BUFFER_SIZE); datagramSocket.receive(reclievedPacket); int length = reclievedPacket.getLength(); String message = new String(reclievedPacket.getData(), 0, length, "utf8"); // Port i host który wysłał nam zapytanie InetAddress address = reclievedPacket.getAddress(); int port = reclievedPacket.getPort(); System.out.println(message); Thread.sleep(1000); //To oczekiwanie nie jest potrzebne dla // obsługi gniazda DatagramPacket response = new DatagramPacket( byteResponse, byteResponse.length, address, port); datagramSocket.send(response); } } } Ważniejsze miejsca programuPrzygotowanie do odbierania połączeń UDP na zadanym portcie: DatagramSocket datagramSocket = new DatagramSocket(9000); Stworzenie pakietu który będzie odbierał dane: DatagramPacket reclievedPacket = new DatagramPacket( new byte[BUFFER_SIZE], BUFFER_SIZE); Odebranie pakietu: datagramSocket.receive(reclievedPacket); Tutaj jest kolejny detal implementacyjny w Javie: musimy przekształcić ciąg bajtów do instancji klasy string. Zakładamy że dane w pakiecie kodowane są za pomocą utf-8. int length = reclievedPacket.getLength(); String message = new String(reclievedPacket.getData(), 0, length, "utf8"); Wysłanie odpowiedzi: byte[] byteResponse = "OK".getBytes("utf8"); DatagramPacket response = new DatagramPacket( byteResponse, byteResponse.length, address, port); Klientimport java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketTimeoutException; public class UDPClient { public static void main(String[] args) throws IOException { String message = "tekst"; InetAddress serverAddress = InetAddress.getByName("localhost"); System.out.println(serverAddress); DatagramSocket socket = new DatagramSocket(); //Otwarcie gniazda byte[] stringContents = message.getBytes("utf8"); //Pobranie strumienia bajtów z wiadomosci DatagramPacket sentPacket = new DatagramPacket(stringContents, stringContents.length); sentPacket.setAddress(serverAddress); sentPacket.setPort(Config.PORT); socket.send(sentPacket); DatagramPacket reclievePacket = new DatagramPacket( new byte[Config.BUFFER_SIZE], Config.BUFFER_SIZE); socket.setSoTimeout(1010); try{ socket.receive(reclievePacket); System.out.println("Serwer otrzymał wiadomość"); }catch (SocketTimeoutException ste){ System.out.println("Serwer nie odpowiedział, więc albo dostał wiadomość albo nie..."); } } } Ważniejsze miejsca w programieWybieramy do jakiego adresu wysyłamy informacje: InetAddress serverAddress = InetAddress.getByName(args[0]); Wysłanie pakietu: byte[] stringContents = message.getBytes("utf8"); //Pobranie strumienia bajtów z wiadomosci DatagramPacket sentPacket = new DatagramPacket(stringContents, stringContents.length); sentPacket.setAddress(serverAddress); sentPacket.setPort(PORT); Odebranie odpowiedzi. Tutaj musimy się na chwilę zatrzymać: w TCP moglibyśmy po prostu poczekać na odpowiedź od serwera, tutaj nie możemy tak zrobić - przecież odpowiedź od serwera może po prostu nie nadejść... należy więc powiedzieć socketowi: poczekaj określony czas na odpowiedż, jeśli nie nadejdzie ona zgłoś wyjątek. Ustawienie okresu oczekiwania na odpowiedź: Uwaga: Argument metody socket.setSoTimeout(1010); Odebranie danych: DatagramPacket reclievePacket = new DatagramPacket(new byte[Config.BUFFER_SIZE], Config.BUFFER_SIZE); try{ socket.receive(reclievePacket); System.out.println("Serwer otrzymał wiadomość"); }catch (SocketTimeoutException ste){ System.out.println("Serwer nie odpowiedział, więc albo dostał wiadomość albo nie..."); } Zadanie 1(Unicast) W parach modyfikujemy powyższe przykłady: Klient:
Serwer:
Zadanie 2(Broadcast) Tworzymy program, który pozwala na sprawdzenie listy obecności na zajęciach (widocznej dla każdego użytkownika sieci). W parach tworzymy jeden program, który wysyła i odbiera dane jednocześnie (2 wątki):
MulticastSerwerimport java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.MulticastSocket; public class MulticastServer { public static void main(String[] args) throws Exception{ byte[] responseBytes = "ACK".getBytes(); InetAddress group = Config.MULTICAST_ADDRESS; MulticastSocket s = new MulticastSocket(Config.MULTICAST_PORT); s.joinGroup(group); try{ while (true){ DatagramPacket recv = new DatagramPacket(new byte[Config.BUFFER_SIZE], Config.BUFFER_SIZE); s.receive(recv); String stringMsg = new String(recv.getData(), 0, recv.getLength(), "utf8"); System.err.println("Got message: \"" + stringMsg + "\""); DatagramSocket responseSocket = new DatagramSocket(); DatagramPacket response = new DatagramPacket(responseBytes, responseBytes.length); response.setAddress(recv.getAddress()); response.setPort(recv.getPort()); Thread.sleep(1000); // Ta linijka powoduje wstrzymanie wysyłania odpowiedzi przez // jedną sekundę --- nie ma ona związku z obsługą UDP. responseSocket.send(response); } }finally { s.leaveGroup(group); } } }
Ważniejsze miejsca programuStworzenie gniazda multicast - oraz dołączenie do grupy mulitcast InetAddress group = Config.MULTICAST_ADDRESS; MulticastSocket s = new MulticastSocket(Config.MULTICAST_PORT); s.joinGroup(group); Klientimport java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketTimeoutException; public class MulticastClient { public static void main(String[] args) throws Exception{ DatagramSocket s = new DatagramSocket(); byte[] message = "Test".getBytes("utf8"); DatagramPacket packet = new DatagramPacket(message, message.length); packet.setPort(Config.PORT); packet.setAddress(Config.MULTICAST_ADDRESS); s.send(packet); System.out.println("Wysłałem pakiet"); s.setSoTimeout(1000); DatagramPacket response = new DatagramPacket(new byte[Config.BUFFER_SIZE], Config.BUFFER_SIZE); try{ s.receive(response); System.out.println("Odpowiedź: "); System.out.print(new String(response.getData(), 0, response.getLength(), "utf8")); }catch (SocketTimeoutException e){ System.out.println("Nie otrzymałem odpowiedzi"); } } }
Ważniejsze miejsca programuWysłanie pakietu na grupę multicast: DatagramPacket packet = new DatagramPacket(message, message.length); packet.setPort(Config.PORT); packet.setAddress(Config.MULTICAST_ADDRESS); s.send(packet); Ustawienie timeoutu na odowiedź: s.setSoTimeout(1000); Zadanie 3(Multicast) Przerabiamy program z Zadania 2, aby działał na multicast. Projekt nr 2Wybieramy (w parach) projekty z listy: http://www.if.pw.edu.pl/~lgraczyk/sk/html/pd2.html |