Bespojna usluga TCP/IP porodice protokola

1. Uvod

Cilj ove laboratorijske vježbe je upoznavanje sa API-jem za pristup uslugama računalne mreže na Unix operacijskom sustavu, a također upoznavanje sa korištenjem i karakteristikama bespojne usluge TCP/IP porodice protokola. Budući da su Windowsi dobar dio mrežne tehnologije preuzeli od BSD varijante Unix-a to za tu grupu operacijskih sustava vrijedi većina idućeg teksta.

2. TCP/IP

TCP/IP porodica protokola je dominantan skup protokola koji se danas koristi za povezivanje računala i srodnih uređaja i predstavlja osnovu Interneta.

Iako se u nazivu spominju samo TCP i IP ta porodica uključuje i mnoštvo drugih protokola od kojih je dio prikazan na slici 1. TCP/IP nikada nije nastao niti je modeliran na osnovu nekog formalnog modela - kao što je to slučaj sa OSI stogom, ali se ipak uslojavanje, tj. hijerarhija protokola, može primjetiti iščitavanjem slike odozdo prema gore (ili obrnuto). Na dnu imamo podatkovni sloj koji se nalazi neposredno iznad fizičkog sloja (koji nije prikazan) te sa njim direktno komunicira. Iznad njega je mrežni sloj sa IPv4, IPv6 i nekim pomoćnim protokolima. Nakon toga dolaze transportni slojevi TCP i UDP te na kraju imamo aplikacije koje su prikazane na samom vrhu slike. Svi protokoli iz TCP/IP porodice protokola, ali i mnoštvo drugih stvari, definirani su serijom RFC dokumenata koji se besplatno mogu skinuti sa Interneta. Mjesta gdje su ti dokumenti pohranjeni navedeno je u literaturi. Dio ispod isprekidane crte označene tekstom API nalazi se implementiran u jezgri operacijskog sustava te u pomoćnim bibliotekama. Dio iznad isprekidane crte su aplikacijski programi koji su isporučeni sa operacijskim sustavom ili ih je napravio korisnik.


Slika 1. Dio porodice TCP/IP protokola

Kako bi programer mogao koristiti usluge koje mu pruža mreža tj. operacijski sustav, potrebno je koristiti API koji nudi OS preko biblioteka isporučenih sa mrežnom komponentom OS-a. Postoji mnoštvo različitih API-ja kao što su to npr. XTI/TLI, STREAMS, itd. No dominantan API je tzv. socket API koji se je prvi puta pojavio 1983. godine prilikom izdavanja 4.2BSD Unix operacijskog sustava. U sljedećem poglavlju taj API je detaljnije opisan sa naglaskom na dio potreban za obavljanje ove laboratorijske vježbe. Ovdje ćemo još samo napomenuti da su mreže toliko integrirane u današnje računastvo da se dobar dio implementacije mrežnih protokola nalazi u samoj jezgri operacijskog sustava. Te implementacije su dosta komplicirane no ipak je zanimljivo pogledati kako je to izvedeno u stvarnosti, tj. u operacijskom susavu koji se koristi i u komercijalnim a ne samo hobističkim ili znanstvenim projektima. To je moguće zahvaljujući dostupnosti, tj. otvorenosti koda *BSD i Linux operacijskih sustava. Iako ti svi operacijski sustavi pripadaju Unix porodici, implementacije su potpuno različite zbog povijesnih razloga i različitih licenci.

3. Socket API

Kao što je to već rečeno u drugom poglavlju, dominantan API za korištenje usluga mrežnog sloja je tzv. socket API. Taj API dizajniran je za programski jezik C iako postoje odgovarajuće ekstenzije i prilagodbe drugim programskim jezicima. Sastoji se od zaglavlja (.h) te biblioteka u kojima se nalazi implementacija dijela funkcionalnosti tog API-ja. Ostatak je implementiran unutar operacijskog sustava. Cijeli sustav je dosta kompleksan i nudi mnoštvo mogućnosti ali u ovoj vježbi ćemo se ograničiti na bespojnu uslugu i funkcionalnosti neophodne za implementaciju vrlo jednostavno poslužitelja i klijenta. Bespojnu uslugu u domeni TCP/IP protokola nudi UDP prijenosni protokol koji ima sljedeće karakteristike:

Kao i svaki drugi API tako se i ovaj sastoji od podatkovnih struktura i funkcija koje koriste te podatkovne strukture te obavljaju operacije nad njima. Redoslijed poziva nekih funkcija je za pojedine namjene unaprijed definiran. Na slici 2 prikazan je redoslijed pozivanja funkcija u slučaju kada se želi ostvariti bespojna veza.


Slika 2. Redoslijed poziva funkcija socket API-ja za korištenje bespojne usluge

Lijeva strana slike odnosi se na poziv funkcija unutar klijenta, dok se desna strana odnosi na poslužitelj. U slijedećem tekstu nešto detaljnije su opisane pojedine funkcije, a zatim i podatkovne strukture koje se koriste. Prije korištenja funkcija socket API-ja potrebno je uključiti zaglavlja sys/types.h, sys/socket.h i netinet/in.h.

socket

Prije no što je moguća bilo kakva komunikacija potrebno je kreirati pristupnu točku ili socket. Pozivom ove funkcije samo se najavljuje kernelu korištenje mrežnih usluga određenog tipa na što kernel odnosno odgovarajuće funkcije u bibliotekama kreiraju potrebne strukture za rad sa mrežom. Funkcija kao rezultat vraća deskriptor koji se koristi u drugim funkcijama slično kao što se deskriptor otvorene datoteke koristi u funkcijama za čitanje i pisanje u datoteku.

Prototip ove funkcije ima sljedeći oblik:

	int socket(int domain, int type, int protocol);
dok argumenti imaju sljedeća značenja:

Povratna vrijednost funkcije je -1 u slučaju greške, a u suprotnom pozitivni broj.

sendto

Nakon što je kreirana, pristupna točka se u slučaju klijenta može početi koristiti. Konkretno moguće je slati poruke na poslužitelj upotrebom funkcije sendto.

Prototip ove funkcije ima sljedeći oblik:

	int sendto(int sockfd, const void *msg, size_t len, int flags,
		const struct sockaddr *to, socklen_t tolen);
dok argumenti imaju sljedeća značenja:

Povratna vrijednost funkcije je -1 u slučaju greške, a u suprotnom broj poslanih okteta.

bind

Za razliku od klijenta koji koji može koristiti pristupnu točku odmah nakon njenog kreiranja, poslužitelj mora prvo vezati adresu uz nju. To se obavlja uz pomoć ove funkcije. Nakon njenog poziva pristupna točka ima adresu te klijent, u slučaju da poznaje tu adresu, može pristupiti poslužitelju.

Prototip ove funkcije ima sljedeći oblik:

	int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
dok argumenti imaju sljedeća značenja:

Povratna vrijednost funkcije je -1 u slučaju greške, a u suprotnom 0.

recvfrom

Primanje paketa obavlja se funkcijom recvfrom. Ta funkcija ima sljedeći prototip:

	int  recvfrom(int sockfd,  void  *buf,  size_t  len, int flags,
		struct sockaddr *from, socklen_t *fromlen);

Argumenti te funkcije analogni su argumentima funkcije sendto sa odgovarajućim razlikama. Tako je prvi argument deskriptor pristupne točke, drugi argument je pokazivač na spremnik u koji se pohranjuje pristigli paket, treći argument određuje maksimalnu veličinu spremnika, četvrti argument je u našem slučaju 0. Peti argument je pokazivač na strukturu u koju se upisuje adresa pristupne točke sa koje je pristigao paket, a u šesti argument se upisuje duljina petog argumenta. U slučaju da nas ne zanima adresa pošiljatelja paketa tada se kao peti i šesti argument navodi konstanta NULL.

Povratna vrijednost funkcije je broj primljenih okteta, odnosno -1 u slučaju greške.

close

Funkcija close je uobičajna funkcija za zatvaranje deskriptora.

sockaddr_in

Ovo je struktura u koju se upisuje odgovarajuća internet adresa. Adresa se sastoji od IP adrese računala i pristupa. U skladu s tim ova struktura ima sljedeća polja:

4. Zadatak

Potrebno je napraviti jednostavni echo klijent i poslužitelj koristeći UDP protokol. Drugim riječima, server čeka na određenom pristupu i sve što mu pristigne na taj pristup vraća nazad klijentu. S druge strane, klijent čita sa tipkovnice liniju po liniju, te svaku liniju šalje poslužitelju, a potom očekuje odgovor poslužitelja. Klijent mora ispisati odgovor. U trenutku kada se upiše "kraj" kao jedina riječ u liniji klijent šalje poslužitelju tu riječ, a poslužitelj nakon odgovora prekida rad. U trenutku primitka odgovora od poslužitelja i klijent prekida rad. Argument klijentu je IP adresa na kojoj se nalazi poslužitelj te pristup na kojem poslužitelj čeka. Argument poslužitelju je pristup na kojemu će čekati pakete od klijenta.