Różnice między wybraną wersją a wersją aktualną.
Both sides previous revision Poprzednia wersja Nowa wersja | Poprzednia wersja | ||
sk2:sockets_caveats [2023/10/17 12:35] jkonczak [Funkcje blokujące / nieblokujące] |
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> | + | <small> |
- | 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, możesz wykonać: |
- | + | <html><div style="margin-top:-1.2em"></html> | |
- | 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 | + | <code> |
+ | wget -O - http://www.cs.put.poznan.pl/jkonczak/_media/sk2:l3.tar.xz | tar xvJ | ||
cd l3 | cd l3 | ||
Linia 15: | Linia 19: | ||
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 i klienta można uruchamiać na jednym komputerze, łącząc się z 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) i 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 z 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ć np. tutaj: 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 podano), 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 systemowym 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 (por. z definicją [[https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_77|blocking]] z POSIX). | + | 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/9799919799/basedefs/V1_chap03.html#tag_03_48|blocking]] z POSIX). | ||
\\ | \\ | ||
Przykłady funkcji które mogą się zablokować to: | Przykłady funkcji które mogą się zablokować to: | ||
Linia 40: | Linia 76: | ||
* ''accept'' (czeka aż przyjdzie nowe połączenie) | * ''accept'' (czeka aż przyjdzie nowe połączenie) | ||
* ''gethostbyname'' / ''getaddrinfo'' (czeka na odpowiedź od serwera nazw, o ile zaszła konieczność odpytania). | * ''gethostbyname'' / ''getaddrinfo'' (czeka na odpowiedź od serwera nazw, o ile zaszła konieczność odpytania). | ||
- | <html><div style="margin-top:-1.4em"></html> | + | <html><div style="margin-top:-1.4em"></div></html> |
Funkcje wykonujące tylko operacje lokalne są nieblokujące. | 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 takich funkcji (sieciowych) to: ''socket'', ''bind'', ''listen'', ''setsockopt'', ''gethostbyname'' / ''getaddrinfo'' (o ile funkcja nie odpytywała serwera nazw, co można wymusić odpowiednimi flagami).\\ |
- | <html></div></html> | + | <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: | + | \\ |
- | <html><div style="margin:-1.4em 0 -1.4em 0"></html> | + | **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(fd, F_GETFL) | O_NONBLOCK)</code> | <code cpp>fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK)</code> | ||
<html></div></html> | <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: | 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> | + | <html><div style="margin-top:-1.2em"></html> |
<code cpp>recv(fileDescriptor, buff, buffSize, MSG_DONTWAIT);</code> | <code cpp>recv(fileDescriptor, buff, buffSize, MSG_DONTWAIT);</code> | ||
<html></div></html> | <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. | + | **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> | <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]]'') | + | 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> | </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 82: | 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 i 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' i '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> | ||
- | <html><small></html> | + | <small> |
- | + | Dla TCP możesz zobaczyć gromadzone przez system operacyjny informacje używane | |
- | 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: \\ | + | do dobrania prędkości do możliwości łącza używając polecenia ''ss'' z |
- | ''ss -atip'' | + | przełącznikiem ''i'', np: ''ss -atip'' |
- | + | </small> | |
- | <html></small></html> | + | |
- | //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 163: | 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. |