===== Návod: Nespojovaná/datagramová komunikace v síti s protokolem IP (Internet Protokol) =====
V současné době je protokol [[https://en.wikipedia.org/wiki/Internet_Protocol|IP (Internet Protokol)]]
nejrozšířenějším standardem pro komunikaci mezi počítači a zařízeními. Protokol je protokolem
vyšší vrstvy na úrovni paketů, datagramů a spojení. Pro realizaci vlastního přenosu rámců
po lokálních sítích se používá řada technologií, asi nejrozšířenější je technologie [[https://en.wikipedia.org/wiki/Ethernet|ETHERNET]], která ve své základní podobě rámců ale neřeší přenos rámců mezi více sítěmi.
Obě technologie se tedy velmi dobře doplňují. Protokol IP existuje ve dvou verzích
[[https://en.wikipedia.org/wiki/IPv4|IPv4]], který používá pro identifikaci rozhraní
každého počítače ve světové síti 32-bitovou adresu. Postupně se pak přechází
na novou verzi protokolu [[https://en.wikipedia.org/wiki/IPv6|IPv6]], který používá 128-bitové
adresy s lépe propracovanými pravidly pro přiřazování a komunikaci.
Pro práci na semestrální úloze se předpokládá použití jednodušší verze IPv4. Nad základní
síťovou vrstvou je definované množství dalších protokolů specializovaných pro rozdílné potřeby
komunikace různých aplikací a druhů přenosu. Asi nejznámější je protokol [[https://en.wikipedia.org/wiki/Transmission_Control_Protocol|TCP]], který
slouží pro zajištění zabezpečeného spojení s kompletním mechanizmem potvrzování přijetí
dat. Proud dat je v případě ztráty paketu pozastavený a je zajištěné opětovné poslání
nedoručených dat. protokol se používá například pro nejrozšířenější transportní aplikační
protokoly a služby HTTP, HTTPS a SSH. Protokol TCP však není vhodný pro paralelní doručování
zpráv se všeobecnou cílovou adresou všem účastníkům v rámci lokální sítě ([[https://en.wikipedia.org/wiki/Broadcasting_%28networking%29|Broadcast]]).
Aby bylo možné jednoduše vytvářet seznam všech připojených jednotek, bude se v rámci semstrální
aplikace používat nezabezpečený, nespojovaný, datagramový protokol [[https://en.wikipedia.org/wiki/User_Datagram_Protocol|UDP]].
Tato volba umožňuje vysílat informaci o stavu jednotky do lokální sítě bez definování adresy určení.
Takovéto zprávy pak mohou přijímat všechny jednotky na lokální síti.
Příklad jednoduchého programu v roli serveru a druhého v klienta lze nalézt například
v návodu [[https://beej.us/guide/bgnet/html/#datagram|Beej's Guide to Network Programming]].
Podrobnější how-to pro práci se sockety naleznete na [[https://support.dce.felk.cvut.cz/pos/cv4/|stránkách bývalého předmětu X35POS v cvičení 4]].
Hlavičkové soubory pro programy v jazyce C, které zpřístupňují soketové služby na systémech splňujících normu [[https://en.wikipedia.org/wiki/POSIX|POSIX]]
#include
#include
#include
#include
#include
#include
#include
#include
#include
Program, který přistupuje k těmto službám, musí nejdříve založit síťový socket
int sockfd;
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror("socket");
exit(1);
}
Volba ''AF_INET'' specifikuje adresní rodinu IPv4, ''SOCK_DGRAM'' pak volí nespojovanou službu UDP.
Více o volání socket v [[https://beej.us/guide/bgnet/html/#socket|Beej's Guide]]
nebo v [[https://www.gnu.org/software/libc/manual/html_node/Creating-a-Socket.html#Creating-a-Socket|dokumentaci GNU C knihovny]] použité na systému GNU/Linux.
Volba lokálního portu, na kterém poté aplikace naslouchá a který je použitý spolu s IP adresou odchozího
rozhraní počítače/jednotky se volí voláním [[https://beej.us/guide/bgnet/html/#bind|bind]] ([[https://www.gnu.org/software/libc/manual/html_node/Setting-Address.html#Setting-Address|GNU]]).
Příklad jednoduchého zavázání IPv4 socketu na všechna lokální rozhraní. Port MY_PORT je 16-ti bitové číslo v rozsahu 0 až 65535. Síťové adresy jsou ukládané v paměťových strukturách již v síťovém pořadí bajtů, proto je potřeba převést interní (h = host) pořadí na síťové (n = network) pořadí bajtů. Porty v rozsahu 1 až 1000 jsou pak rezervované pro privilegované služby, mohou je používat jen programy s právy administrátora nebo přímého přístupu k síti.
struct sockaddr_in bindaddr;
memset(&bindaddr, 0, sizeof(bindaddr));
bindaddr.sin_family = AF_INET;
bindaddr.sin_port = htons(MY_PORT);
bindaddr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) == -1) {
perror("bind");
exit(1);
}
Systém však po ukončení služby na určitém portu ještě po určitou dobu znovuvyužití portu rezervuje/blokuje, aby zpožděné, nezpracované příchozí pakety nekolidovaly s nově spuštěnou službou/programem. Proto je vhodné před voláním bind() odmítnout tuto blokaci pro službu, která musí běžet na pevném čísle portu.
int yes=1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(yes)) == -1) {
perror("setsockopt (SO_REUSEADDR)");
exit(1);
}
Protože by mohlo dojít k rozesílání datagramům všem jednotkám na lokální síti omylem, musí být tato volba povolena
int broadcast = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast,
sizeof broadcast) == -1) {
perror("setsockopt (SO_BROADCAST)");
exit(1);
}
Dále je již možné k zasílání zpráv využít volání ''[[https://www.gnu.org/software/libc/manual/html_node/Sending-Datagrams.html|sendto]]'' a ''[[https://www.gnu.org/software/libc/manual/html_node/Receiving-Datagrams.html|recvfrom]]''.
IPv4 adresu pro hromadné rozeslání zprávy na všechny uzly v lokální síti je možné nastavit
struct sockaddr_in braddr;
memset(&braddr, 0, sizeof(braddr));
braddr.sin_family = AF_INET;
braddr.sin_port = htons(HELLO_SERVICE_PORT);
braddr.sin_addr.s_addr = INADDR_BROADCAST;