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