Instrukcja do laboratorium Systemów Operacyjnych (semestr drugi)
w
Ć iczenie dziesi t
ą e
Temat: Gniazda BSD – protoko y
ł internetowe
Opracowanie:
mgr in .
ż Arkadiusz Chrobot
1. Us u
ł gi sieciowe w Linuksie
Gniazda BSD (nazwa pochodzi od wersji BSD Uniksa, istnieją również gniazda TLI pochodzące z Systemu V, ale nie są one używane w Linuksie) stanowią API dla
protoko ów
ł
komunikacyjnych
stosowane
we
wszystkich
systemach
operacyjnych, które umożliwiają pracę w sieci. W przypadku systemów uniksowych umożliwiają nie tylko pracę w
rodowisku
ś
rozproszonym, ale
również lokalną komunikację między procesami stanowiąc uzupełnienie wcze niej
ś
omawianych mechanizmów. Dzięki gniazdom można pracować z protoko ami
ł
należącymi do różnych dziedzin np.: Uniksa, Internetu i Xerox NS. Dodatkowo mo liwa
ż
jest praca zarówno z protokołami po czeniowymi, łą
jak
i bezpołączeniowymi.
2. Ró n
ż ice mi d
ę zy protokołem TCP i UDP
W dziedzinie Internetu aplikacje sieciowe wykorzystują najczęściej jeden z dwóch najpopularniejszych protokołów: TCP lub UDP. Za pomocą pierwszego dane są wysy ane
ł
w postaci strumienia. Ten protokół jest nazywany po czeniowym,
łą
gdyż nadzoruje przebieg transmisji dbając o retransmisję zagubionych i zniekształconych pakietów. Nie ma w nim ograniczenia na rozmiar wysy anych
ł
danych. W niektórych zastosowaniach mo e
ż
się jednak
okazać za wolny. Można wtedy zamiast niego zastosować protokół UDP. Jest on protoko em
ł
bezpołączeniowym. Jednorazowo można za pomocą tego protoko u ł
wys a
ł ć dane w wielkości mniejszej od 64KB. Protokół ten nie zapewnia retransmisji danych, a przypadki zniekształcenia lub zagubienia pakietów nale y
ż
obsługiwać samodzielnie. Jest on jednak zdecydowanie szybszy od protoko u
ł
TCP. Pakiety obu protoko ów
ł
są „opakowywane” w pakiety protoko u
ł
IP, st d najcz
ą
ciej w literaturze pojawiaj
ęś
si
ą
nazwy TCP/IP i UDP/IP.
ę
3. Serwery iteracyjne i wspó b
ł ie n
ż e
Aplikacje sieciowe można podzielić na dwie kategorie: klientów i serwery1.
1 W terminologii uniksowej nazywane demonami.
2
Zadaniem serwerów jest wykonywanie usług, o które proszą klienty2. Obs uga ł
żądań klientów mo e
ż
przebiegać w sposób sekwencyjny (iteracyjny) lub wspó bie
ł
ny.
ż
W pierwszym przypadku serwer nawiązuje po czenie łą
z klientem,
realizuje jego pro b
ś
,
ę wysy a
ł
odpowiedź i wraca do oczekiwania na po czenia
łą
z innymi klientami. Podczas realizacji żądania klienta aden ż
inny klient nie jest
w stanie połączyć się z serwerem. Serwer współbieżny po nawiązaniu po czenia
łą
z klientem tworzy proces potomny (w ogólnym przypadku wątek), który obs uguje
ł
prośbę klienta, a proces macierzysty oczekuje na połączenia od innych klientów.
4. Struktury danych
Strukturę wysy anych
ł
przez gniazdo danych, czyli protokół wy szego
ż
rzędu,
osadzony na protokole transmisji określa użytkownik. Aby jednak nawi za ą
ć
po czenie
łą
nale y
ż
zadeklarować i wypełnić odpowiednie pola zmiennej typu struct sockaddr_in.
5. Opis funkcji
W tym podrozdziale opisane zostaną tylko funkcje niezbędne do wykonania wi kszo
ę
ci
ś
zadań zawartych w instrukcji. Osoby, które chcą dokładniej zapoznać się z tematyką pisania oprogramowania dla sieci komputerowych powinny skorzysta z innych
ć
róde
ź
ł, jak np.: klasyczna już książka W. Richarda
Stevensa „Programowanie zastosowań sieciowych w systemie Unix”.
socket() – funkcja ta zwraca deskryptor gniazda poprzez które będzie odbywa a
ł
się komunikacja mi dzy
ę
stacjami roboczymi w sieci. Mo na
ż
o niej my le
ś ć jako o funkcji open przeznaczonej dla urządzeń sieciowych.
Pierwszy pobierany przez nią argument oznacza rodzinę protoko ów ł
(PF_INET dla protoko ów
ł
Internetu), drugi rodzaj gniazda (połączeniowe -
SOCK_STREAM, bezpo czeniowe
łą
- SOCK_DGRAM), natomiast ostatni
okre la
ś
którego konkretnie protoko u
ł
będziemy u ywa
ż
ć (w wypadku
protoko ów
ł
internetowych jest zawsze równy 0). Funkcja wykorzystywana jest zarówno przez oprogramowanie serwera jak i klienta. Szczegóły: man socket.
2 To nie literówka. W języku polskim wprowadzono dwie odmiany liczby mnogiej wyrazu „klient”, aby odróżni
ć ludzi od programów.
3
funkcja bind() – wyznacza gniazdu nazw .
ę Zazwyczaj wywołuje ją serwer
przed rozpoczęciem komunikacji z klientem, ale mo e ż
jej również u y
ż ć
klient celem zanotowania lub sprawdzenia adresu. Jako argumenty funkcja
pobiera
deskryptor
gniazda,
strukturę
zawierającą
adres
komputera (patrz: man unix, man 7 ip), oraz rozmiar tej struktury.
Szczegóły: man 2 bind.
funkcja connect() – jest wykorzystywana tylko przez klienta i służy do ustanowienia po czenia
łą
z serwerem w protokole po czeniowym.
łą
Pobiera
jako parametry: deskryptor gniazda, strukturę z adresem serwera i rozmiar tej struktury. Szczegó y: man connect ł
funkcja listen() – używana jest przez serwer pracujący z protoko em ł
połączeniowym do zgłoszenia
e
ż
będzie nasłuchiwał
da
żą
ń po czenia.
łą
Jeśli odbierze takie żądanie umie ci
ś
je w kolejce. Listen przyjmuje dwa
argumenty: deskryptor gniazda oraz liczbę żądań, które system mo e ż
umie ci
ś ć w kolejce zanim zostaną one zaakceptowane (szczegóły: man tcp).
Szczegóły: man listen.
funkcja accept() – jest wywoływana przez oprogramowanie serwera pracuj cego
ą
z
protoko em
ł
po czeniowym.
łą
S u
ł
y
ż
do
przyjmowania
połączeń. Wymaga trzech argumentów. Pierwszym jest deskryptor gniazda, drugim wskaźnik do struktury do której będzie zapisany adres klienta, a trzecim rozmiar tej struktury. Funkcja pobiera pierwsze żądanie z kolejki i tworzy dla niego gniazdo, o takich samych w a ł
ciwo
ś
ciach
ś
jak gniazdo,
do którego nadesz o
ł
danie.
żą
Jeśli kolejka jest pusta, to accept blokuje do
momentu, aż pojawi się w niej jakieś żądanie. Zazwyczaj gniazdo, którego deskryptor zwraca accept jest obsługiwane przez proces potomny.
Szczegóły: man 2 accept.
Funkcje read(), write() i close() – s u
ł żą do odbierania i zapisywania danych
w gniazdach je li
ś
obsługują one protokół połączeniowy. Dzia aj
ł ą one
trochę inaczej niż w przypadku plików. Jeśli przez gniazdo połączeniowe są wysyłane dane o rozmiarze przekraczającym rozmiar bufora (zazwyczaj 4KB), to wprawdzie są wysy ane
ł
jako jeden strumień, ale mogą ulec
segmentacji. Oznacza to, że funkcja read mo e
ż odebrać mniej danych, niż
okre lili
ś
my
ś
to w jej wywołaniu. Nie jest to b d,
łą
nale y
ż
po prostu
powtórzyć jej dzia anie.
ł
Po zako czeniu
ń
komunikacji nale y
ż
zamknąć
gniazda przy pomocy funkcji close niezale nie
ż
od tego jakim protokołem
się posługujemy.
4
Funkcja sendto() – s u
ł
y do wysy
ż
ania informacji przez gniazdo w protokole
ł
bezpołączeniowym. Przyjmuje sześć argumentów wywołania: deskryptor, wska nik
ź
na bufor wysy anych
ł
danych, rozmiar bufora, flagę (najczęściej
0), wskaźnik na strukturę w której zapisany jest adres przeznaczenia oraz rozmiar tej struktury. Zwraca liczbę przes anych ł
bajtów. Szczegóły: man
sendto.
Funkcja recvfrom() – s u
ł
y
ż
do odbioru danych z gniazda w protokole
bezpołączeniowym. Liczba i znaczenie argumentów jest podobne jak w przypadku funkcji sendto. Różnica polega na tym, że do bufora na dane są zapisywane odebrane informacje, a w przedostatnim argumencie zapisywany jest adres strony po czenia,
łą
która te informacje nada a.
ł
Szósty argument jest wska nikiem
ź
na zmienną i w niej jest zapisywana
wielkość odebranej struktury adresu. Funkcja ta blokuje działanie w oczekiwaniu na dane, jeśli nie zostały jeszcze wysłane.
Funkcje zmiany porz dku
ą
bajtów – porządek bajtów (big-endian) mo e
ż się
ró ni
ż ć od tego który jest u ywany
ż
w komputerze pod czonym
łą
do sieci.
Aby sobie z tą ró nica
ż
poradzić stworzono funkcje, które dokonują
odpowiedniej konwersji. Przy realizacji zadań przydatne b d ę
:
ą htons() (host
to network short) – do przeliczania numeru portu, htonl() (host to network long) do przeliczania adresu serwera, który mo na ż
domyślnie okre li
ś ć jako
INADDR_ANY oraz funkcja inet_addr(), która przekszta ca ł
adres w notacji
kropkowej (będący ciągiem bajtów) do 32-bitowego adresu internetowego.
Szczegóły: man inet_addr, man htons, man htonl
6. Kolejność wywo a
ł ń funkcji
Serwer
w
protokole
połączeniowym
wywołuje
opisane
wyżej
funkcje
w nast puj
ę
cej kolejno
ą
ci:
ś
socket() -> bind() -> listen() -> accept() -> (read(), write()) -> close() natomiast klient w nast puj
ę
cej:
ą
socket() -> connect() -> (write(), read()) -> close() Serwer w protokole bezpołączeniowym wywo uje
ł
funkcje w nast puj
ę
cej
ą
kolejno ci:
ś
socket() -> bind() -> (recvfrom(), sendto()) -> close() 5
ę
cej:
ą
socket() -> (sendto(), recvfrom()) -> close() Zadania
1. Napisz
program
do
przesyłania
prostych
komunikatów
między
dwoma
komputerami z u yciem protoko
ż
łu TCP/IP.
2. Napisz program, który będzie wysyłał komunikaty o rozmiarze przekraczającym 64KB mi dzy dwoma komputerami z u
ę
yciem protoko
ż
u TCP/IP.
ł
3. Napisz
program,
który
będzie
realizował
polecenie
zawarte
w
zadaniu
pierwszym, ale w oparciu o protokół UDP/IP.
4. Uzupe nij
ł
program
z
trzeciego
zadania
o
wykrywanie
i
retransmisję
zagubionych pakietów. Podpowied :
ź
można wykorzystać obsługę sygnałów,
w szczególno ci sygna
ś
SIGALRM.
ł
5. Stwórz serwer wspó bie
ł
ny,
ż
który b dzie
ę
obsługiwał po czenia
łą
od wielu
klientów, również napisanych przez Ciebie – mogą przesyłać np. losowe liczby do serwera, który b dzie je wy
ę
wietla
ś
na ekranie.
ł
6. Zamiast tworzyć w serwerze za każdym razem kiedy pojawi się danie
żą
od
klienta nowy proces można u y
ż ć funkcji select() (szczegó y:
ł
man select).
Zrealizuje zadanie pi te z u
ą
yciem w
ż
a
ł
nie tej funkcji.
ś
6