===== 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;