Narzędzia użytkownika

Narzędzia witryny


Pasek boczny


O mnie


Dydaktyka:

Feedback


sk2:sockets_intro

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:

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 progkompiluje plik zrodlo.cpp do programu prog
c++ -Wall z.cpp -o pwłącza wszystkie1) ostrzeżenia kompilatora (-W = warn, all = wszystkie)
c++ -O0 -g z.cpp -o pwyłącza optymalizacje i dodaje do programu dane umożliwiające debugowanie
c++ --std=c++20 z.cpp -o pwłącza używanie standardu ISO C++ z 2020 roku
c++ -pthread z.cpp -o pwłącza obsługę wątków standardu POSIX
wymagane do wersji glibc ≤ 2.33 [1] [2]

Przykładowy plik 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)

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).
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.

Zadanie 1 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).

z1.cpp
#include <fcntl.h>
#include <unistd.h>
using namespace std;
int main(int argc, char **argv) {
 
	return 0;
}

Zadanie 2 Co trzeba dodać do obsłużenia błędów?

Adresacja połączeń na warstwie transportu

Zadanie 3 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 (link do materiałów z SK1)

Zadanie 4 Prześlij między sobą dowolny tekst używając programu netcat lub socat

Zadanie 5 Nawiąż połączenie na port 13 (daytime) twojego komputera.
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.

Więcej informacji o "useful debugging and measurement tools": echo, discard, chargen, qotd, daytime

Sockets

BSD sockets

Gniazdo (socket) – interfejs między systemem operacyjnym a programem użytkownika używany do dwukierunkowej komunikacji poza program.
Porównaj z: FIFO (named pipe, nazwany potok; było na SOP i PSiW)

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
Ekstra: man 7 pipe – przypomnienie potoków; man 7 unix – unix sockets – wykorzystanie gniazd do komunikacji międzyprocesowej

[ekstra] Kolejność bajtów (i bitów)

W jakiej kolejności są wysyłane bajty danych i dlaczego to jest ważne.

Przykład wysłania liczby 0x010F, czyli 271, czyli 0b0000000100001111

byte order\bit orderMSB firstLSB first
little endian00001111000000011111000010000000 x86/x86_64
ARM, IA-64 (Itanium), SPARC≥v9, RISC-V
big endian00000001000011111000000011110000Motorola 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

Sieciowa kolejność bajtów (networking byte order) to BE – odwrotnie niż na x86 https://en.wikipedia.org/wiki/Endianness#Networking

uint16_t hostEndianessPort = 13;
uint16_t networkEndianessPort = htons(hostEndianessPort);

Opis funkcji pomocniczych – patrz man byteorder lub 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 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 6 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 – strona Darka lub man 3 … / man 3p …
Potrzebne pliki nagłówkowe to:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

Zadanie 7 Przekształć poprzedni program tak, by czytał z adresu IP i portu podanego w argumentach programu.

Zadanie 8 Dodaj do programu obsługę błędów zwracanych przez funkcje connect i read.

Zadanie 9 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 10 Zmień program tak, by zamiast read(…) używał funkcji recv(…)

Zadanie 11 Przed wywołaniem connect ustal lokalny adres funkcją bind. Powtórz zadanie 8 i 9.

1) "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 gcc/clang
sk2/sockets_intro.txt · ostatnio zmienione: 2024/10/07 20:27 przez jkonczak