UDP w Javie =========== Unicast/Broadcast ################# .. note:: Java automaycznie wyśle pakiet broadcast jeśli poprosimy ją o wysłanie danych na adres broadcast. Klasa z konfiguracją -------------------- .. code-block:: java import 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); } } } Server ------ .. code-block:: java import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; public class UDPServer { 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 recievedPacket = new DatagramPacket( new byte[Config.BUFFER_SIZE], Config.BUFFER_SIZE); datagramSocket.receive(recievedPacket); int length = recievedPacket.getLength(); String message = new String(recievedPacket.getData(), 0, length, "utf8"); // Port i host, ktory wyslal nam zapytanie InetAddress address = recievedPacket.getAddress(); int port = recievedPacket.getPort(); System.out.println(message); Thread.sleep(1000); //oczekiwanie nie jest niezbedne DatagramPacket response = new DatagramPacket(byteResponse, byteResponse.length, address, port); datagramSocket.send(response); } } } Ważniejsze miejsca programu *************************** Przygotowanie do odbierania połączeń UDP na zadanym portcie: .. code-block:: java DatagramSocket datagramSocket = new DatagramSocket(Config.PORT); Stworzenie pakietu który będzie odbierał dane: .. code-block:: java DatagramPacket recievedPacket = new DatagramPacket( new byte[Config.BUFFER_SIZE], Config.BUFFER_SIZE); Odebraie pakietu: .. code-block:: java datagramSocket.receive(recievedPacket); 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. .. code-block:: java int length = reclievedPacket.getLength(); String message = new String(reclievedPacket.getData(), 0, length, "utf8"); Wysłanie odpowiedzi: .. code-block:: java byte[] byteResponse = "OK".getBytes("utf8"); DatagramPacket response = new DatagramPacket(byteResponse, byteResponse.length, address, port); Klient ------ Oprogramowanie klienta UDP jest również relatywnie proste. .. note:: Nasz klient przyjmuje z linii komend dwa argumenty: adres na który wysyła wiadomość oraz treść wiadomości. .. code-block:: java import 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("194.29.174.217"); //adres serwera System.out.println(serverAddress); DatagramSocket socket = new DatagramSocket(); //Otwarcie gniazda byte[] stringContents = message.getBytes("utf8"); //Pobranie strumienia bajtow z wiadomosci DatagramPacket sentPacket = new DatagramPacket(stringContents, stringContents.length); sentPacket.setAddress(serverAddress); sentPacket.setPort(Config.PORT); socket.send(sentPacket); DatagramPacket receivedPacket = new DatagramPacket( new byte[Config.BUFFER_SIZE], Config.BUFFER_SIZE); socket.setSoTimeout(1010); try{ socket.receive(receivedPacket); int length = receivedPacket.getLength(); String receivedMessage = new String(receivedPacket.getData(), 0, length, "utf8"); System.out.println("Serwer otrzymal wiadomosc: "+receivedMessage); }catch (SocketTimeoutException ste){ System.out.println("Serwer nie odpowiedzial, byc moze dostal wiadomosc albo nie..."); } } } Ważniejsze miejsca programu *************************** Wybieramy do jakiego adresu wysyłamy informacje: .. code-block:: java InetAddress serverAddress = InetAddress.getByName("194.29.174.217"); Wysłanie pakietu: .. code-block:: java 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); 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ź: .. note:: Argument metody ``setSoTimeout`` to maksymalny czas oczekiwania na odpowiedź  w milisekundach. .. code-block:: java socket.setSoTimeout(1010); Odebranie danych: .. code-block:: java DatagramPacket receivedPacket = new DatagramPacket( new byte[Config.BUFFER_SIZE], Config.BUFFER_SIZE); socket.setSoTimeout(1010); try{ socket.receive(receivedPacket); int length = receivedPacket.getLength(); String receivedMessage = new String(receivedPacket.getData(), 0, length, "utf8"); System.out.println("Serwer otrzymal wiadomosc: "+receivedMessage); } catch (SocketTimeoutException ste){ System.out.println("Serwer nie odpowiedzial, byc moze dostal wiadomosc albo nie..."); } Zadanie 1 ######### (Unicast) W parach modyfikujemy powyższe przykłady: Klient: 1. Klient po uruchomieniu wysyła "Imię Nazwisko" (np. "Adam Szczurek") użytkownika do serwera. Następnie odbiera potwierdzenie otrzymania wiadomości z serwera. 2. Dalej klient odczytuje informację z konsoli: jeśli użytkownik wpisze "x", to wysyła zapytanie do serwera o przesłanielisty wszystkich użytkowników; jeśli "z", to program się kończy; jeśli użytkownik wpisze cokolwiek innego - jest to przesyłane jako kolejna nazwa użytkownika do serwera. Serwer: 3. Serwer dodaje kolenych użytkowników do tablicy: ``ArrayList clients = new ArrayList();`` i przesyła do klienta tekst "Dodano klienta o nazwie: ". 4. Jeśli serwer otrzyma "x", to przesyła do klienta zawartość tablicy ``clients``. 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): 1. Pierwszy wątek (odbierający): w pętli nasłuchuje na wybranym porcie (np. 9111) i jeśli coś otrzyma to wyświetla na ekran w formacie . 2. Drugi wątek (wysyłający): w pętli co 1 s wysyła na adres Broadcast pakiet z informacją o loginie (tekst ). Multicast ######### Serwer ------ .. code-block:: java import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketTimeoutException; public class MulticastServer { public static void main(String[] args) throws Exception{ DatagramSocket s = new DatagramSocket(); byte[] message = "Test".getBytes("utf8"); while (true){ DatagramPacket packet = new DatagramPacket(message, message.length); packet.setPort(9200); packet.setAddress(InetAddress.getByName("224.0.0.40")); s.send(packet); System.out.println("Wyslalem pakiet"); Thread.sleep(1000); //w ms } } } Ważniejsze miejsca programu *************************** Wysłanie pakietu na grupę multicast: .. code-block:: java DatagramPacket packet = new DatagramPacket(message, message.length); packet.setPort(9200); packet.setAddress(InetAddress.getByName("224.0.0.40")); s.send(packet); Klient ------ .. code-block:: java import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.MulticastSocket; public class MulticastClient { public static void main(String[] args) throws Exception{ byte[] responseBytes = "ACK".getBytes(); InetAddress group = InetAddress.getByName("224.0.0.40"); MulticastSocket s = new MulticastSocket(9200); s.joinGroup(group); try{ while(true) { DatagramPacket recv = new DatagramPacket(new byte[1024], 1024); 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); responseSocket.send(response); } }finally { s.leaveGroup(group); } } } Ważniejsze miejsca programu *************************** Stworzenie gniazda multicast --- oraz dołączenie do grupy mulitcast .. code-block:: java InetAddress group = InetAddress group = InetAddress.getByName("224.0.0.40"); MulticastSocket s = new MulticastSocket(9200); s.joinGroup(group); Zadanie 3 ######### (Multicast) Przerabiamy program z zadania 2, aby działał na multicast.