Dydaktyka:
FeedbackTo jest stara wersja strony!
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++17 -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 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++17 z.cpp -o p | włącza używanie standardu ISO C++ z 2017 roku |
c++ -pthread z.cpp -o p | włącza obsługę wątków standardu POSIX |
Przykładowy plik cmake:
cmake_minimum_required(VERSION 3.1) project(projname) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Debug") endif() set(CMAKE_CXX_STANDARD 17) find_package(Threads) add_executable(example example.cpp) target_link_libraries(example ${CMAKE_THREAD_LIBS_INIT})
Interface obsługi gniazd BSD jest bardzo zbliżony do API specyfikowanego przez POSIX do obsługi plików (który znacie z PW).
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
).
#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?
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.
Więcej informacji o "useful debugging and measurement tools": echo, discard, chargen, qotd, daytime
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 PW)
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
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 order | MSB first | LSB first | ||
little endian | 0000111100000001 | 1111000010000000 | x86 | ARM, IA-64 (Itanium), SPARC≥v9 |
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
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: http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/bsdsockets.html#funkcje-pomocnicze
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: man 7 ip
lub man netinet_in.h
lub
http://www.cs.put.poznan.pl/ddwornikowski/sieci/sieci2/bsdsockets.html#glowne-funkcje-interfejsu-gniazd
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).
Zadanie 6. Zmień program do odczytu pliku tak, by zamiast funkcji open(…)
:
socket(…)
gniazdo:PF_INET
(zamiennie AF_INET
) SOCK_STREAM
(czyli TCP) IPPROTO_TCP
(można też podać 0
, oznaczające domyślny protokół)sockaddr_in
(zdefiniowaną w pliku nagłówkowym netinet/in.h
):AF_INET
htons
)inet_addr
lub inet_aton
); htonl(INADDR_LOOPBACK)
. .sin_addr
to struktura typu in_addr
z jedną składową .s_addr
typu uint32_t
inet_addr
przyjmuje wskaźnik na strukturę, natomiast inet_aton
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);
connect(…)
connect
oczekuje sockaddr*
zamiast sockaddr_in*
– wymagane rzutownaiesizeof()
zmiennej lub typuclose()
zamykał połączenie funkcją shutdown()
; 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.
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.