accept()
,
connect()
,
listen()
,
shutdown()
.
accept()
,
a dla klienta - connect()
. Obie te funkcje sa blokujace o
ile nie ustawimy odpowiednich opcji , np. funkcja fcntl()
.
Serwer aby mogl przyjmowac polaczenia musi, zanim wywola accept()
,
wywolac funkcje listen()
dla okreslenia wielkosci kolejki
zgloszen klientow. Zamykanie polaczenia umozliwia funkcja shutdown()
,
ktora daje pewne mozliwosci sterowania przeplywem danych.
accept()
#include <sys/types.h> #include <sys/socket.h> int accept(int fd, struct sockaddr *upeer_sockaddr, int *upeer_addrlen); ARGUMENTY: fd - deskryptor gniazda. upeer_sockaddr - wsk. na nazwe gniazda klienta. (jako dodatkowy wynik) upeer_addrlen - rozmiar nazwy upeer_sockaddr. (jako dodatkowy wynik) WYNIK: deskryptor nowo utworzonego gniazda, lub -1 - w przypadku bledu, np: EBADF - fd jest nieprawidlowym deskryptorem. ENOTSOCK - fd nie jest deskryptorem gniazda. EOPNOTSUPP - gniazdo (zwiazane z fd) nie jest strumieniowe (SOCK_STREAM). EFAULT - obszar wskazywany parametrem upeer_sockaddr nie jest dostepny dla uzytkownika. EWOULDBLOCK - operacja byla nieblokujaca, a akurat nie mozna od razu nawiazac polaczenia. Funkcja ta musi byc poprzedzona funkcjami : socket() - utworzenie gniazda, bind() - dowiazanie nazwy do gniazda, listen() - okreslenie kolejki do akceptowania polaczen.
Implementacja :
{
if (fd nie jest prawidlowym deskryptorem)
return(-EBADF);
if (fd nie jest deskryptorem gniazda)
return(-ENOTSOCK);
if (jestesmy juz polaczeni z jakims gniazdem)
return(-EINVAL);
if (nie wykonalismy przedtem listen())
return(-EINVAL);
Alokujemy nowe gniazdo i przepisujemy na nie:
- rodzaj gniazda fd,
- rodzine protokolow zwiazana z fd.
Wykonujemy funkcje rodziny duplikujaca gniazdo fd na nasze nowe gniazdo.
Wywolujemy wlasciwa funkcja rodziny, ktora ustanowi polaczenie (unix_accept(),inet_accept()
).
Pobieramy deskryptor nowego gniazda (zostanie zwrocony jako wynik).
if (upeer_sockaddr)
{
Pobieramy nazwe partnera i dlugosc tej nazwy.
Te wartosci przepisujemy na parametry upeer_sockaddr,upeer_addrlen.
}
Zwracamy nowy deskryptor.
}
Opis funkcji accept() z poziomu rodziny protokolow
(unix_accept(),
inet_accept()
) :
Sprawdzamy kolejke ze zgloszeniami polaczen klientow. Jesli nie ma
zadnych a funkcja nie jest nieblokujaca, to zostajemy blokowani az
do momentu pojawienia sie jakiegos zgloszenia. Jesli jednak funkcja
jest nieblokujaca i nie ma zgloszen to konczymy dzialanie.
Po odebraniu zgloszenia zapamietujemy na nowym gniezdzie nazwe
klienta.
Jesli jest to rodzina Unixa, to budzimy klienta, wysylamy sygnaly SIGIO
do grupy procesow zwiazanych z jego gniazdem i konczymy dzialanie.
Wpp. jesli jest to rodzina Inernetu, to wysylamy odpowiedz do klienta i
blokujemy nasz proces w oczekiwaniu na ostateczne potwierdzenie.
Dopiero po otrzymaniu potwierdzenia od klienta polaczenie jest ustanowione.
Po pomyslnym wykonaniu funkcji accept dla nowego gniazda mamy okreslona pelna asocjacje, ale dla gniazda fd nie, dzieki czemu mozemy ponownie odbierac zgloszenia od klienow na gniezdzie fd.
connect()
#include <sys/types.h> #include <sys/socket.h> int connect(int fd, struct sockaddr *uservaddr, int addrlen); ARGUMENTY: fd - deskryptor gniazda. uservaddr - adres serwera. addrlen - rozmiar adresu serwera. WYNIK: 0 - w przypadku sukcesu, lub wartosc ujemna - w przypadku bledu, np: EBADF - fd jest nieprawidlowym deskryptorem. ENOTSOCK - fd nie jest deskryptorem gniazda. EISCONN - juz jestesmy polaczeni.
Proces klienta nie musi wykonywac przedtem funkcji bind() dowiazujacej adres lokalny, bo zrobi to funkcja connect().
Implementacja :
{
if (fd nie jest poprawnym deskryptorem)
return(-EBADF);
if (fd nie jest deskryptorem gniazda)
return(-ENOTSOCK);
Jesli jestesmy polaczeni a nasze gniazdo nie jest gniazdem datagramowym
return -EISCONN;
Wykonujemy funkcje rodziny (unix_connect(), inet_connect()
), ktora nawiaze polaczenie.
Jesli funkcja rodziny zakonczyla sie pomyslnie zwracamy 0,
wpp. wartosc ujemna, ktora ta funkcja zwrocila.
}
Opis funkcji connect()
z poziomu rodziny protokolow
(unix_connect(),
inet_connect()
) :
Jesli fd jest deskryptorem gniazda datagramowego, to funkcja
okresli tylko nazwe gniazda serwera dzieki czemu dla kazdego
przesylanego datagramu nie bedziemy musieli za kazdym razem okreslac
tej wartosci. W tym przypadku funkcja connect()
nie
ustanawia zadnego polaczenia. Dla gniazd datagramowych mozna
wielokrotnie wykonywac connect()
.
Dla gniazda strumieniowego:
Wysylamy zgloszenie do serwera z nasza struktura sock
budzac go i blokujemy sie (o ile funkcja nie jest nieblokujaca) w
oczekiwaniu na odpowiedz od niego.
Jesli jest to rodzina Unixa, to po otrzymaniu odpowiedzi od serwera
polaczenie uznajemy za ustanowione i konczymy dzialanie.
W przypadku rodziny Internetu po otrzymaniu takiej odpowiedzi wysylamy
jeszcze do serwera ostateczne potwierdzenie i wtedy dopiero polaczenie
jest ustanowione.
listen()
#include <sys/types.h> #include <sys/socket.h> int listen(int fd, int backlog); ARGUMENTY: fd - deskryptor gniazda. backlog - maksymalna liczba zgloszen polaczenia z serwerem. WYNIK: 0 - w przypadku sukcesu, lub wartosc ujemna - w przypadku bledu, np: EBADF - fd nie jest poprawnym deskryptorem. ENOTSOCK - fd nie jest deskryptorem gniazda. EOPNOTSUPP - funkcjalisten()
nie jest zaimplementowana dla naszej rodziny. Implementacja: { if (fd nie jest poprawnym deskryptorem) return(-EBADF); if (fd nie jest deskryptorem gniazda) return(-ENOTSOCK); if (jestesmy polaczeni) return(-EINVAL); if (funkcja listen() jest okreslona dla naszej rodziny) { Wykonujemy funkcje rodziny (unix_listen(),inet_listen()
). if (funkcja rodziny zakonczyla sie pomyslnie) ustawiamy znacznik SO_ACCEPTCON. } Zwracamy 0 w przypadku sukcesu, lub warosc ujemna zwrocona przez funkcje rodziny. }
Opis funkcji listen()
z poziomu rodziny protokolow
(unix_listen(),
inet_listen()
) :
Funkcja ustala maksymalna dlugosc kolejki klientow czekajacych na polaczenie.
Wartosc ta jest pamietana w polu max_ack_backlog
struktury sock
i jest ograniczona stala SOMAXCONN=128
.
W przypadku rodziny Unixa gniazdo musi byc strumeniowe i miec juz
dowiazana nazwe.
W przypadku rodziny Inernetu jesli gniazdo nie ma nazwy, to
listen()
automatycznie ja dowiaze.
shutdown()
#include <sys/types.h>
#include <sys/socket.h>
int shutdown(int sockfd, int how);
ARGUMENTY:
sockfd - deskryptor gniazda.
how - wartosc okreslajaca sposob zamkniecia gniazda.
WYNIK:
0 - w przypadku sukcesu, lub
wartosc ujemna - w przypadku bledu, np:
EBADF - sockfd nie jest poprawnym deskryptorem.
ENOTSOCK - sockfd nie jest deskryptorem gniazda.
ENOTCONN - gniazdo jest niepolaczone z zadnym innym.
Implementacja:
{
if (fd nie jest poprawnym deskryptorem)
return(-EBADF);
if (fd nie jest deskryptorem gniazda)
return(-ENOTSOCK);
Wykonujemy funkcje rodziny (unix_shutdown(),inet_shutdown()
).
Zwracamy wynik funkcji rodziny.
}
Opis funkcji shutdown()
z poziomu rodziny protokolow
(unix_shutdown(),
inet_shutdown()
) :
Funkcja ustawia pole shutdown struktury sock stalymi RCV_SHUTDOWN,
SEND_SHUTDOWN,SHUTDOWN_MASK w zaleznosci od parametru how.
I tak:
how=0 - gniazdo nie moze juz przyjac zadnych komunikatow.
how=1 - gniazdo nie moze juz wysylac zadnych komunikatow.
how=2 - gniazdo nie moze przyjmowac, ani wysylac zadnych komunikatow.
include/linux/net.h
-definicje stalych i struktur.
include/linux/socket.h
-definicje stalych.
net/socket.c
-funkcje systemowe dla gniazd.
net/unix/af_unix.c
-funkcje rodziny Unixa.
net/ipv4/af_inet.c
-funkcje rodziny Internetu.