DatagramPacket
Transkrypt
DatagramPacket
Programowanie sieciowe Wykład 5: Java sieciowa cd. mgr inŜ. Paweł Kośla mgr Marcin Raniszewski Łódź, 2009 1 Plan wykładu ServerSocket – serwer TCP DatagramSocket i DatagramPacket – UDP FTP 2 Serwer TCP Gniazdo serwerowe ma za zadanie nasłuchiwania na podanym porcie zgłoszeń klientów i następnie obsługiwanie połączenia z nim. Klasa ServerSocket MoŜliwość obsługi wielu klientów. Połączenie dwukierunkowe. Transmisja strumieniowa. Jeden proces moŜe słuchać na danym porcie w danym czasie. 3 Typologia serwerów Rodzaje serwerów: • serwer jednowątkowy • serwer pozornie wielowątkowy • serwer wielowątkowy 4 Typologia serwerów Serwer jednowątkowy: Obsługa klienta Oczekiwanie Cechy: • prostota budowy • moŜliwość obsługi tylko jednego klienta naraz • kolejny klient musi długo czekać na swoją kolej 5 Typologia serwerów Serwer pozornie wielowątkowy: elementy obsługi klienta 1 Dołączenie nowego klienta elementy obsługi klienta 2 .... Sprawdzenie elementy obsługi klienta n Cechy: • zaawansowana budowa • moŜliwość obsługi kilku klientów naraz • kolejny klient musi czekać aŜ wykonana zostanie tura obsługi podłączonych 6 klientów Typologia serwerów Serwer wielowątkowy: Wątek główny Utworzenie nowego procesu Wątek potomny Początek Oczekiwanie Obsługa klienta Koniec Wątek potomny Początek Obsługa klienta Koniec Cechy: • same zalety • moŜliwość obsługi kilku klientów naraz • nowi klienci obsługiwani są natychmiastowo 7 Serwer TCP Podstawowy schemat serwera: open ServerSocket() bind accept read write close open bind accept read write close close2 – tworzy gniazdo serwera – łączy stworzone gniazdo z lokalnym portem – nasłuchuje i akceptuje połączenia od klientów – pobiera dane ze strumienia otwartego połączenia z klientem – wysyła dane – zamyka połączenie – zamyka serwer close2 8 Serwer TCP Realizacja serwera jednowątkowego: ServerSocket() accept open connect read write write read close close close2 serwer klient 9 Serwer TCP Realizacja serwera wielowątkowego: open connect wątek ServerSocket() read write accept close write/read close klient 2 close2 serwer open connect wątek read write close write/read close klient 1 10 Serwer TCP klient 1 Jeszcze lepszy serwer wielowątkowy: open connect x10 ServerSocket() write/read close createThread() close2 wątek wątek wątek accept accept accept read write read write read write close close serwer ... close 11 Serwer TCP – tworzenie Do tworzenia gniazd serwera TCP słuŜą konstruktory: public ServerSocket(int port) throws IOException public ServerSocket(int port, int backlog) throws IOException public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException Najczęściej wykorzystuje się pierwszy konstruktor, podając numer portu na którym serwer ma nasłuchiwać: try { ServerSocket ss = new ServerSocket(80); } catch (IOException e) { System.err.println(e); } 12 Serwer TCP – tworzenie Podczas tworzenia obiektu ServerSocket obiekt ten próbuje dowiązać się do wskazanego portu na lokalnym hoście (ang. binding). Jeśli port jest juŜ zajęty wyrzucony zostanie wyjątek: java.net.BindException z klasy bazowej java.io.IOException Tworzenie własnych serwerów na portach >1024. W systemach Unixowych programy korzystające z portów pomiędzy 1 a 1023 muszą mieć juŜ uprawnienia administratora. Port nr 0. Numer ten mówi Javie, aby skorzystała z pierwszego wolnego portu. Wybrany automatycznie numer portu moŜna odczytać za pomocą metody getLocalPort(). try { ServerSocket ftpdata = new ServerSocket(0); int port = ftpdata.getLocalPort(); } catch (IOException e) { System.err.println(e); } 13 Serwer TCP – tworzenie Skaner portów lokalnych. Sprawdzenie na których portach są juŜ aktywne połączenia. Porównaj z "netstat -an" import java.net.*; import java.io.*; public class SkanerLoc { public static void main(String[] args) { int startport = 1; int stopport = 65535; for (int port = startport; port <= stopport; port++) { try { ServerSocket ss = new ServerSocket(port); ss.close(); } catch (IOException ex) { System.out.println("Port " + port + " jest zajety."); } } } 14 } Serwer TCP – Kolejka połączeń System operacyjny zapamiętuje połączenia przychodzące dla kaŜdego portu w kolejce typu FIFO aŜ do jej zapełnienia. Po zapełnieniu kolejki następne połączenia przychodzące będą odrzucane, chyba, Ŝe w kolejce zwolni się jakieś miejsce. Długość kolejki zaleŜy od systemu operacyjnego (5-50). Klient Serwer Klient Klient Klient 15 Serwer TCP – tworzenie Rozmiar kolejki połączeń moŜe zostać zmieniony w zaleŜności od potrzeb. Wykorzystujemy do tego drugi konstruktor: public ServerSocket(int port, int backlog) throws IOException Przykład: try { ServerSocket httpd = new ServerSocket(80, 50); } catch (IOException ex) { System.err.println(ex); } KaŜdy system operacyjny ma określoną maksymalną długość kolejki. Jeśli więc zadeklarowana będzie kolejka dłuŜsza niŜ istniejące maksimum, to długość kolejki zawęŜona będzie do wartości maksymalnej. Po utworzeniu gniazda serwera długość kolejki nie moŜe być juŜ zmieniana. 16 Serwer TCP – tworzenie Komputer moŜe uŜywać wielu interfejsów sieciowych. 192.168.0.2 10.0.0.0 10.0.0.0 192.168.0.0 192.168.0.0 10.0.0.1 Host 212.191.89.15 212.191.89.0 212.191.89.0 Poprzednio stworzone gniazda nasłuchują na wszystkich interfejsach. Do stworzenia gniazda serwera tylko dla wybranego interfejsu słuŜy: public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException Przykład: try { InetAddress ia = InetAddress.getByName("192.168.0.2"); ServerSocket ss = new ServerSocket(80, 50, ia); } catch (IOException e) { System.err.println(e); } 17 Serwer TCP – tworzenie Istnieje moŜliwość rozbicia tworzenia obiektu ServerSocket i przywiązania do portu. Wykorzystujemy wówczas konstruktor i metodę bind: public ServerSocket() throws IOException public void bind(SocketAddress endpoint) throws IOException Odczyt informacji o gnieździe (tylko lokalne informacje): public InetAddress getInetAddress() public int getLocalPort() 18 Serwer TCP – nasłuchiwanie Po stworzeniu gniazda serwer czeka na zgłoszenia klientów. Odpowiedzialna za to jest metoda: public Socket accept() throws IOException Metoda nasłuchuje i akceptuje połączenia z klientem (czekającym w kolejce). Zwraca obiekt klasy Socket, reprezentujący połączenie klient-serwer. Jest to metoda blokująca. Blokuje wykonywanie dalszych instrukcji dopóki nie nastąpi połączenie. MoŜna zmienić opcje blokującej metody accept() public void setSoTimeout(int timeout) throws SocketException timeout – czas czekania na połączenie od klienta (0 – domyślne, oznacza nieskończoność). Wówczas metoda accept moŜe zwrócić wyjątek SocketTimeoutException gdy czas nasłuchiwania minie. 19 Serwer TCP – akceptowanie ServerSocket accept() Klient Socket read/write Serwer 20 Serwer TCP – połączenie Połączenie z klientem realizowane jest za pomocą obiektu gniazda Socket Odczytywanie informacji o połączeniu jak w przypadku Klienta TCP Wysyłanie i odbieranie danych takie samo jak w Kliencie TCP 21 Serwer TCP – zakończenie Metoda close() klasy Socket kończy konkretne połączenie klientserwer. Serwer dalej działa. Aby zakończyć działanie serwera naleŜy wykonać metodę close() na obiekcie klasy ServerSocket 22 Serwer TCP Fragment serwera: try { ServerSocket ss = new ServerSocket(2345); Socket s = ss.accept(); PrintWriter pw = new PrintWriter(s.getOutputStream()); pw.println("Hello!"); pw.println("Goodbye"); s.close(); ss.close(); } catch (IOException e) { System.err.println(e); } close() zamyka połączenie gniazda Socket s 23 Serwer TCP Fragment serwera wielokrotnie akceptującego połączenia: try { ServerSocket ss = new ServerSocket(2345); while (bStop==false) { Socket s = ss.accept(); PrintWriter pw = new PrintWriter(s.getOutputStream()); pw.println("Hello!"); pw.println("Goodbye"); s.close(); } ss.close(); } catch (IOException e) { System.err.println(e); } 24 Serwer TCP - podsumowanie Opisane metody pozwalają obsługiwać klientów po kolei. Do obsługiwania wielu klientów jednocześnie wykorzystujemy wielowątkowość. Serwer startujemy na porcie, który później poznaje klient. Oczekujemy zgłoszeń klientów i następnie tworzymy komunikację między gniazdami Socket-Socket. 25 Komunikacja UDP Niepewna transmisja – zagubione datagramy Brak kontroli przepływu – datagramy w róŜnych kolejnościach Transmisja szybsza od TCP Wiadomość zawiera się w pojedynczym komunikacie UDP (dane nie większe niŜ ok. 60kB) Do obsługi komunikacji UDP słuŜą klasy: DatagramSocket – gniazdo datagramowe DatagramPacket – pakiet UDP (zamiast strumieni TCP) Host 1 Host 2 26 Komunikacja UDP DatagramSocket jest uŜywane do wysyłania i odbierania DatagramPacket DatagramPacket jest nakładką dla tablicy bajtów, z której dane będą wysyłane lub do której dane będą przyjmowane. Obiekt tej klasy przechowuje takŜe adres i port hosta, do którego dane będą wysłane. DatagramSocket jest połączeniem do portu, z którego nadawane i odbierane są przesyłki. Nie ma rozróŜnienia między klientem a serwerem DatagramSocket moŜe wysyłać do róŜnych odbiorców gdyŜ adres odbiorcy zapisany jest w pakiecie a nie w gnieździe. 27 UDP - DatagramPacket Nakładka dla tablicy bajtów. Zawiera takŜe port i adres hosta do którego dane będą wysłane (lub z którego dane zostały odebrane) Do tworzenia pakietu słuŜą konstruktory: public DatagramPacket(byte[] data, int length) public DatagramPacket(byte[] data, int length, InetAddress host, int port) Pozwalają przekazać obiektowi DatagramPacket tablicę bajtów oraz liczbę bajtów, która ma być wysłana. String s = "Wiadomosc do odbiorcy" byte[] b = s.getBytes(); DatagramPacket dp = new DatagramPacket(b, b.length); UŜywamy tylko gdy gniazdo zostało przypisane do jakiegoś odbiorcy metoda connect() (bardzo rzadko) 28 UDP - DatagramPacket Tradycyjnie powinno podać się adres odbiorcy i port na który wysyłamy. try { InetAddress host = InetAddress.getByName("192.168.0.2"); int port = 1111; String s = "Wiadomosc do odbiorcy2" byte[] b = s.getBytes(); DatagramPacket dp = new DatagramPacket(b, b.length, host, port); } catch (UnknownHostException ex) { System.err.println(ex); } Tablica bajtów jest przekazywana do konstruktora przez referencję. Dlatego jeśli jej zawartość zostanie zmieniona poza obiektem, to zmiany te dotyczyć będą równieŜ danych w DatagramPacket 29 UDP - DatagramPacket Obiekty są modyfikowalne, tzn. Ŝe moŜna zmienić ich parametry takie jak: dane, długość danych, port, adres. Robi się to za pomocą metod: public public public public void void void void setAddress(InetAddress host) setPort(int port) setData(byte buffer[]) setLength(int length) Do odczytu wspomnianych właściwości moŜna uŜyć metod: public public public public InetAddress getAddress() int getPort() byte[] getData() int getLength() Te drugie szczególnie przydają się przy odbieraniu pakietów. 30 UDP - DatagramSocket Mamy juŜ pakiet, teraz trzeba go wysłać. Do tworzenia datagramowych gniazd uŜywamy konstruktorów: public DatagramSocket() throws SocketException public DatagramSocket(int port) throws SocketException public DatagramSocket(int port, InetAddress laddr) throws SocketException Pierwszy uŜywany jest do gniazd datagramowych działających w trybie klienta (tylko do wysyłania, z pierwszego wolnego portu) Dwa następne mają dodatkowo moŜliwość nasłuchiwania i odbierania pakietów na wskazanym porcie i opcjonalnie tylko ze wskazanego interfejsu. Wysyłanie takim gniazdem następuje z podanego portu. Tak jak w TCP, tylko jeden proces moŜe nasłuchiwać na danym porcie (porty TCP i UDP są rozdzielne) 31 UDP - DatagramSocket Skaner portów lokalnych UDP UDP UDP UDP UDP UDP Port Port Port Port Port Port 445 zajety 500 zajety 1033 zajety 1060 zajety 1081 zajety 1900 zajety import java.net.*; import java.io.IOException; public class UDPSkaner { public static void main(String[] args) { int startport = 1; int stopport = 65535; for (int port = startport; port <= stopport; port++) { try { DatagramSocket ds = new DatagramSocket(port); ds.close(); } catch (IOException ex) { System.out.println("UDP Port " + port + " zajety"); } } } } PoniewaŜ UDP nie korzysta z połączeń, nie moŜna napisać skanera zdalnych portów UDP. 32 UDP – wysyłanie danych Przekonwertować dane na tablicę bajtów. Przekazać do konstruktora DatagramPacket tablicę, razem z długością danych umieszczonych w tablicy, oraz InetAddress z numerem portu, pod który wysyłane będą dane. Wykonać metodę send() na obiekcie DatagramSocket try { InetAddress host = InetAddress.getByName("192.168.0.2"); int port = 1111; String s = "Wiadomosc do odbiorcy2" byte[] b = s.getBytes(); DatagramPacket dp = new DatagramPacket(b, b.length, host, port); DatagramSocket sender = new DatagramSocket(); sender.send(dp); } catch (UnknownHostException ex) { System.err.println(ex); } catch (IOException e) { System.out.println(e); } 33 UDP – odbieranie danych Utworzyć obiekt klasy DatagramSocket przekazując co najmniej numer portu, na którym będzie prowadzony nasłuch. Przekazać pusty obiekt DatagramPacket do metody receive() gniazda DatagramSocket. public void receive(DatagramPacket dp) throws IOException Wątek wywołujący tą metodę zablokuje się, aŜ do chwili otrzymania pakietu. Po otrzymaniu pakietu obiekt DatagramPacket wypełniony zostanie danymi (takŜe hostem i portem nadawcy) try{ DatagramSocket ds = new DatagramSocket(1111); byte buffer[] = new byte[10]; DatagramPacket dp = new DatagramPacket(buffer,buffer.length); ds.receive(dp); byte[] data = dp.getData(); String s = new String(data, 0, data.length); System.out.println(s); System.out.println(dp.getPort()); } catch(IOException e) { System.out.println(e); } 34 UDP - gniazda Istnieje moŜliwość przywiązania gniazda UDP do hosta i portu zdalnego. Metoda klasy DatagramSocket: public void connect(InetAddress address, int port) Mimo sugerującej nazwy, nie następuje proces łączenia ze zdalnym hostem. Dzięki niej gniazdo takie, ma moŜliwość wysyłania i odbierania pakietów UDP do/z podanego zdalnego hosta i portu. UŜywamy wówczas konstruktora pakietu: public DatagramPacket(byte[] data, int length) Próba wykorzystania drugiego (przekazując inny host lub port od tego związanego funkcja connect() ) kończy się niepowodzeniem. try{ DatagramSocket ds = new DatagramSocket(1234); String s = "Komunikat" ; byte[] b = s.getBytes(); InetAddress host = InetAddress.getByName("192.168.66.5"); ds.connect(host,1111); DatagramPacket dp = new DatagramPacket(b, b.length); ds.send(dp); } catch(IOException e){ System.out.println(e); } 35 UDP - rozgłaszanie Jeśli chcemy wysłać pakiet do wszystkich hostów w sieci, adresujemy obiekt DatagramPacket adresem rozgłoszeniowym. try{ DatagramSocket ds = new DatagramSocket(); String s = "Do wszystkich!!" ; byte[] b = s.getBytes(); InetAddress host = InetAddress.getByName("192.168.0.255"); DatagramPacket dp = new DatagramPacket(b, b.length, host, 1111); ds.send(dp); } catch(IOException e) { System.out.println(e); } 36 UDP – rozgłaszanie grupowe Do rozgłaszania grupowego korzystamy z klasy MulticastSocket, która jest pochodną DatagramSocket. Zdefiniowane są tu dodatkowe metody: public void joinGroup(InetAddress mcastaddr) throws IOException public void leaveGroup(InetAddress mcastaddr) throws IOException SłuŜą one do zapisywania i wypisywania z grupy. W parametrze przekazujemy obiekt InetAddress reprezentujący adres grupy (adres klasy D) Jeśli następnie wyślemy pakiet na ten adres dotrze on do wszystkich hostów zapisanych. String msg = "Hello"; InetAddress group = InetAddress.getByName("228.5.6.7"); MulticastSocket s = new MulticastSocket(6789); s.joinGroup(group); DatagramPacket hi = new DatagramPacket(msg.getBytes(), msg.length(), group, 6789); s.send(hi); 37 UDP – podsumowanie Przepis na komunikację UDP: 1. Utworzenie obiektu DatagramSocket bez podawania jakichkolwiek argumentów (podanie portu waŜne przy odbieraniu pakietów). 2. Opcjonalnie związanie ze zdalnym hostem metodą connect(). 3. Stworzenie jednego lub kilku obiektów DatagramPacket – zawierają tablice bajtów, które naleŜy wysłać lub które zostały odebrane. 4. Jeśli nie została wywołana metoda connect(), to podczas tworzenia obiektów DatagramPacket naleŜy podać obiekt InetAddress oraz numer portu. 5. Określenie długości poszczególnych pakietów oraz przesłanie ich poprzez metodę send(pakietOut) 6. Odebranie danych poprzez metodę receive(pakietIn) 38 Protokół FTP import java.net.*; import java.io.*; 220- public class FTP { Witaj - [email protected] public static void main(String[] args) { Aby pobrac dane z konta uzyj sftp, scp badz try{ klienta Socket s = new Socket("ftp.kis.p.lodz.pl",21); wspierajacego protokol ftps. BufferedReader we = new BufferedReader(new InputStreamReader(s.getInputStream()) ); Wersja tekstowa klienta scp: PrintWriter wy = new PrintWriter(s.getOutputStream()); wy.println("USER anonymouse"); http://www.chiark.greenend.org.uk/~sgtatham/put wy.println("PASS [email protected]"); ty/ wy.println("PWD"); Wersja okienkowa klienta scp: wy.println("QUIT"); http://winscp.vse.cz/eng/ wy.flush(); String line; Wersja okienkowa klienta ftps: while( (line=we.readLine()) != null){http://www.smartftp.com/ System.out.println(line); } 220 212.191.89.2 FTP server ready s.close(); 550 SSL/TLS required on the control channel } 550 SSL/TLS required on the control channel catch(IOException e) 550 SSL/TLS required on the control channel { System.out.println(e); 221 Goodbye. } } } 39