SK Zadanie 5From Łukasz Graczykowski(Difference between revisions)
Revision as of 15:24, 30 November 2016
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 dane za pomocą pakietów broadcast (trafiają one do wszystkich systemów w sieci) przykładowe rozwiązanie (nie sprawdzałem!).
Do tej pory wszystkie 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 Winows (zaimplementowanego w linuksie jako 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).
Kiedy nasz system wysyla wiele krótkich wiadomości do wielu innych systemów, okazuje się że narzut na handshake TCP jest nie do dość wydajne. Przykładowo 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 jes 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); Odebraie 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-9. 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 { for(String s: args){ System.out.println(s); } //Odczytujemy wiadomosc albo z konsoli albo z pierwszego argumentu String message = null; if (args.length != 2){ System.out.println(args.length); System.out.println("USAGE: Client <serverAddress> <nessage>"); return; } message = args[1]; InetAddress serverAddress = InetAddress.getByName(args[0]); 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. |