przejście do zawartości
Jan Kończak
Narzędzia użytkownika
Zaloguj
Narzędzia witryny
Narzędzia
Pokaż stronę
Poprzednie wersje
Odnośniki
Ostatnie zmiany
Menadżer multimediów
Indeks
Zaloguj
Ostatnie zmiany
Menadżer multimediów
Indeks
Jesteś tutaj:
start
»
sk2
»
sockets_intro
sk2:sockets_intro
Ta strona jest tylko do odczytu. Możesz wyświetlić źródła tej strony ale nie możesz ich zmienić.
====== Wstęp – przypomnienia ====== \\ ==== C++ ==== Większość przykładów w materiałach i programów pisanych na laboratoriach jest w C+////+.\\ Stąd przypominam jak obsługiwać kompilator GCC / clang w Linuksie. Przykładowe polecenie do kompilacji przykładów ze strony: <html><code>g++ --std=c++20 -Wall -O0 -g <html><span style="color:gray;">-pthread</span> -o example example.cpp</code></html> |''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| |<html><code><span style="color:gray;">c++ -pthread z.cpp -o p</span></code></html>|<html><span style="color:gray;"></html>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]]]<html></span></html>| Przykładowy plik cmake: <file cmake CMakeLists.txt> cmake_minimum_required(VERSION 3.12) project(projname) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug") endif() set(CMAKE_CXX_STANDARD 20) find_package(Threads) link_libraries(Threads::Threads) add_executable(example1 example1.cpp) add_executable(example2 example2.cpp) # target_link_libraries(example2 Threads::Threads) </file> ==== Obsługa plików w POSIX ==== Interface obsługi gniazd BSD jest bardzo zbliżony do API specyfikowanego przez POSIX do obsługi plików (który znacie z PSiW). \\ <html><small> BSD socket API został przyjęty jako standard przez większość systemów operacyjnych, m. inn. został bez większych zmian wpisany w standard POSIX.</small></html> ~~Zadanie.#~~ Napisz program który otworzy plik o nazwie 'date' i wypisze jego zawartość. Poniżej "pusty" plik źródłowy. Pomiń obsługę błędów. (Jeśli nie wiesz skąd wziąć plik ''date'', wpisz w konsolę: ''date>date'' .) \\ Do obsługi pliku i standardowego wejścia / wyjścia użyj funkcji ''open'', ''read'', ''write'' i ''close''. \\ Opis funkcji znajdziesz na stronach podręcznika systemowego (np. ''man open''). <file cpp z1.cpp> #include <fcntl.h> #include <unistd.h> using namespace std; int main(int argc, char **argv) { return 0; } </file> ~~Zadanie.#~~ Co trzeba dodać do obsłużenia błędów? ==== Adresacja połączeń na warstwie transportu ==== ~~Zadanie.#~~ Jakie adresy (numery) są potrzebne by móc przesłać wiadomość do konkretnego programu na wybranym komputerze? Przypomnienie programów ''netcat'' / ''socat'' oraz programów ''netstat'' / ''ss'' ([[sk1:transport#monitorowanie_biezacych_polaczen|link do materiałów z SK1]]) ~~Zadanie.#~~ Prześlij między sobą dowolny tekst używając programu ''netcat'' lub ''socat'' ~~Zadanie.#~~ Nawiąż połączenie na port 13 (daytime) twojego komputera. \\ <html><small></html>Jeżeli używasz własnego komputera i port 13 nie odpowiada, uruchom i pozostaw działającą w osobnym terminalu komendę \\ ''socat tcp-listen:1313,fork,reuseaddr exec:date'' i do tego oraz dalszych ćwiczeń używaj port 1313.<html></small></html> <html><small></html>Więcej informacji o "useful debugging and measurement tools": [[http://www.ietf.org/rfc/rfc862.txt|echo]], [[http://www.ietf.org/rfc/rfc863.txt|discard]], [[http://www.ietf.org/rfc/rfc864.txt|chargen]], [[http://www.ietf.org/rfc/rfc865.txt|qotd]], [[http://www.ietf.org/rfc/rfc867.txt|daytime]]<html></small></html> ====== Sockets ====== ==== BSD sockets ==== Gniazdo (socket) – interfejs między systemem operacyjnym a programem użytkownika używany do dwukierunkowej komunikacji poza program.\\ <html><small></html>Porównaj z: FIFO (named pipe, nazwany potok; było na SOP i PSiW)<html></small></html> API stworzone dla systemu BSD zostało przyjęte przez praktycznie wszystkie systemy operacyjne (POSIX socket API, WinSock). Pełen opis: [[http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/bsdsockets.html]] oraz [[https://en.wikipedia.org/wiki/Berkeley_sockets]] ''man 7 socket tcp udp''\\ <html><small></html>Ekstra: ''man 7 pipe'' – przypomnienie potoków; ''man 7 unix'' – unix sockets – wykorzystanie gniazd do komunikacji międzyprocesowej<html></small></html> ==== [ekstra] Kolejność bajtów (i bitów) ==== W jakiej kolejności są wysyłane bajty danych i dlaczego to jest ważne. <html><small style="color:gray;"></html> Przykład wysłania liczby 0x010F, czyli 271, czyli 0b0000000100001111 |byte order\bit order|MSB first|LSB first| | | |little endian|0000111100000001|1111000010000000| x86/x86_64 |<html><div style="margin-top:1em">ARM, IA-64 (Itanium), SPARC≥v9, RISC-V</div></html>| |big endian|0000000100001111|1000000011110000|Motorola 68k, SPARC<v9, z/Arch| :::| | | I²C, SDH | Ethernet, RS232, USB | | MSB/LSB first – to głównie problem producentów sprzętu. Little/big endian - to problem programistów.\\ [[https://en.wikipedia.org/wiki/Most_significant_bit]] [[https://pl.wikipedia.org/wiki/Kolejność_bajtów]] <html></small></html> Sieciowa kolejność bajtów (networking byte order) to BE – odwrotnie niż na x86 [[https://en.wikipedia.org/wiki/Endianness#Networking]] <code cpp> uint16_t hostEndianessPort = 13; uint16_t networkEndianessPort = htons(hostEndianessPort); </code> 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** (<html><small>lub ścieżkę dla gniazd unix</small></html>) 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]] <html><small></html> 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). <html></small></html> ==== "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)''. \\ <html><small></html> 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'': \\ <code cpp>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); </code> <html></small></html> * łą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()''; \\ <html><small></html>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''<html></small></html> 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: <code cpp> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> </code> ~~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'', <small> ''recvfrom'' i ''recvmsg''</small>, 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'', <small>''sendto'' i ''sendmsg''</small>, analogicznych do powyższych. \\ Dodatkowy argument ''recv''/''send'' (pole flag) pozwala na zmianę zachowania tych funkcji i będzie omawiany później.\\ <small> ''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. </small> ~~Zadanie.#~~ Zmień program tak, by zamiast ''read(…)'' używał funkcji ''recv(…)'' <small> ~~Zadanie.#~~ Przed wywołaniem ''connect'' ustal lokalny adres funkcją ''bind''. Powtórz zadanie 8 i 9. </small>
sk2/sockets_intro.txt
· ostatnio zmienione: 2024/10/07 20:27 przez
jkonczak
Narzędzia strony
Pokaż stronę
Poprzednie wersje
Odnośniki
Złóż / rozłóż wszystko
Do góry