g++ --std=c++20 -Wall -O0 -g -pthread -o example example.cpp
|''c+////+''|domyślny kompilator C+////+. Zwykle link do ''g+////+'' lub ''clang+////+''|
|''c+////+ zrodlo.cpp -o prog''|kompiluje plik ''zrodlo.cpp'' do programu ''prog''|
|''c+////+ -Wall z.cpp -o p''|włącza wszystkie(("Wszystkie" oznacza wybrany zbiór ostrzeżeń o nazwie wszystkie, poza ''-Wall'' warto też dodać ''-Wextra'' i rozważyć dodanie ''-pedantic''. Szczegóły w dokumentacji kompilatora [[https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html|gcc]]/[[https://clang.llvm.org/docs/UsersManual.html#enabling-all-diagnostics|clang]])) ostrzeżenia kompilatora (''-W'' = warn, ''all'' = wszystkie)|
|''c+////+ -O0 -g z.cpp -o p''|wyłącza optymalizacje i dodaje do programu dane umożliwiające debugowanie|
|''c+////+ --std=c+////+20 z.cpp -o p''|włącza używanie standardu ISO C+////+ z 2020 roku|
|c++ -pthread z.cpp -o p
|włącza obsługę wątków standardu POSIX \\ wymagane do wersji glibc ≤ 2.33 [[https://developers.redhat.com/articles/2021/12/17/why-glibc-234-removed-libpthread|[1]]] [[https://lists.gnu.org/archive/html/info-gnu/2021-08/msg00001.html|[2]]]|
Przykładowy plik cmake:
uint16_t hostEndianessPort = 13;
uint16_t networkEndianessPort = htons(hostEndianessPort);
Opis funkcji pomocniczych – patrz ''man byteorder'' lub [[http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/bsdsockets.html#funkcje-pomocnicze|strona Darka Dwornikowskiego]]
==== Zapis adresu gniazda ====
**Protokół** trzeba podać na etapie tworzenia gniazda.
**Adres IP** oraz **numer portu** (lub ścieżkę dla gniazd unix) trzeba podać w odpowiedniej strukturze.
Do przekazywania adresu gniazda funkcje wymagają wskaźnika na strukturę **''sockaddr''** (w C – ''struct sockaddr'').
Ta struktura nie pozwala na bezpośrednie wykorzystanie. Zamiast tego dla IPv4 należy używać **''sockaddr_in''** (w C – ''struct sockaddr_in'')
Opis struktury – patrz ''man 7 ip'' i ''man netinet_in.h'' lub
[[http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/bsdsockets.html#glowne-funkcje-interfejsu-gniazd|strona Darka]]
C nie pozwala na dziedziczenie, więc zamiast tego ([[https://en.wikipedia.org/wiki/Type_punning#Sockets_example]]) struktura ''sockaddr'' ma kilka "specjalizacji" dla konkretnej rodziny adresów:
*''sockaddr_in'' (INET, czyli IPv4)
*''sockaddr_in6'' (INET6, czyli IPv6)
*''sockaddr_un'' (dla unix socket).
==== "Hello world" ====
~~Zadanie.#~~ Zmień program do odczytu pliku tak, by zamiast funkcji ''open(…)'':
* tworzył funkcją ''socket(…)'' gniazdo:
* domeny komunikacyjnej protokołu IPv4 – stała ''PF_INET'' (zamiennie ''AF_INET'')
* typu strumieniowego – stała ''SOCK_STREAM'' (czyli TCP)
* protokołu TCP – stała ''IPPROTO_TCP'' (można też podać ''0'', oznaczające domyślny protokół)
* wypełniał strukturę ''sockaddr_in'' (zdefiniowaną w pliku nagłówkowym ''netinet/in.h''):
* rodzina adresów IPv4 – stała ''AF_INET''
* port – 13 (port usługi daytime; pamiętaj o porządku bajtów - ''htons'')
* adres IP – 127.0.0.1 (localhost, do konwersji użyj ''inet_addr'' lub ''inet_aton''); \\ można też ustawić adres na stałą ''htonl(INADDR_LOOPBACK)''. \\ Uwaga: ''.sin_addr'' to struktura typu ''in_addr'' z jedną składową ''.s_addr'' typu ''uint32_t'' \\ ''inet_aton'' przyjmuje wskaźnik na strukturę, natomiast ''inet_addr'' zwraca liczbę którą trzeba przypisać składowej ''.s_addr'': \\ sockaddr_in nazwa_zmiennej;
wersja 1: nazwa_zmiennej.sin_addr.s_addr = inet_addr("8.8.8.8");
wersja 2: inet_aton("8.8.8.8", &nazwa_zmiennej.sin_addr);
* łączył się z użyciem powyższej struktury – funkcja ''connect(…)''
* funkcja ''connect'' oczekuje ''sockaddr*'' zamiast ''sockaddr_in*'' – wymagane rzutownaie
* trzeci argument funkcji connect to rozmiar struktury opisującej adres – ''sizeof()'' zmiennej lub typu
* przed wywołaniem funkcji ''close()'' zamykał połączenie funkcją ''shutdown()''; \\ w funkcji ''shutdown()'' można osobno zakończyć nadawanie i odbiór, drugi argument określa co zakończyć – ''SHUT_RD'' / ''SHUT_WR'' / ''SHUT_RDWR''
Opis funkcji – [[https://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/bsdsockets.html#glowne-funkcje-interfejsu-gniazd|strona Darka]] lub ''man 3 …'' / ''man 3p …''\\
Potrzebne pliki nagłówkowe to:
#include
#include
#include
~~Zadanie.#~~ Przekształć poprzedni program tak, by czytał z adresu IP i portu podanego w argumentach programu.
~~Zadanie.#~~ Dodaj do programu obsługę błędów zwracanych przez funkcje ''connect'' i ''read''.
~~Zadanie.#~~ Zmień IP na losowe (tak, by nie odpowiadało na próbę połączenia). Programem ''netstat -tnp'' / ''ss -tnp'' wyświetl utworzone połączenie.
==== Funkcje send/recv/… ====
Poza funkcją ''read(…)'' do odbierania danych można używać funkcji ''recv'',
''recvfrom'' i ''recvmsg'', przy czym ''read(fd, buf, len)'' jest
równoważne ''recv(fd, buf, len, 0)'' i ''recvfrom(sockfd, buf, len, 0, NULL, NULL)''. \\
Podobnie poza funkcją ''write'' do wysyłania można używać też funkcji ''send'',
''sendto'' i ''sendmsg'', analogicznych do powyższych. \\
Dodatkowy argument ''recv''/''send'' (pole flag) pozwala na zmianę zachowania
tych funkcji i będzie omawiany później.\\
''recvfrom'' i ''sendto'' mają dodatkowe pole na adres nadawcy/odbiorcy i są
przeznaczone dla protokołów warstwy transportowej pozwalających na komunikację
po jednym gnieździe z wieloma partnerami. Będą omawiane przy obsłudze UDP.
~~Zadanie.#~~ Zmień program tak, by zamiast ''read(…)'' używał funkcji ''recv(…)''
~~Zadanie.#~~ Przed wywołaniem ''connect'' ustal lokalny adres funkcją ''bind''. Powtórz zadanie 8 i 9.