Różnice między wybraną wersją a wersją aktualną.
| Both sides previous revision Poprzednia wersja Nowa wersja | Poprzednia wersja | ||
|
sk2:sockets_full [2024/10/07 20:25] jkonczak |
sk2:sockets_full [2025/10/16 22:26] (aktualna) jkonczak |
||
|---|---|---|---|
| Linia 1: | Linia 1: | ||
| ====== Interface gniazd BSD (1/2) ====== | ====== Interface gniazd BSD (1/2) ====== | ||
| - | Schemat kolejności wywołać funkcji bibliotecznych: \\ | + | Schemat kolejności wywołań funkcji bibliotecznych: \\ |
| <html><object id="svg-object" data="/jkonczak/_media/sk2:sockets.svg" type="image/svg+xml"></object></html>\\ | <html><object id="svg-object" data="/jkonczak/_media/sk2:sockets.svg" type="image/svg+xml"></object></html>\\ | ||
| - | (Porównaj z: [[http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/bsdsockets.html#rys-1]].) | + | ----- |
| + | Funkcja ''socket'' i kolejność wywoływania funkcji z API gniazd na deskryptorze zwróconym przez tę funkcję:\\ | ||
| + | <html><object id="svg-object" data="/jkonczak/_media/sk2:gniazdo-diagram.svg" type="image/svg+xml"></object></html> | ||
| ===== Serwer TCP ===== | ===== Serwer TCP ===== | ||
| Aby oczekiwać na przychodzące połączenia TCP konieczne jest stworzenie gniazda do odbierania nowych połączeń. Takie gniazdo, tzw. **gniazdo nasłuchujące** (listening), nie umożliwia odbierania ani wysyłania danych - **pozwala tylko na odbieranie przychodzących połączeń** i tworzy **dla każdego nowego połączenia kolejne gniazdo** reprezentujące to odebrane połączenie. | Aby oczekiwać na przychodzące połączenia TCP konieczne jest stworzenie gniazda do odbierania nowych połączeń. Takie gniazdo, tzw. **gniazdo nasłuchujące** (listening), nie umożliwia odbierania ani wysyłania danych - **pozwala tylko na odbieranie przychodzących połączeń** i tworzy **dla każdego nowego połączenia kolejne gniazdo** reprezentujące to odebrane połączenie. | ||
| Linia 15: | Linia 18: | ||
| **Wywołanie funkcji ''listen'' nakazuje systemowi operacyjnemu czekać na połączenia** (porównaj z: ''connect''). Wykonanie funkcji listen jest natychmiastowe – ta funkcja nie czeka na połączenie, tylko informuje system operacyjny że nowe połączenia przychodzące na ustawiony adres mają być kierowane na to gniazdo. \\ | **Wywołanie funkcji ''listen'' nakazuje systemowi operacyjnemu czekać na połączenia** (porównaj z: ''connect''). Wykonanie funkcji listen jest natychmiastowe – ta funkcja nie czeka na połączenie, tylko informuje system operacyjny że nowe połączenia przychodzące na ustawiony adres mają być kierowane na to gniazdo. \\ | ||
| - | Argumentem funkcji ''listen'' jest ilość nowych połączeń które czekają w kolejce na odebranie (tj. połączeń dla których nie wykonano jeszcze funkcji ''accept'')((Argument "backlog" funkcji ''listen'' powinien się zawierać w zakresie ''1''÷''SOMAXCONN'' (w tej chwili równe 128 w Linuksie) i jest traktowany jako podpowiedź, tzn. system operacyjny może używać innego limitu niż podany w argumencie ''listen''.)). | + | Argumentem funkcji ''listen'' jest ilość nowych połączeń które czekają |
| + | w kolejce na odebranie (tj. połączeń dla których nie wykonano jeszcze funkcji | ||
| + | ''accept'')((Argument "backlog" funkcji ''listen'' powinien się zawierać w | ||
| + | zakresie ''1''÷''SOMAXCONN'' (w tej chwili o wartości [[https://elixir.bootlin.com/linux/v6.17.1/source/include/linux/socket.h#L298|4096 w Linuksie]]) i jest traktowany jako podpowiedź, tzn. system operacyjny może używać innego limitu niż podany w argumencie ''listen''.)). | ||
| Do odebrania nowych połączeń używa się funkcji ''accept(…)''. **Funkcja ''accept'' zwraca nowe gniazdo** reprezentujące nawiązane połączenie. | Do odebrania nowych połączeń używa się funkcji ''accept(…)''. **Funkcja ''accept'' zwraca nowe gniazdo** reprezentujące nawiązane połączenie. | ||
| Linia 30: | Linia 36: | ||
| * zakończy program. | * zakończy program. | ||
| - | ~~Zadanie.#~~a Krótko((https://github.com/torvalds/linux/search?q=TCP_TIMEWAIT_LEN)) po zamknięciu programu serwera sprawdź poleceniem ''netstat -tpn'' oraz ''ss -atnop'' w jakim stanie jest połączenie. Przypomnij sobie co oznacza ten stan – [[https://tools.ietf.org/html/rfc793#page-22|RFC793]]. Spróbuj w tym czasie uruchomić ponownie program serwera. \\ ~~Zadanie.#2~~b Ustaw przed wywołaniem ''bind'' opcję ''SO_REUSEADDR'' gniazda (kod poniżej) i powtórz zadanie 2a.<code cpp>const int one = 1; | + | ~~Zadanie.#~~a Krótko((https://elixir.bootlin.com/linux/v6.17.1/source/include/net/tcp.h#L128)) po zamknięciu programu serwera sprawdź poleceniem ''netstat -tpn'' oraz ''ss -atnop'' w jakim stanie jest połączenie. Przypomnij sobie co oznacza ten stan – [[https://tools.ietf.org/html/rfc793#page-22|RFC793]]. Spróbuj w tym czasie uruchomić ponownie program serwera. \\ ~~Zadanie.#2~~b Ustaw przed wywołaniem ''bind'' opcję ''SO_REUSEADDR'' gniazda (kod poniżej) i powtórz zadanie 2a.<code cpp>const int one = 1; |
| setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));</code> | setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));</code> | ||
| <small> | <small> | ||
| Linia 42: | Linia 48: | ||
| Funkcja ''accept'' (podobnie jak wprowadzana za chwilę ''recvfrom'') może przekazać informację o adresie z którego nawiązano połączenie. W tym celu należy jej podać: | Funkcja ''accept'' (podobnie jak wprowadzana za chwilę ''recvfrom'') może przekazać informację o adresie z którego nawiązano połączenie. W tym celu należy jej podać: | ||
| * gdzie ma zapisać ten adres – tj. podać adres struktury sockaddr (drugi argument) | * gdzie ma zapisać ten adres – tj. podać adres struktury sockaddr (drugi argument) | ||
| - | * adres zmiennej, która w momencie wywołania ''accept'' ma wpisany rozmiar przekazanej struktury (trzeci argument)<html><small></html><code cpp> | + | * adres zmiennej, która w momencie wywołania ''accept'' ma wpisany rozmiar przekazanej struktury (trzeci argument)<html><small style="user-select: none;"></html><code cpp> |
| sockaddr_in nazwa_zmiennej; | sockaddr_in nazwa_zmiennej; | ||
| socklen_t inna_zmienna = sizeof(nazwa_zmiennej); | socklen_t inna_zmienna = sizeof(nazwa_zmiennej); | ||
| Linia 98: | Linia 104: | ||
| Tworząc gniazdo UDP należy użyć następujących argumentów: | Tworząc gniazdo UDP należy użyć następujących argumentów: | ||
| + | <html><div style=margin-top:-1.2em></div></html> | ||
| <code cpp> | <code cpp> | ||
| socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP) | socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP) | ||
| Linia 105: | Linia 112: | ||
| UDP nie nawiązuje połączenia – do odbioru i wysyłania wiadomości należy używać funkcji ''sendto'' i ''recvfrom'' (lub ''recv''/''read'', jeśli nie obchodzi nas nadawca). \\ W formie ułatwienia BSD socket API pozwala działać gniazdom UDP w trybie pseudo-połączeniowym - tzn. można wywołać funkcję ''connect'' (która ustali adres odbiorcy) i dalej korzystać z ''send'' / ''write''. | UDP nie nawiązuje połączenia – do odbioru i wysyłania wiadomości należy używać funkcji ''sendto'' i ''recvfrom'' (lub ''recv''/''read'', jeśli nie obchodzi nas nadawca). \\ W formie ułatwienia BSD socket API pozwala działać gniazdom UDP w trybie pseudo-połączeniowym - tzn. można wywołać funkcję ''connect'' (która ustali adres odbiorcy) i dalej korzystać z ''send'' / ''write''. | ||
| - | Przykład użycia funkcji ''sendto'' i ''recvfrom'': <html><small></html><code cpp> | + | Przykład użycia funkcji ''sendto'' i ''recvfrom'': |
| + | <html><div style=margin-top:-1.2em></div></html> | ||
| + | <html><small style="user-select: none;"></html><code cpp> | ||
| sockaddr_in nazwa_zmiennej {...}; | sockaddr_in nazwa_zmiennej {...}; | ||
| socklen_t inna_zmienna = sizeof(nazwa_zmiennej); | socklen_t inna_zmienna = sizeof(nazwa_zmiennej); | ||