Różnice między wybraną wersją a wersją aktualną.
Both sides previous revision Poprzednia wersja Nowa wersja | Poprzednia wersja | ||
sk2:sockets_caveats [2019/11/15 12:53] jkonczak |
sk2:sockets_caveats [2023/10/17 12:45] (aktualna) jkonczak [Opcje gniazd] |
||
---|---|---|---|
Linia 6: | Linia 6: | ||
**{{:sk2:l3.tar.xz|Kody źródłowe zadań 1-9,12}}** | **{{:sk2:l3.tar.xz|Kody źródłowe zadań 1-9,12}}** | ||
- | <html><small></html>Aby zbudować programy ręcznie, wykonaj:<code>mkdir build | + | <html><small></html> |
+ | Uwaga: jeśli korzystasz z innej biblioteki niż glibc, ściągnij też plik {{:sk2:error.h|}} - w programach jest użyte niestandardowa funkcja [[https://linux.die.net/man/3/error|error]] z glibc, a podlinkowany plik jest uproszczoną implementacją tej funkcji. | ||
+ | |||
+ | Aby ściągnąć i zbudować programy ręcznie, wykonaj:<code>wget -O - http://www.cs.put.poznan.pl/jkonczak/_media/sk2:l3.tar.xz | tar xvJ | ||
+ | cd l3 | ||
+ | |||
+ | mkdir build | ||
cd build | cd build | ||
cmake .. | cmake .. | ||
Linia 17: | Linia 23: | ||
==== Read i Write - ilość odczytanych / zapisanych bajtów ==== | ==== Read i Write - ilość odczytanych / zapisanych bajtów ==== | ||
- | Funkcje ''read'', ''recv'',… często zwracają mniej niż podana w argumentach wielkość bufora. Powód? Mniej danych przyszło lub skończyło się miejsce w buforze odbiorczym((Rozmiar bufora odbiorczego można ustawić funkcją: ''setsockopt(socket, SOL_SOCKET, SO_RCVBUF, …)'')). Może się tak stać nawet pomimo ustawienia flagi ''MSG_WAITALL'' w funkcji ''recv'' i podobnych (żądającej odebrania dokładnie tylu bajtów ile zażądano), np. w przypadku zakończenia połączenia. | + | Funkcje ''read'', ''recv'',… często zwracają mniej niż podana w argumentach wielkość bufora. Powód? Mniej danych przyszło lub skończyło się miejsce w buforze odbiorczym((Rozmiar bufora odbiorczego można ustawić funkcją: ''setsockopt(socket, SOL_SOCKET, SO_RCVBUF, …)'')). Może się tak stać nawet pomimo ustawienia flagi ''MSG_WAITALL'' w funkcji ''recv'' i podobnych (żądającej odebrania dokładnie tylu bajtów ile podano), np. w przypadku zakończenia połączenia. |
Funkcje ''write'', ''send'',… mogą też w pewnych okolicznościach wysłać mniejszą ilość bajtów niż zażądano. | Funkcje ''write'', ''send'',… mogą też w pewnych okolicznościach wysłać mniejszą ilość bajtów niż zażądano. | ||
- | Jest to spowodowane zapełnieniem systemowego buforu wysyłania (czyli jeśli dane są dostarczane do wysłania szybciej niż można je wysyłać). Normalnie funkcje zapisujące blokują się do momentu aż w buforze będzie dość miejsca. Jeśli jednak gniazdo pracuje w trybie nieblokującym (patrz niżej), to funkcje takie jak ''write'', ''send'', … zapiszą tylko część danych jeśli w buforze skończy się miejsce. | + | Jest to spowodowane zapełnieniem systemowego buforu wysyłania (czyli jeśli dane są dostarczane do wysłania szybciej niż można je wysyłać). Normalnie funkcje zapisujące blokują się do momentu aż w buforze będzie dość miejsca. Jeśli jednak gniazdo pracuje w trybie nieblokującym (patrz niżej), to funkcje takie jak ''write'', ''send'', … zapiszą tylko część danych jeśli w buforze systemowym skończy się miejsce. |
Dlatego zarówno przy odbieraniu jak i wysyłaniu trzeba sprawdzać zwracaną ilość przetworzonych danych. | Dlatego zarówno przy odbieraniu jak i wysyłaniu trzeba sprawdzać zwracaną ilość przetworzonych danych. | ||
Linia 26: | Linia 32: | ||
==== Funkcje blokujące / nieblokujące ===== | ==== Funkcje blokujące / nieblokujące ===== | ||
- | Zasadniczo funkcje które wymagają wysłania lub odebrania danych z sieci mogą się //zablokować//, tj. po ich wywołaniu program zatrzymuje się do czasu zakończenia żądanej operacji. Przykłady takich funkcji to ''read'' / ''recv'' / ''recvfrom'' (czeka aż przyjdą dane), ''write'' / ''send'' (czeka aż zwolni się miejsce w buforze nadawczym), ''connect'' (czeka aż uda się nawiązać połączenie), ''accept'' (czeka aż przyjdzie nowe połączenie), ''gethostbyname'' / ''getaddrinfo'' (czeka na odpowiedź od serwera nazw, o ile zaszła konieczność odpytania).\\ | + | Zasadniczo funkcje które wymagają wysłania lub odebrania danych z sieci mogą się //zablokować//, tj. po ich wywołaniu program zatrzymuje się do czasu zakończenia żądanej operacji (por. z definicją [[https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_77|blocking]] z POSIX). |
- | Funkcje wykonujące tylko operacje lokalne są nieblokujące. Przykłady takich funkcji (sieciowych) to ''socket'', ''bind'', ''listen'', ''setsockopt'', ''gethostbyname'' / ''getaddrinfo'' (o ile funkcja nie odpytywała serwera nazw). | + | \\ |
+ | Przykłady funkcji które mogą się zablokować to: | ||
+ | * ''read'' / ''recv'' / ''recvfrom'' (czeka aż przyjdą dane) | ||
+ | * ''write'' / ''send'' (czeka aż zwolni się miejsce w buforze nadawczym) | ||
+ | * ''connect'' (czeka aż uda się nawiązać połączenie) | ||
+ | * ''accept'' (czeka aż przyjdzie nowe połączenie) | ||
+ | * ''gethostbyname'' / ''getaddrinfo'' (czeka na odpowiedź od serwera nazw, o ile zaszła konieczność odpytania). | ||
+ | <html><div style="margin-top:-1.4em"></html> | ||
+ | Funkcje wykonujące tylko operacje lokalne są nieblokujące. | ||
+ | \\ | ||
+ | Przykłady takich funkcji (sieciowych) to: ''socket'', ''bind'', ''listen'', ''setsockopt'', ''gethostbyname'' / ''getaddrinfo'' (o ile funkcja nie odpytywała serwera nazw). | ||
+ | <html></div></html> | ||
Można zmienić domyśle blokujące zachowanie przestawiając gniazdo w //tryb nieblokujący//. | Można zmienić domyśle blokujące zachowanie przestawiając gniazdo w //tryb nieblokujący//. | ||
- | Jeśli wykonanie żądanej funkcji na gnieździe w trybie nieblokującym nie jest możliwe bez czekania, to wykonanie funkcji nie powodzi się (zwracany jest wynik ''-1'') a zmienna ''errno'' jest ustawiana na ''EAGAIN'' lub ''EWOULDBLOCK''((Wyjątkiem jest funkcja ''connect'', która rozpoczyna nawiązywanie połączenia w tle, zwraca ''-1'' i ustawia ''errno'' na ''EINPROGRESS''.)). Gniazdo przestawia się w tryb nieblokujący podobnie jak każdy inny deskryptor pliku:<code cpp>fcntl(fileDescriptor, F_SETFL, O_NONBLOCK, 1);</code> | + | Jeśli wykonanie żądanej funkcji na gnieździe w trybie nieblokującym nie jest możliwe bez czekania, to wykonanie funkcji nie powodzi się (zwracany jest wynik ''-1'') a zmienna ''errno'' jest ustawiana na ''EAGAIN'' lub ''EWOULDBLOCK''((Wyjątkiem jest funkcja ''connect'', która rozpoczyna nawiązywanie połączenia w tle, zwraca ''-1'' i ustawia ''errno'' na ''EINPROGRESS''.)). \\ Gniazdo przestawia się w tryb nieblokujący podobnie jak każdy inny deskryptor pliku: |
- | Niektóre funkcje sieciowe (np. ''recv'', ''send'') pozwalają na ustawienie w polu flag wartości ''MSG_DONTWAIT'', która wykonuje żądaną operację w trybie nieblokującym niezależnie od tego w jakim trybie pracuje gniazdo:<code cpp>recv(fileDescriptor, buff, buffSize, MSG_DONTWAIT);</code> | + | <html><div style="margin:-1.4em 0 -1.4em 0"></html> |
+ | <code cpp>fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK)</code> | ||
+ | <html></div></html> | ||
+ | Niektóre funkcje sieciowe (np. ''recv'', ''send'') pozwalają na ustawienie w polu flag wartości ''MSG_DONTWAIT'', która wykonuje żądaną operację w trybie nieblokującym niezależnie od tego w jakim trybie pracuje gniazdo: | ||
+ | <html><div style="margin-top:-1.4em"></html> | ||
+ | <code cpp>recv(fileDescriptor, buff, buffSize, MSG_DONTWAIT);</code> | ||
+ | <html></div></html> | ||
+ | Uwaga: w BSD/POSIX socket API każde nowo utworzone gniazdo działa w trybie blokującym, nawet jeżeli gniazdo zostało utworzone w wyniku działania funkcji ''accept'' na nasłuchującym gnieździe działającym w trybie nieblokującym. | ||
+ | <small> | ||
+ | W Linuksie można ustawiać tryb nieblokujący wykorzystując też niebędące częścią standardu POSIX rozszerzenia: 1) jeżeli ostatni argument funkcji ''socket'' będzie zsumowany z ''SOCK_NONBLOCK'', to nowo stworzone gniazdo będzie w trybie nieblokującym (''[[https://man7.org/linux/man-pages/man2/socket.2.html|man 2 socket]]''), 2) jeżeli zamiast funkcji ''accept'' wykorzysta się odpowiednik tej funkcji rozszerzony o pole flag – ''accept4'' – i poda flagę ''SOCK_NONBLOCK'' (''[[https://man7.org/linux/man-pages/man2/accept4.2.html|man 2 accept4]]'') | ||
+ | </small> | ||
==== Zadania ===== | ==== Zadania ===== | ||
Linia 77: | Linia 104: | ||
===== Kolejność danych i (nie)zawodność ===== | ===== Kolejność danych i (nie)zawodność ===== | ||
//Zadanie 10.// Wykonaj z roota poniższe polecenie, które spowoduje pomieszanie kolejności pakietów wysłanych przez interfejs ''lo'': | //Zadanie 10.// Wykonaj z roota poniższe polecenie, które spowoduje pomieszanie kolejności pakietów wysłanych przez interfejs ''lo'': | ||
+ | <html><div style="margin-top:-1.4em"></html> | ||
<code> | <code> | ||
tc qdisc add dev lo root netem delay 5ms 5ms distribution normal loss 10% | tc qdisc add dev lo root netem delay 5ms 5ms distribution normal loss 10% | ||
</code> | </code> | ||
+ | <html></div></html> | ||
- | Aby przywrócić domyślne zachowanie, wpisz: | + | Aby przywrócić domyślne zachowanie po wykonaniu ćwiczeń, możesz wpisać: |
+ | <html><div style="margin-top:-1.4em"></html> | ||
<code> | <code> | ||
tc qdisc del root dev lo | tc qdisc del root dev lo | ||
</code> | </code> | ||
+ | <html></div></html> | ||
//Zadanie 11.// Uruchom ponownie programy z zadań 5 i 7. | //Zadanie 11.// Uruchom ponownie programy z zadań 5 i 7. | ||
Linia 91: | Linia 122: | ||
//Zadanie 13.// Wykonaj z roota poniższe polecenie, które spowoduje ograniczenie prędkości wysyłania pakietów i przetestuj program z poprzedniego zadania: | //Zadanie 13.// Wykonaj z roota poniższe polecenie, które spowoduje ograniczenie prędkości wysyłania pakietów i przetestuj program z poprzedniego zadania: | ||
+ | <html><div style="margin-top:-1.4em"></html> | ||
<code> | <code> | ||
tc qdisc add dev lo root tbf rate 10kbps burst 1.5kb limit 10kb | tc qdisc add dev lo root tbf rate 10kbps burst 1.5kb limit 10kb | ||
</code> | </code> | ||
+ | <html></div></html> | ||
+ | |||
+ | <html><small></html> | ||
+ | |||
+ | Dla TCP możesz zobaczyć gromadzone przez system operacyjny informacje używane do dobrania prędkości do możliwości łącza używając polecenia ''ss'' z przełącznikiem ''i'', np: \\ | ||
+ | ''ss -atip'' | ||
+ | |||
+ | <html></small></html> | ||
//Zadanie 14.// Jak w protokole UDP radzić sobie z kolejnością pakietów? Jak radzić sobie ze zgubieniem pakietów? | //Zadanie 14.// Jak w protokole UDP radzić sobie z kolejnością pakietów? Jak radzić sobie ze zgubieniem pakietów? | ||
Linia 129: | Linia 169: | ||
</code>++++ | </code>++++ | ||
- | Funkcja ''fcntl'' (''man fcntl open'') pozwala na ustawienie (''F_SETFL'') opcji ''O_NONBLOCK'' potrzebnej do nieblokującej obsługi gniazd. (''O_NONBLOCK'' można też ustawić sumując ostatni argument funkcji ''socket'' z ''SOCK_NONBLOCK''.) | + | Funkcja ''fcntl'' (''man fcntl open'') pozwala na ustawienie (''F_SETFL'') opcji ''O_NONBLOCK'' potrzebnej do nieblokującej obsługi gniazd. |