Narzędzia użytkownika

Narzędzia witryny


sk2:sockets_caveats

Różnice

Różnice między wybraną wersją a wersją aktualną.

Odnośnik do tego porównania

Both sides previous revision Poprzednia wersja
Nowa wersja
Poprzednia wersja
sk2:sockets_caveats [2019/11/15 12:53]
jkonczak
sk2:sockets_caveats [2024/10/31 11:42] (aktualna)
jkonczak [Funkcje blokujące / nieblokujące]
Linia 2: Linia 2:
  
 ===== Kody źródłowe do zadań ===== ===== Kody źródłowe do zadań =====
-Z doświadczeń lat ubiegłych wynika, że sprawne pisanie prostych programów stanowi problem dla części uczestników zajęć. Stąd w trakcie tych zajęć proszę korzystać z gotowych kodów źródłowych,​ analizując je przez uruchomieniem:​+Z doświadczeń lat ubiegłych wynika, że sprawne pisanie prostych programów 
 +stanowi problem dla części uczestników zajęć. 
 +Stąd w trakcie tych zajęć proszę korzystać z gotowych kodów źródłowych,​ 
 +analizując je przez uruchomieniem:​
  
-**{{:​sk2:​l3.tar.xz|Kody źródłowe zadań ​1-9,12}}**+**{{:​sk2:​l3.tar.xz|Kody źródłowe zadań}}**
  
-<​html>​<​small>​</​html>​Aby zbudować programy ręcznie, ​wykonaj:<​code>​mkdir build+<​small>​ 
 +Aby ściągnąć i zbudować programy ręcznie, ​możesz wykonać: 
 +<​html><​div style="​margin-top:​-1.2em"></​html>​ 
 +<​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 ..
-make</​code>​ +make 
-<​html>​</​small>​</html>+</​code>​ 
 +</​small>​ 
 + 
 +Programy serwera i klienta można uruchamiać na jednym komputerze, łącząc się z klienta na adres 127.0.0.1 lub //​localhost//​.
  
-Programy serwera ​klienta można uruchamiać na jednym komputerze, łącząc się klienta ​na adres 127.0.0.1 lub localhost.+===== Bufory w komunikacji sieciowej ===== 
 +Kolejne etapy "​podróży"​ danych od karty sieciowej do aplikacji użytkownika:​ 
 +<​html><​div style="​margin-top:​-1.4em"></​html>​ 
 +  - pakiet odbierany przez kartę sieciową (NIC) jest najpierw zapisywany do wewnętrznego bufora NIC, 
 +  - NIC kopiuje dane ze swoich buforów (używając DMA) do pamięci głównej (RAM) wywołuje przerwanie,​ 
 +  - system operacyjny (OS) w procedurze obsługi przerwania przetwarza kolejno każdy z odczytanych pakietów, 
 +  - dla pakietów które przenoszą dane do już istniejącego połączenia, \\ OS kopiuje dane do powiązanego ​gniazdem systemowego bufora odbiorczego,​ 
 +  - kiedy program zażądał od systemu operacyjnego odczyt danych i są dane w buforze systemowym: \\ OS kopiuje dane z systemowego bufora odbiorczego do bufora podanego przez użytkownika. 
 +W podobny sposób dane są buforowane przy wysyłaniu – podany przez użytkownika 
 +bufor kopiowany jest do powiązanego z gniazdem bufora systemowego,​ z którego 
 +dane są przenoszone do powiązanej z kartą kolejki danych do wysłania, skąd 
 +trafiają do kolejki ​na karcie. 
 +   
 +<​small>​Więcej można doczytać nptutaj: https://​wiki.linuxfoundation.org/​networking/​kernel_flow</​small>​
  
 ===== Blokowanie i wartości zwracane przez funkcje odbierające / wysyłające dane  ===== ===== Blokowanie i wartości zwracane przez funkcje odbierające / wysyłające dane  =====
 ==== 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ą 
-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.+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 
 +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.
  
 ==== 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ą 
-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).+się //​zablokować//,​ tj. po ich wywołaniu program zatrzymuje się do czasu 
 +zakończenia żądanej operacji ​(porz definicją 
 +[[https://​pubs.opengroup.org/​onlinepubs/​9799919799/​basedefs/​V1_chap03.html#​tag_03_48|blocking]] z POSIX). 
 +\\ 
 +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"></​div></​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, co można wymusić odpowiednimi flagami).\\ 
 +<​small>​Funkcja zamykająca połączenie – ''​shutdown''​ – jest nieblokująca,​ natomiast funkcja zamykająca gniazdo – ''​close''​ – może się blokować dla gniazd połączeniowych dla których ustawiono na nim opcję SO_LINGER na czas wysłania danych z bufora systemowego,​ ale nie dłużej niż ilość sekund podana w opcji SO_LINGER[[https://​pubs.opengroup.org/​onlinepubs/​9799919799/​functions/​close.html|[1]]].</​small>​
  
 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_NONBLOCK1);</​code>​ +\\ 
-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>​+**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: 
 +<​html><​div style="​margin:​-1.2em 0 -1.2em 0"></​html>​ 
 +<code cpp>​fcntl(fd, F_SETFL, ​fcntl(fdF_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.2em"></​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>​
 +Według standardu POSIX z 2024 roku tryb nieblokujący można ustawiać też 
 +sumując ostatni argument funkcji ''​socket''​ ze stałą ''​SOCK_NONBLOCK''​
 +[[https://​pubs.opengroup.org/​onlinepubs/​9799919799/​functions/​socket.html|[1]]],​
 +jak i
 +ustawiając flagę ''​SOCK_NONBLOCK''​ w polu flag funkcji ''​accept4''​
 +[[https://​pubs.opengroup.org/​onlinepubs/​9799919799/​functions/​accept.html|[2]]]
 +(wersja funkcji ''​accept''​ z dodatkowym polem flag).
 +Linux od dawna wspiera stałą ''​SOCK_NONBLOCK''​.
 +</​small>​
 ==== Zadania ===== ==== Zadania =====
  
-//Zadanie ​1.// Stwórz serwer TCP, który w pętli akceptuje nowe połączenia i je ignoruje (tzn. nie odbiera ani nie wysyła danych, nie zamyka połączeń).+~~Zadanie.#~~ Stwórz serwer TCP, który w pętli akceptuje nowe połączenia 
 +i je ignoruje (tzn. nie odbiera ani nie wysyła danych, nie zamyka połączeń).
  
-//Zadanie ​2.// Stwórz klienta TCP który:+~~Zadanie.#~~ Stwórz klienta TCP który:
   * łączy się pod podany adres   * łączy się pod podany adres
   * w pętli:   * w pętli:
     * wysyła dane     * wysyła dane
     * wypisuje kolejny numer i ilość wysłanych danych     * wypisuje kolejny numer i ilość wysłanych danych
 +<​html><​div style="​margin-top:​-1.4em"></​div></​html>​
 Połącz klienta do serwera z zadania 1. Połącz klienta do serwera z zadania 1.
  
-<​html>​<small></​html+<​small>​ 
-Możesz sprawdzić poleceniem ''​netstat'',​ np. ''​netstat -tn'',​ ile bajtów jest w buforze odbiorczym/​nadawczym (Recv-Q i Send-Q) +Możesz sprawdzić poleceniem ''​netstat'',​ np. ''​netstat -tn'',​ ile bajtów jest w 
-<​html>​</small></​html>+buforze odbiorczym/​nadawczym (kolumny ​Recv-Q i Send-Q). 
 +</​small>​
  
-//Zadanie ​3.// Stwórz klienta TCP który:+~~Zadanie.#~~ Stwórz klienta TCP który:
   * łączy się pod podany adres   * łączy się pod podany adres
   * ustawia tryb nieblokujący   * ustawia tryb nieblokujący
Linia 55: Linia 137:
     * wypisuje kolejny numer i ilość wysłanych danych     * wypisuje kolejny numer i ilość wysłanych danych
     * jeśli wysłał mniej danych niż chciał, kończy się     * jeśli wysłał mniej danych niż chciał, kończy się
 +<​html><​div style="​margin-top:​-1.4em"></​div></​html>​
 Połącz klienta do serwera z zadania 1.((W tym zadaniu warto porównać wynik dla klienta z lokalnego komputera i ze zdalnego komputera)) Połącz klienta do serwera z zadania 1.((W tym zadaniu warto porównać wynik dla klienta z lokalnego komputera i ze zdalnego komputera))
  
 ===== Protokół strumieniowy / zorientowany na wiadomość ===== ===== Protokół strumieniowy / zorientowany na wiadomość =====
  
-//Zadanie ​4.// Napisz serwer TCP który po odebraniu połączenia,​ do jego zamknięcia,​ wykonuje w pętli:+~~Zadanie.#~~ Napisz serwer TCP który po odebraniu połączenia,​ do jego 
 +zamknięcia,​ wykonuje w pętli:
   * odbiera do 10 bajtów   * odbiera do 10 bajtów
   * wypisuje ile bajtów odebrał i co odebrał   * wypisuje ile bajtów odebrał i co odebrał
  
-//Zadanie ​5a.// Napisz klienta TCP który wysyła cały alfabet po jednej literze. Podłącz się (wielokrotnie) do serwera z zadania 4. \\ +~~Zadanie.#~~a Napisz klienta TCP który wysyła cały alfabet po jednej literze. 
-//Zadanie ​5b.// Napisz klienta TCP który wysyła wielokrotnie((10-20 razy)) po kilkanaście liter. Podłącz się (wielokrotnie) do serwera z zadania 4. \\ +Podłącz się (wielokrotnie) do serwera z zadania 4. \\ 
-<​html><​small></​html>​(W zadaniach 5a i 5b łącz się do swojego komputera.)<​html></​small></​html>​+~~Zadanie.#5~~b Napisz klienta TCP który wysyła wielokrotnie((10-20 razy)) po 
 +kilkanaście liter. Podłącz się (wielokrotnie) do serwera z zadania 4.
  
-//Zadanie ​6.// Powtórz zadanie 4 dla UDP.+~~Zadanie.#~~ Powtórz zadanie 4 dla UDP. \\ 
 +~~Zadanie.#​~~ Powtórz zadanie 5a i 5b dla UDP.
  
-//Zadanie 7.// Powtórz zadanie 5a 5b dla UDP.+<​small>​ 
 +~~Zadanie.#​~~ Napisz klienta TCP który w wielokrotnie:​ 
 +<​html><​div style="​margin-top:​-1.4em"><​/div></html> 
 +  * losuje wielkość tablicy znaków do wysłania (w zakresie 1500 do 3500) 
 +  * wypełnia tablicę znakiem ''​.'',​ wstawia ''​b''​ na jej początek, ''​e''​ na koniec 
 +  * wysyła całą tablicę 
 +  * czeka 10ms 
 +<​html><​div style="​margin-top:​-1.4em"><​/div></html> 
 +Następnie napisz serwer TCP który po odebraniu połączenia,​ do jego 
 +zamknięcia,​ w pętli odbiera do 1MB danych, a następnie sprawdza w każdej odebranej porcji 
 +na jakich pozycjach znajdują się znaki '​b' ​'​e'​.\\ 
 +Przed uruchomieniem programu, zmień ustawienia swojego interfejsu sieciowego poleceniami:​ 
 +<​html><​div style="​margin-top:​-1.4em"></​div></​html>​ 
 +  ip link set lo mtu 1500 
 +  tc qdisc add dev lo root netem delay 2ms 3ms 90% distribution pareto 
 +<​html><​div style="​margin-top:​-1.4em"></​div></​html>​ 
 +Aby wrócić do domyślnych ustawień, wpisz: 
 +<​html><​div style="​margin-top:​-1.4em"></​div></​html>​ 
 +  ip link set lo mtu 65535 
 +  tc qdisc del dev lo root 
 +</​small>​
  
-//Zadanie ​8.// Jak w strumieniu danych – czyli w połączeniu TCP – przesyłać dane tak by serwer wiedział kiedy cała wiadomość dotrze?+~~Zadanie.#~~ Jak w strumieniu danych – czyli w połączeniu TCP – przesyłać dane 
 +tak by serwer wiedział kiedy cała wiadomość dotrze?
  
-//Zadanie ​9.// Jak w protokole UDP zapewnić, że cała wiadomość została odebrana?+~~Zadanie.#~~ Jak w protokole UDP zapewnić, że cała wiadomość została odebrana?
  
 ===== 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.#~~ Wykonaj z roota poniższe polecenie, które spowoduje pomieszanie 
 +kolejności pakietów wysłanych przez interfejs ''​lo''​
 +\\ 
 +Następnie uruchom ponownie programy z zadań 4÷7. 
 +<​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.#~~ Przygotuj program, który wyśle 1000 pakietów UDP zawierających 
 +kolejne liczby.
  
-//Zadanie ​12.// Przygotuj program, który wyśle 1000 pakietów UDP zawierających kolejne liczby. +~~Zadanie.#~~ 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>​
 +
 +<​small>​
 +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''​
 +</​small>​
  
-//Zadanie ​14.// Jak w protokole UDP radzić sobie z kolejnością pakietów? Jak radzić sobie ze zgubieniem pakietów?+~~Zadanie.#~~ Jak w protokole UDP radzić sobie z kolejnością pakietów? 
 +Jak radzić sobie ze zgubieniem pakietów?
  
 ===== Opcje gniazd ===== ===== Opcje gniazd =====
Linia 129: Linia 253:
 </​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.
  
sk2/sockets_caveats.1573818795.txt.gz · ostatnio zmienione: 2019/11/15 12:53 przez jkonczak