background image

 

 

12. GNIAZDA BSD

Biblioteka funkcji związanych z gniazdami jest interfejsem programisty do 
obsługi protokołów

komunikacyjnych. Została utworzona dla Unixa BSD, głównie w związku z 
implementacją

protokołów rodziny TCP/IP. Gniazda umożliwiają komunikację zarówno między 
procesami

umiejscowionymi w jednym komputerze (komunikację w dziedzinie Unixa), jak 
i między procesami

w różnych komputerach (komunikację w dziedzinie Internetu). Z punktu 
widzenia programisty

posługiwanie się funkcjami jest w obu przypadkach podobne - podstawowa 
różnica związana jest

adresowaniem gniazd.

W przypadku komunikacji w dziedzinie Unixa gniazda (podobnie, jak łącza 
komunikacyjne) mogą

mieć nazwy i być uwidocznione w systemie plików, lub mogą być bezimienne 
(nienazwane).

W przypadku komunikacji w dziedzinie Internetu gniazda są identyfikowane 
poprzez adres IP

komputera oraz numer portu.

Gniazda umożliwiają łączność dwukierunkową (zarówno zapis, jak i odczyt 
danych).

background image

 

 

Biblioteka funkcji związanych z gniazdami została zaprojektowana pod kątem 
tworzenia par programów

typu klient - serwer (w szczególności serwerów współbieżnych). Przesyłanie 
informacji pomiędzy

klientem a serwerem może mieć charakter połączeniowy (analogia: rozmowa 
telefoniczna) lub

bezpołączeniowy (analogia: wysyłanie telegramów). Abstrakcją transmisji 
połączeniowej jest

strumień danych (zapewniający zachowanie kolejności przesyłanych informacji i 
niezawodność ich

dostarczania). Abstrakcją transmisji bezpołączeniowej jest ciąg oddzielnych 
pakietów informacji,

tak zwanych datagramów (o ograniczonej wielkości), które mogą wędrować 
różnymi drogami,

docierać w zmienionej kolejności, a czasem ulegać zagubieniu lub zduplikowaniu.

      klient

       serwer

            Protokół połączeniowy

     klient

      serwer

    Protokół 
bezpołączeniowy

background image

 

 

Uwaga.

1) Protokół połączeniowy (szczególnie w przypadku połączenia między różnymi 
komputerami) jest

    wielokrotnie wolniejszy od bezpołączeniowego (potwierdzenia, sprawdzanie 
sum kontrolnych ...).

2) W przypadku protokołu bezpołączeniowego programista musi sam 
zabezpieczać swoje programy

    przed skutkami jego zawodności.

3) Zawodność transmisji bezpołączeniowej objawia się zwykle tylko w przypadku 
przesłań pomiędzy

    oddzielnymi komputerami (i to nie w obrębie jednej sieci lokalnej).

Typowym przykładem protokołu połączeniowego jest TCP 

(Transmission Control 

Protocol)

, protokołu

bezpołączeniowego - UDP 

(User Datagram Protocol)

, oba działające na bazie 

(bezpołączeniowego)

protokołu niższego poziomu IP 

(Internet Protocol)

.

background image

 

 

Podstawowe funkcje operujące na gniazdach

Typowa kolejność wywoływania funkcji w przypadku transmisji połączeniowych i 
bezpołączeniowych

(wg W.R. Stevensa):

    
serwer

  socket 
( );

  bind 
( );

listen 
( );

accept ( 
);

  read ( );

przetwarza
nie

     danych

 write 
( );

klient

 socket ( 
);

connect ( );

 write ( );

 read ( );

oczekiwanie na 
uzys-

kanie połączenia

oczekiwanie na

wyniki

a) transmisja 
połączeniowa

dane

wyniki

background image

 

 

    
serwer

  socket 
( );

  bind 
( );

recvfrom ( 
);

przetwarza
nie

     danych

 sendto 
( );

klient

 socket ( 
);

     bind ( );

 sendto 
( );

recvfrom 
( );

oczekiwanie na 

dane

oczekiwanie na

wyniki

b) transmisja 
bezpołączeniowa

dane

wyniki

Gniazda zamykamy zwykle funkcją close( )  (tak, jak zwykłe pliki).

background image

 

 

Istotnym problemem dla transmisji połączeniowej pomiędzy oddzielnymi 
komputerami jest 

synchronizacja pomiędzy zapisem (write) i odczytem (read). Problem ten nie 
występuje dla połączeń

w dziedzinie Unixa, bo zapis i odczyt są wykonywane jako niepodzielne operacje 
przez to samo jądro.

W przypadku połączenia w dziedzinie Internetu zwykle funkcja read wykonywana 
jest w pętli aż do

upewnienia się przez program, że już wszystkie przekazywane dane zostały 
przeczytane.

Uwaga.

Dla zapewnienia komunikacji przez gniazda procesów spokrewnionych zwykle 
stosujemy funkcję

socketpair, która generuje deskryptory pary gniazd (podobnie, jak funkcja pipe) i 
od razu ustala

pomiędzy nimi połączenie. Dalsze postępowanie wygląda również podobnie, jak w 
przypadku łącz

nienazwanych (utworzenie procesów potomnych dziedziczących deskryptory, 
zamykanie niepo-

trzebnych deskryptorów itd.).

background image

 

 

int socketpair (int rodzina, int typ, int protokół, int deskr [2] );

Zwraca: 0 w przypadku sukcesu;

              -1 w przypadku błędu.

rodzina - oznaczenie rodziny protokołów komunikacyjnych (w tym przypadku 
może być tylko

                PF_UNIX )                             

( uwaga: oznaczenie AF_UNIX jest 

równoważne )

typ - oznaczenie typu gniazd:  SOCK_STREAM (gniazdo strumieniowe)

                                         lub   SOCK_DGRAM  (gniazdo datagramowe)

protokół - zwykle podajemy wartość 0, żeby system dobrał wartości domyślne 
(TCP dla strumieni,

                 UDP dla datagramów)

deskr [2] - para deskryptorów gniazd (podobnie, jak dla pary łącz 
nienazwanych)

Działanie: tworzy parę gniazd nienazwanych i w przypadku gniazd 
strumieniowych otwiera

                 połączenie pomiędzy nimi.

background image

 

 

int socket ( int rodzina, int typ, int protokół );

Zwraca: deskryptor gniazda w przypadku sukcesu;

              -1 w przypadku błędu.

Znaczenie argumentów - takie samo, jak dla funkcji socketpair.

Działanie: tworzy gniazdo nienazwane podanego typu.

int bind (int deskryptor, struct sockaddr adres, int długość );

Zwraca: 0 w przypadku sukcesu;

              -1 w przypadku błędu.

deskryptor - wartość zwrócona przez funkcję socket

adres - wskaźnik do struktury zawierającej adres gniazda (interpretacja 
zawartości tej struktury

            zależy od używanego protokołu)

background image

 

 

długość - długość adresu gniazda (nie uwzględniając znaku pustego kończącego 
łańcuch)

Działanie: robi z gniazda nienazwanego gniazdo nazwane (w przypadku 
operowania w dziedzinie

                 Unixa umiejscawia je w strukturze plików, zaś w przypadku 
operowania w dziedzinie

                 Internetu wiąże je z adresem IP danego komputera i numerem 
portu).

Uwaga.

1) Nie ma potrzeby bezpośredniego wpisywania adresu IP własnego komputera 
(można to zrobić

    automatycznie).

2) Numer portu może być wpisany bezpośrednio (jest to wskazane w przypadku 
serwera, którego

    adres musi być ogólnie znany), lub wygenerowany automatycznie przez 
wpisanie wartości 0

    (jest to zwykle praktykowane w przypadku klienta).

background image

 

 

int listen (int deskryptor, int rozmkolejki);

Zwraca: 0 w przypadku sukcesu;

              -1 w przypadku błędu.

deskryptor - jak poprzednio

rozmkolejki - maksymalny rozmiar kolejki zgłoszeń klientów (typowa wartość 
wynosi 5)

Działanie: tworzy kolejkę dla zgłoszeń klientów chcących połączyć się z danym 
gniazdem.

Jeśli w którymś momencie kolejka przepełni się, klienci chcący nawiązać 
połączenie będą

otrzymywali sygnał błędu.

background image

 

 

int connect (int deskryptor, struct socksddr serwadres, int długość);

Zwraca: 0 w przypadku sukcesu;

              -1 w przypadku błędu.

deskryptor - jak poprzednio

serwadres - wskaźnik do struktury zawierającej adres gniazda serwera (o postaci 
zależnej od

                   używanego protokołu)

długość - długość adresu gniazda serwera

Działanie: spowodowanie (jeśli to możliwe) wpisania zgłoszenia klienta do kolejki 
serwera,

                 a następnie nawiązanie połączenia pomiędzy gniazdem klienta a 
gniazdem serwera.

background image

 

 

int accept (int deskryptor, struct sockaddres kliadres, int długość);

Zwraca: nową wartość deskryptora gniazda w przypadku sukcesu;

              -1 w przypadku błędu.

deskryptor - jak poprzednio

kliadres - wskaźnik do struktury, do której w wyniku wykonania funkcji zostanie 
wpisany adres

                gniazda klienta, którego żądanie połączenia oczekuje jako 
najwcześniejsze w kolejce

długość - w wyniku wykonania funkcji zostaje tu wpisana długość adresu gniazda 
klienta

Działanie: jeśli kolejka jest pusta, proces serwera zostaje zawieszony. Kiedy w 
kolejce pojawią się

                 zgłoszenia, pobrane jest najwcześniejsze z nich, utworzone jest nowe 
gniazdo serwera

                 i połączone jest z gniazdem klienta (którego adres gniazda i jego 
długość są zwracane

                 przez drugi i trzeci argument funkcji).

background image

 

 

int sendto (int deskryptor, char bufor, int wielkość, int flagi, strust sockaddr 

adres, int długość);

Zwraca: liczbę faktycznie wysłanych bajtów w przypadku sukcesu;

              -1n w przypadku błędu.

deskryptor - jak poprzednio

bufor - wskaźnik do bufora zawierającego dane do wysłania

wielkość - liczba bajtów danych

flagi - różne znaczenia (mogą na przykład nadać danym wyższy priorytet)

adres - wskaźnik do struktury zawierającej adres gniazda odbiorcy

długość - długość adresu gniazda odbiorcy

Działanie: wysyła porcję informacji zgromadzonej w buforze z gniazda o danym 
deskryptorze do

                  gniazda o podanym adresie

background image

 

 

int recvfrom (int deskryptor, char bufor, int wielkość, int flagi, struct sockaddr 

adres, int długość);

Zwraca: liczbę faktycznie odebranych bajtów w przypadku sukcesu;

              -1 w przypadku błędu.

deskryptor - jak poprzednio

bufor - wskaźnik do bufora przeznaczonego do przyjęcia danych

wielkość - rozmiar tego bufora

flagi - różne znaczenia (mogą na przykład spowodować skopiowanie zamiast 
przeniesienia danych

           do bufora)

adres - wskaźnik do struktury, do której w wyniku wykonania funkcji zostaje 
wpisany adres gniazda 

            nadawcy

długość - wskaźnik do zmiennej, do której w wyniku działania funkcji zostaje 
wpisana długość adresu

                gniazda nadawcy

Działanie: zawiesza proces odbiorcy, jeśli nie ma żadnych danych do odebrania. 
Jeśli są, pobiera do

                 bufora dane wysłane do gniazda o danym deskryptorze. Adres gniazda 
nadawcy i jego

                 długość zostają zwrócone przez dwa ostatnie argumenty funkcji.


Document Outline