Uvod u soket-e



Yüklə 92,17 Kb.
tarix17.10.2017
ölçüsü92,17 Kb.
#5091


  1. Mrezno programiranje


pod UNIX-om

Uvod

Ovaj tekst predvidjen je pre svega kao prirucnik za pripremu i polaganje pismenog dela ispita iz Programskih sistema. On sadrzi osnovne informacije o upotrebi raznih funkcija i sistemskih poziva za mrezno programiranje pod UNIX-om. Obradjene su sledece oblasti: Uvod u sockete, Elementarni TCP socketi, I/O multipleksiranje, Opcije socketa, Elementarni UDP socketi kao i Elementarne funkcije za konverziju adresa. Pri tom je kompletan tekst radjen po knizi UNIX Network Programming Volume1 (u daljem tekstu UNPv1) ciji je autor Richard Stevens. Dakle, svi prototipovi funkcija, njihove povratne vrednosti i nacin rada prepisani su iz ove knjige, pa se autor ovog materijala ogradjuje unapred u slucaju netacnih informacija. Takodje, ovaj materijal nije namenjen za ucenje, pa je strogo preporucljivo procitati UNPv1 pre citanja ovog materijala. Osnovna zamisao ovog teksta je da se prilikom programiranja lakse pronadje neka funkcija i vidi kako se ona koristi (lakse je prelistati petnaestak stranica nego prevrtati kopiju UNPv1-a od 1000 strana). Mnoge bitne i sustinske stvari koje se ticu mreznog programiranja su u ovom tekstu izostavljene. Pojedine funkcije koje su se autoru ucinile slozenije, i nepotrebne iz ugla svakodnevnog programiranja su preskocene. Neke stvari koje sam autor nije razumeo, preskocio je (autor ovog teksta u trenutku pisanja nije jos polozio sisteme  ). Dakle, ovaj materijal sadrzi samo one podatke koji su u trenutku programiranja (npr. na ispitu) potrebne, a koje je smesno pamtiti. Medjutim, razumevanje funkcionisanja mreze pod UNIX sistemom neophodno je za programiranje, i to se mora procitati iz UNPv1-a.

Osim pomenutog UNPv1-a, obavezna literatura za ovaj ispit je knjiga C Programming Language ciji su autori Dennis Ritchie i Brian Kernighan. U njima se moze naci detaljan pregled standardne C biblioteke, kompletnog C jezika, kao i neke varijante implementacije pojedinih standardnih C funkcija pod UNIX sistemom. Sve ovo je preporucljivo procitati.

Terminologija karakteristicna za mreze je preuzeta iz UNPv1. Autor nije zeleo da ulazi u detalje, tako da ako neki termin nije jasan ili je citavo objasnjenje vezano za funkciju nejasno, objasnjenje morate naci u UNPv1. Nije lose procitati i neke dodatne tekstove (ili cak citave knjige  ) o mrezama i njihovom funkcionisanju, preporucujem Internet literaturu koje ima zaista mnogo.

Vazno je napomenuti da pre svega gore navedenog treba procitati folije asistenta mr Aca Samardzica za UNIX, kao i za GNU programerske alate. U ovom trenutku je na njegovom sajtu okacena nova verzija UNIX folija, mada autor preferira staru, pa bolje nadjite nju. Takodje, pametno bi bilo procitati neku dobru knjizicu o administraciji UNIX sistema (ili za Linux), kako bi se student upoznao sa osnovnim UNIX konceptima. Naravno, potrebno je procitati literaturu vezanu za prvi deo materije koja dolazi za ispit, a to je programiranje u UNIX okruzenju. Zvanicna literatura za taj deo gradiva je Advanced Programming in UNIX Enviroment (APUE), a autor je Richard Stevens.

Autor ovog teksta je Bankovic Milan student Matematickog fakulteta u Beogradu. Sve primedbe, ispravke, kritike, sugestije i ostalo slati na mr01015@alas.matf.bg.ac.yu .


Puno pozdrava od Milana. Srecan rad! 


Uvod u soket-e



#include
struct in_addr {

in_addr_t s_addr; /* 32-bitna IPv4 adresa (mrezni raspored bajtova) */

};
struct sockaddr_in {

uint8_t sin_len; /* duzina strukture (16) */

sa_family_t sin_family; /* AF_INET */

in_port_t sin_port; /* 16-bitni TCP ili UDP port (mrezni raspored bitova) */

struct in_addr sin_addr; /* 32-bitna IPv4 adresa (mrezni raspored bajtova) */

char sin_zero[8]; /* ne koristi se */

};
Opis:

Struktura sockaddr_in se koristi za smestanje informacija o adresi za IPv4 protokol. Clan sin_family se koristi od strane funkcija koje rade sa vise razlicitih mreznih protokola, kako bi se odredio tip konekcije. U slucaju IPv4 konekcije, ovaj clan je uvek AF_INET. Mrezni raspored bajtova oznacava da se visebajtni podaci zapisuju u onom rasporedu bajtova koji je standardizovan za mrezu. Pored ovog postoji i host raspored bajtova, koji moze biti razlicit od mreznog. Vise o tome kasnije. IP adresa se ne smesta direktno kao clan strukture (kao cetvorobajtni integer) vec postoji posebna in_addr_t struktura ciji je jedini clan IP adresa. Sada se IP adresi pristupa sa .sin_addr.s_addr. Razlog za ovakav nacin je istorijski. Clan sin_zero se uvek postavlja na nulu na pocetku i ne koristi se. Sama struktura se koristi iskljucivo na host-u i nije predmet komunikacije izmedju protokola.


#include
struct in6_addr {

uint8_t s6_addr[16]; /* 128-bitna IPv6 adresa (mrezni raspored bajtova) */

};
struct sockaddr_in6 {

uint8_t sin6_len; /* duzina strukture (24) */

sa_family_t sin6_family; /* AF_INET6 */

in_port_t sin6_port; /* 16-bitni port (mrezni raspored bajtova) */

uint32_t sin6_flowinfo;/* prioritet i tok (mrezni raspored bajtova) */

struct in6_addr sin6_addr; /* 128-bitna IPv6 adresa (mrezni raspored bajtova) */

};
Opis:

Potpuno analogno prethodnom, s tim sto je ovde u pitanju IPv6 verzija protokola. Njene adrese su duze (128 bita) kako bi se povecao broj racunara u mrezi, zbog naglog razvoja Interneta. U ovom slucaju familija protokola se oznacava konstantom AF_INET6. Clan sin6_flowinfo je ubacen zbog novijih mogucnosti protokola koje su jos u eksperimentalnoj fazi. Nizih 24 bita se koriste za labelu toka, naredna cetiri za prioritet, dok su najstarija cetiri bita rezervisana.

Primetimo da je struktura sockaddr_in6 velicine 24 bajta, dok je struktura sockaddr_in velicine 16 bajtova.Pri tom se prvih 32 bita u oba slucaja koriste u iste svrhe (za velicinu, familiju i 16-bitni port). Na ovaj nacin funkcije koje barataju sa razlicitim tipovima struktura mogu da prihvate pokazivac na bilo koju strukturu, a da citanjem odgovarajucih bitova utvrde o kojoj se strukturi radi. U tu svrhu se korisiti sledeca tzv. genericka struktura:
#include
struct sockaddr {

uint8_t sa_len;

sa_family_t sa_family;

char sa_data[14];

};

Kao sto se moze videti, opet imamo isti raspored polja, dok je poslednje polje velicine 14 bajtova, sto ukupno daje 16 bajtova, sto je i inace najmanja velicina ovakvih struktura. Funkcijama se sada predaje pokazivac na ovu strukturu (upotrebom kastovanja), dok funkcija interno utvrdjuje o kojoj se strukturi zaista radi. Upotreba void pokazivaca bi bila jednostavnije resenje, ali ove stvari su nastale pre nego sto je ANSI standard uveo void pokazivac, kao tip generickog pokazivaca.


#include
uint16_t htons( uint16_t host16bitvalue);

uint32_t htonl( uint32_t host32bitvalue);

uint16_t ntohs( uint16_t net16bitvalue);

uint32_t ntohl( uint32_t net32bitvalue);
Opis:

Ovim funkcijama se vrsi konverzija zapisa visebajtnih podataka. Podaci mogu biti rasporedjeni po tzv. big-endian i little-endian principu. Kod prvog na najmanju adresu upisujemo bajt najvece tezine, dok je kod drugog obrnuto. Kako nacin zapisa nije standardizovan, postoji potreba za konverzijom. Pri tom raspored bajtova koji se koristi na racunaru na kome funkcionise aplikacija nazivamo host raspored bajtova, dok raspored bajtova koji vazi u mreznom prenosu nazivamo mrezni raspored bajtova. Ovi rasporedi mogu biti isti, ali to sve zavisi od racunara na kome radimo. Po pravilu IP protokoli koriste big-endian zapis. U gornjim funkcijama imamo po dve verzije za oba smera konverzije, short (sa sufiksom s) za 16-bitne podatke, i long (sa sufiksom l) za 32-bitne podatke. Pri tom hton oznacava host-to-network, dok ntoh oznacava network-to-host.


#include
void bzero( void *dest, size_t nbytes);

void bcopy( const void *src , void *dest, size_t nbytes);

int bcmp( const void *ptr1, const void *ptr2, size_t nbytes);
Opis:

Funkcije sluze za manipulaciju memorijom. Funkcija bzero() inicijalizuje na nulu memorijski prostor pocev od lokacije na koju pokazuje prvi parametar, i velicine koja je u bajtovima zadata drugim parametrom. Funkcija bcopy() kopira sadrzaj memorije iz src u dest u velicini nybtes bajtova. Funkcija bcmp() vrsi poredjenje sadrzaja i vraca 0 ako su jednaki, dok vraca !=0 ako su sadrzaji razliciti. Poredi se prvih nbytes bajtova.

Slicnu ulogu imaju i funkcije definisane u standardnoj biblioteci u zaglavlju string.h:

#include


void *memset( void *dest,int c, size_t len);

void *memcpy(void *dest, const void *src, size_t nbytes);

int memcmp(const void *ptr1,const void *ptr2, size_t nbytes);
Jedina razlika je u tome sto funkcija memset() dozvoljava “punjenje” memorije i nekim drugim sadrzajem (ne samo nulama), u ovom slucaju bajt c se upisuje u sve lokacije. Kod funkcije memcpy() su redosledi prva dva parametra obrnuti u odnosu na analognu funkciju bcopy() od malopre.
#include
int inet_aton( const char *strptr, struct in_addr *addrptr);

in_addr_t inet_addr( const char *strptr);

char * inet_ntoa( struct in_addr inaddr);
Opis:

Funkcije se koriste za konverziju izmedju binarnog zapisa (mrezni raspored bajtova) IP adresa i uobicajenog dekadnog zapisa a.b.c.d (svaki bajt IPv4 adrese se zapisuje dekadno, medjusobno su razdvojeni tackama npr. 212.62.32.1). Ove funkcije rade sa IPv4 protokolom. Funkcija inet_aton() konvertuje dekadni zapis u binarni i smesta ga u strukturu in_addr na koju pokazuje drugi parametar. Funkcija vraca 1 ako je string ispravan, 0 u suprotnom. Funkcija inet_addr() radi slicnu stvar, s tim sto se rezultat vraca u obliku celobrojnog in_addr_t tipa. Ova funkcija se ne preporucuje, jer je indikator greske INADDR_NONE, koji je najcesce po vrednosti odgovarajuca stringu 255.255.255.255, pa se ova adresa ne moze ocitavati ovom funkcijom. Funkcija inet_ntoa() radi obrnuto: prihvata in_addr strukturu, iz nje ocitava IP adresu i konvertuje je u dekadni zapis. Vraca pokazivac na string u koji je smesten dekadni zapis.


#include
int inet_pton( int family, const char *strptr, void *addrptr);

const char * inet_ntop( int family , const void *addrptr, char *strptr, size_t len);
Opis:

Ove funkcije rade isto sto i prethodne, s tim sto mogu raditi i sa IPv4 i IPv6 protokolom. To se odredjuje prvim parametrom (AF_INET, za v4, AF_INET6 za v6). Ako sistem ne prepozna ili ne podrzava odgovarajucu familiju, tada se errno postavlja na EAFNOSUPPORT. Funkcija inet_pton() vraca 1 ako je sve u redu, 0 ako string nije u odgovarajucem formatu, -1 u slucaju greske (za informacije o formatima IPv6 adresa i njihovom uobicajenom heksadekadnom zapisu videti fusnotu na strani 14) . Funkcija inet_ntop() vraca pokazivac na bafer u koji je smesten dekadni format, ili NULL u slucaju greske. Parametar len se koristi za specificiranje maksimalne duzine bafera. U slucaju prekoracenja funkcija vraca gresku, a errno se postavlja na ENOSPC.


#include

int isfdtype( int fd, int fdtype);
Opis:

Funkcija vraca 1 ako je fajl deskriptor fd tipa fdtype. Pri tom je vrednost drugog parametra S_IFSOCK, S_IFREG, S_IFDIR, S_IFFIFO, S_IFCHAR, S_IFBLOCK. Posix jedino garantuje da ce funkcija raditi sa S_IFSOCK vrednoscu, tj. za soket fajl deskriptore.


Elementarni TCP soketi



#include
int socket(int family, int type, int protocol);
Opis:

Ova funkcija kreira soket. Prvi parametar definise familiju protokola, najcesce koriscene vrednosti su AF_INET, i AF_INET6 (za IPv4 i IPv6 familije protokola respektivno). Ostale vrednosti su AF_LOCAL, AF_ROUTE, i AF_KEY. Drugi parametar predstavlja tip soketa: SOCK_STREAM je za TCP soket, SOCK_DGRAM se koristi za UDP protokol, SOCK_RAW se koristi za direktnu komunikaciju sa IP protokolom.Treci parametar je po pravilu 0, osim za raw-sokete. Funkcija vraca pozitivan ceo broj koji se koristi za rukovanje soketom (soket deskriptor). U slucaju greske vraca -1;


#include
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
Opis:

Funkcija connect povezuje klijent sa serverom. Konekcija se vrsi preko soketa koji je dat prvim parametrom, drugi parametar je adresa servera (uz obavezno kastovanje u genericku strukturu sockaddr), a treci parametar je velicina predate strukture (dobija se sizeof() operatorom). Funkcija blokira sve dok ne stigne ack,SYN sekvenca od servera. Tada vraca 0, a u slucaju greske vraca -1;


#include
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
Opis:

Funkcija dodeljuje soketu port i home adresu koji se zadaju adresnom strukturom (parametar myaddr). Treci parametar je velicina adresne strukture. Postoje sledece mogucnosti:



  1. Aplikacija sama bira i port i home IP adresu koju ce koristiti. U tom slucaju se oba podatka specificiraju u adresnoj strukturi.

  2. Aplikacija bira samo port. Tada se IP adresa u strukturi postavlja na INADDR_ANY. U ovom slucaju se ostavlja kernelu da kasnije (kada se pozove connect sa klijenske strane, odnosno, kada se prihvati konekcija funkcijom accept sa serverske strane) odabere IP adresu. Pri tom je na serverskoj strani izbor IP adrese diktiran time na koju IP adresu je stigao zahtev.

  3. Aplikacija bira samo adresu. Tada se port u adresnoj strukturi postavlja na nulu. Funkcija bind() sama odmah bira slobodan port koji dodeljuje soketu.

  4. Aplikacija ne bira ni jedan od parametara. U ovom slucaju nema ni potrebe za pozivom funkcije bind(). Jedina razlika je u tome sto ce se u slucaju nepozivanja funkcije bind() port dodeliti tek prilikom pozivanja connect funkcije, odnosno listen() u slucaju servera. IP adresa bice dodeljena tek nakon pozivanja funkcije connect() odnosno prilikom prihvatanja konekcije od strane servera funkcijom accept().

U slucaju uspeha bind() vraca 0, -1 u slucaju greske.


#include

int listen(int sockfd, int backlog);
Opis:

Funkcija pretvara soket u prijemni soket za konekcije sa klijentima (listening socket). U slucaju da nije bind-ovan port, listen() dodeljuje port soketu. U kernelu se azuriraju dva reda povezana sa ovim soketom: prvi red nepotpunih konekcija( konekcije koje su poslale SYN), i red potpunih konekcija (konekcije koje su poslale i ack). Konekcije iz ovog drugog reda bivaju prihvacene funkcijom accept. Maksimalni broj konekcija u oba reda zadaje se drugim parametrom (s tim sto taj broj nije bukvalno zadat, vec se zadati parametar mnozi nekim faktorom). Funkcija vraca -1 u slucaju greske, 0 za OK.


#include

int accept(int sockfd, struct sockaddr *client, socklen_t *addrlen);
Opis:

Funkcija accept() prihvata konekciju iz reda potpunih konekcija koji se azurira za listening socket zadat prvim parametrom. Funkcija vraca drugi soket (konektovani soket) koji se kasnije u aplikaciji koristi za manipulisanje tom konekcijom. Funkcija vraca -1 u slucaju greske. U drugi parametar se upisuje adresa klijenta koji je konektovan. Treci parametar ima dvosmernu ulogu: pre poziva accept() se celobrojna promenljiva postavlja na velicinu klijent-adresne strukture (drugi parametar) cime se specifira koliko se bajtova ocekuje za upis. Nakon povratka iz accept-a se u ovu promenljivu upisuje stvarni broj upisanih bajtova. Poslednja dva parametra mogu biti NULL pokazivaci . To znaci da nas ne zanima adresa klijenta.


#include

int close(int sockfd);
Opis:

Funkcija se koristi za zatvaranje soketa. Nakon poziva ove funkcije soket se vise ne moze koristiti. Napomenimo da vise socket deskriptora moze pokazivati na isti soket. To se moze desiti ako dupliramo soket deskriptor ( dup() i dup2() ), ili ako forkujemo proces (time se duplira proces, a samim tim i svi deskriptori koje proces drzi otvorene). Slicno, prilikom poziva exec funkcija deskriptori se ne zatvaraju po defaultu, osim ako fcntl() pozivom nije drugacije specificirano. Zato se za svaki otvoren soket odrzava broj deskriptora koji na njega pokazuju. Kada se ovaj broj smanji na nulu, tada se vrsi stvarno zatvaranje soketa, tj. salje se FIN konektovanom paru (prethodno se eventualni podaci u baferu za slanje posalju paru); soket se zaista zatvara kasnije, kada se zavrsi ceo cetvorodelni proces zatvaranja konekcije; u slucaju listening socketa vrsi se trenutno zatvaranje soketa. Funkcija vraca -1 u slicaju greske,0 za OK.


#include
int getsockname( int sockfd, struct sockaddr *localaddr, socklen_t *addrlen);

int getpeername( int sockfd, struct sockaddr *peerladdr, socklen_t *addrlen);
Opis:

Obe funkcije se koriste za ocitavanje adresa i portova, kako svojih (prva od ove dve), tako i konektovanog para (druga). Komentari za druga dva parametra su isti kao i za accept() funkciju, Funkcije vracaju 0 za OK, -1 za gresku. Funkcija getcockname() u svakom trenutku ocitava informacije koje su trenutno dostupne (npr. nakon bind() funkcije ocitava port koji je dodeljen od strane kernela, ali ne i IP adresu, ako je specifirano INADDR_ANY, jer se u tom slucaju adresa odredjuje tek nakon connect() odnosno accept() f-je).


I/O Multipleksiranje



#include

#include
int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout);
Opis:

Funkcija select() izdvaja fajl deskriptore koji su spremni za neku od ulazno-izlaznih operacija, cime se osigurava da nece biti blokiranja. Problem je, zapravo, u tome sto read() ili write() blokiraju proces kada ulazno-izlazna operacija nije trenutno moguca, bilo zato sto nema informacija za citanje (read) ili kernel nije u stanju da prihvati odredjene informacije za pisanje (write). Dok proces stoji blokiran, on nije u stanju da registruje mogucnost citanja i pisanja sa drugih deskriptora; zato se koristi select(): on blokira dok neki od deskriptora koji su za nas od interesa ne bude u stanju da obavi odredjenu operaciju. Tada select() procesu vraca informacije o tome koji je deskriptor za koju operaciju spreman. Tako nema blokiranja prilikom read-write poziva, i u svakom trenutku smo spremni da komuniciramo sa ostalim deskriptorima. Parametar maxfdp1 oznacava maksimalni broj deskriptora pocev od 0 koji se posmatraju (npr. ako je ovaj parametar 6, tada se posmatraju deskriptori 0,1,2,3,4,5; otuda ovo p1 na kraju: plus1). Tip fd_set moze biti implementiran na razlicite nacine, najcesce kao niz integera, sto nas ne zanima jer postoje makroi koji sa njim barataju. To su:



void FD_ZERO(fd_set *fdset); /* postavlja sve bitove na nulu (tj. prazan skup) */

void FD_SET(int fd, fd_set *fdset); /* ukljucuje odgovarajuci bit za fd */

void FD_CLR(int fd, fd_set *fdset); /* iskljucuje odgovarajuci bit za fd */

int FD_ISSET(int fd, fd_set *fdset); /* ispituje da li je odgovarajuci bit ukljucen (1 za da, 0 za ne) */
Upotreba ovih makroa je jasna. Nakon inicijalizacije odgovarajucih skupova postavlja se timeout-vreme kojim opisujemo koliko dugo zelimo da select() blokira. Ovde koristimo sledecu strukturu:

struct timeval {

long tv_sec; /* sekunde */

long tv_usec; /* mikrosekunde */

};

Ovom strukturom se specifira vreme u sekundama i mikrosekundama. Ako se timeval postavi na NULL tada se ceka neograniceno dugo. Ako se oba polja strukture postave na 0, tada se ne ceka uopste (tj. odmah se vraca koji je deskriptor za koju operaciju dostupan). Pri tom deskriptori koji predstavljaju regularne fajlove su dostupni za citanje ako postoje informacije koje se mogu procitati sa tog uredjaja, odnosno ako je moguce upisati podatke. Kod socket deskriptora stvar je malo komplikovanija. Deskriptor koji predstavlja socket je spreman za citanje ako:



  1. Broj bajtova dostupnih za citanje je veci ili jednak low-water vrednosti za prijemni bafer socketa. Ova vrednost se podesava SO_RCVLOWAT opcijom. Na taj nacin mozemo podesiti ovu vrednost tako da nas ne zanima socket osim ako za citanje nije dostupno bar npr. 64 bajta. Ova vrednost je ipak po defaultu postavljena na 1. Naglasimo da ona ima uticaja samo na select() funkciju, ne i na read(), tj. read() ce citati kad god je dostupan bar jedan bajt, bez obzira na vrednost ove opcije.

  2. Ako je drugi kraj prekinuo konekciju, tj. pozvao close() tada svaki poziv read() vraca 0; u ovom slucaju se deskriptor smatra dostupnim za citanje, i select ga tako tretira;

  3. Ako je u pitanju listening socket, tada accept() nece blokirati akko postoji bar jedna kompletirana konekcija za taj socket. U ovom slucaju ce select() smatrati ovaj deskriptor spremnim za citanje.

  4. Ukoliko je doslo do greske na socketu, tj. ako bi read() vratio -1 prilikom poziva, tada se ovaj deskriptor spreman za citanje, i tako ga select() tretira;

Socket deskriptor je spreman za pisanje u sledecim slucajevima:

Ako je broj slobodnih bajtova u baferu za slanje datog socketa veci ili jednak low-water vrednosti za ovaj bafer, sto se podesava SO_SNDLOWAT opcijom. Po defaultu ova vrednost je 2048. Drugi uslov je da je ili u pitanju povezani socket (dakle ne listening) u slucaju TCP-a, odnosno da socketu nije ni potrebna konekcija (UDP socket). Specijalno, kod UDP socketa, bafer za slanje i ne postoji ,vec postoji samo njegova velicina, kojom se prakticno ogranicava velicina UDP datagrama. Zato je kod UDP-a neophodan i dovoljan uslov za mogucnost pisanja da je low-water manji ili jednak od specificirane velicine (nepostojeceg) bafera za slanje.

Ako drugi kraj konekcije zatvori deskriptor, tada se prilikom pisanja generise signal SIGPIPE koji po defaultu izaziva terminaciju procesa. Socket deskriptori kod kojih je par zatvorio konekciju spremni su za pisanje i tako ih select() tretira.

Doslo je do greske na socketu, tj. write() bi vratio -1 u slucaju poziva. Tada se ovaj deskriptor smatra dostupnim za citanje.

Treci skup, exceptset registruje deskriptore kod kojih je doslo do izuzetaka. Pod izuzetkom se najcesce podrazumeva pojava tzv. prioritetnih podataka (out-of-band data). Ako nas neka operacija ne zanima, tada mozemo odgovarajuci parametar postaviti na NULL; tako cemo exceptset najcesce postavljati na NULL. Cesto cemo i writeset postaviti na NULL jer ce nas zanimati samo citanje. Ako i readset postavimo na NULL tada cemo u stvari dobiti tajmer koji blokira dok ne istekne vreme a zatim vraca 0; ovaj tajmer je precizniji od sleep() tajmera koji meri vrednost u sekundama! Jos jedno objasnjenje oko samog sadrzaja skupa deskriptora fd_set: mi predajemo select() funkciji skup deskriptora koji su za nas od interesa, a funkcija nam preko istih pokazivaca vraca skup deskriptora koji su za datu operaciju dostupni. Dakle, u pitanju su dvosmerni parametri.

Funkcija select() vraca broj dostupnih deskriptora, 0 u slucaju isteka vremena, odnosno -1 u sucaju greske;


#include
int shutdown(int sockfd, int howto);
Opis:

Funkcija vrsi zatvaranje konekcije. Razlika izmedju ove funkcije i close() poziva je u sledecem: close() zatvara deskriptor, a konekcija se pri tom zatvara samo ako ne postoje drugi deskriptori koji pokazuju na isti socket. Dalje, close() zatvara konekciju u oba smera; nasuprot tome, shutdown() zatvara konekciju bez obzira na druge deskriptore, a ima mogucnost zatvaranja samo jednog od smerova komunikacije, u zavisnosti od parametra howto. Ovaj parametar moze imati vrednost SHUT_RD, SHUT_WR, SHUT_RDWR. U prvom slucaju se konekcija zatvara za citanje, a eventualni podaci u prijemnom baferu se odbacuju; svaki poziv read() nadalje vraca 0; svi kasnije primljeni podaci bivaju odbaceni; u drugom slucaju se konekcija zatvara za pisanje, svi eventualni podaci u baferu za slanje se salju, a zatim se salje FIN, cime se obezbedjuje da read() funkcija na drugom kraju konekcije uvek vraca 0; medjutim, par i dalje moze slati podatke, kako mi nismo zatvorili konekciju za citanje, nece biti poslat RST paru, odnosno nece biti generisan SIGPIPE na drugom kraju konekcije. Mi i dalje mozemo da pozivamo read() jer nismo zatvorili deskriptor, kao u slucaju close(). U trecem slucaju se desavaju prvi i drugi slucaj zajedno. Funkcija vraca 0 za OK, -1 za gresku.


#include

int poll(struct pollfd *fdarray , unsigned long nfds, int timeout);
Opis:

Ova funkcija radi isto sto i select() uz neke dodatne mogucnosti. Elementi niza fdarray su strukture tipa pollfd. Ova struktura data je na sledecoj strani:



struct pollfd {

int fd; /* fajl deskriptor koji nas zanima */

short events; /* dogadjaji koji nas zanimaju */

short revents; /* dogadjaji koji su se desili (upisuje kernel) */

};
Pri tom se za postavljanje odgovarajucih dogadjaja, kao i za citanje istih koriste sledeci flagovi:


Flag

Znacenje

POLLRDNORM

spreman za citanje

POLLRDBAND

citanje prioritetnih podataka

POLLIN

POLLRDNORM | POLLRDBAND (disjunkcija)

POLLWRNORM

spreman za pisanje

POLLWRBAND

pisanje prioritetnih podataka

POLLOUT

isto kao i POLLWRNORM

POLLPRI

citanje podataka visokog prioriteta

POLLERR

doslo je do greske

POLLHUP

doslo je do prekida veze

POLLNVAL

deskriptor nije otvoren

Poslednja tri flaga se nikada ne postavljaju u events, vec se iskljucivo dobijaju kao rezultat u revents od strane kernela. Oni identifikuju razlog zbog koga je doslo do greske. To je jedna prednost poll() funkcije. Flagovi POLLIN i POLLOUT se najcesce koriste. Ove konstante su starijeg datuma, pa je na novijim sistemima preporucljivo koristiti POLLRDNORM i POLLWRNORM. Prioritetni podaci iz tabele se cesto oznacavaju kao out-of-band data. Drugi parametar poll() funkcije je broj elemenata u nizu struktura, dok je treci kao i kod select()-a timeout period, izrazen u milisekundama. Ako se postavi na 0 tada ne ceka uopste, vec odmah vraca rezultat. Ako se postavi na pozitivnu vrednost ceka odgovarajuci broj milisekundi (ili vraca ranije, ako neki deskriptor bude spreman). Ako postavimo ovaj parametar na INFTIM, tada se ceka neograniceno dugo. Funkcija vraca broj spremnih deskriptora (odnosno, broj struktura u nizu ciji je revents razlicit od nule), 0 u slucaju isteka vremena, i -1 u slucaju greske. Pri tom se deskriptor smatra dostupnim za citanje ako postoje podaci koji se mogu procitati u tom trenutku. Slicno je i za pisanje. Opcije low-water nemaju uticaja na poll() vec samo na select(). Ove normalne read-write sposobnosti se oznacavaju xxxNORM flagovima. Ostale specijalne okolnosti nabrojane kod select() funkcije ovde se kontrolisu na sledeci nacin:



  1. Ako je par zatvorio konekciju za pisanje (tj. poslao je FIN) tada read() vraca 0 pri svakom pozivu. U ovom slucaju se deskriptor takodje tretira flagom POLLRDNORM.

  2. Ako je doslo do greske tada se ona identifikuje uz pomoc poslednaj tri flaga. Neke greske mogu biti tumacene i kao normalan ulaz-izlaz.

  3. U slucaju listening-socketa mogucnost prihvatanja nove konekcije se moze, u zavisnosti od implementacije, tumaciti i kao prioritetni ulaz, ili kao normalan ulaz. Najcesce se tumaci kao normalan.

Ako nas neki od deskriptora u nizu vise ne zanima, mozemo postaviti njegovo fd polje na negativnu vrednost. U tom slucaju ce poziv poll() funkcije ignorisati taj clan niza pollfd struktura, a njegob revents ce postaviti na nulu. Za vise detalja o poll() funkciji i koriscenju njenih event flagova pogledati UNPV1.


Socket opcije



#include
int getsockopt( int sockfd, int level, int optname, void *optval, socklen_t *optlen);

int setsockopt( int sockfd, int level, int optname, const void *optval, socklen_t optlen);
Opis:

Funkcije sluze za citanje i postavljanje opcija vezanih za sockete. Postoji veliki broj opcija, a mogu se podeliti na opcije koje se prosto ukljucuju ili iskljuciju (flagovi) i opcije koje postavljaju odredjenu vrednost. Prvi argument obe funkcije je socket za koji citamo/postavljamo opcije. Drugi parametar level se koristi za specificiranje grupe kojoj opcija pripada (SOL_SOCKET za osnovne opcije, IPPROTO_IP za Ipv4 opcije, IPPROTO_IPV6 za Ipv6, IPPROTO_TCP za TCP opcije). Treci parametar predstavlja ime opcije kojom se bavimo. Parametar optval je pokazivac na podatak koji predstavlja vrednost opcije, kao i prostor u koji treba smestiti tu vrednost u slucaju citanja. Za slucaj flag opcija smatra se da je opcija ukljucena ako joj je vrednost razlicita od nule, a u suprotnom je iskljucena (u pitanju je int podatak). Vecinu opcija je moguce i postavljati i ocitavati, mada postoje i neke za koje je dozvoljena samo jedna od operacija. Poslednji parametar je kao i ranije velicina bafera optval. U slucaju getsockopt() funkcije u pitanju je dvosmerni parametar, tj. prilikom poziva se predaje velicina bafera, a funkcija nakon vracanja upisuje u njega stvarnu velicinu upisanog podatka. Obe funkcije vracaju 0 za OK, -1 u slucaju greske.

Neke znacajnije opcije:

- SO_BROADCAST – omogucava broadcast-ovanje za taj socket (iskljucivo UDP).

- SO_DEBUG – ukljucuje debug za taj socket (tj. belezi celokupan transfer na tom socketu).

- SO_DONTROUTE – sprecava rutiranje za taj socket.

- SO_KEEPALIVE – omogucava proveru postojanja konekcije u slucaju da duze vreme nema saobracaja;

- SO_RVCBUF i SO_SNDBUF – definisu velicinu prijemnog bafera i bafera za slanje.

- SO_RCVLOWAT i SO_SNDLOWAT – definisu low-water vrednosti za slanje i prijem (select funkcija)

- SO_REUSEADDR i SO_REUSEPORT – omogucavaju koriscenje zauzetih adresa i portova na host-u.

- SO_LINGER – definise ponasanje close() funkcije, tj. nacin prekidanja konekcije;

- SO_TYPE – vraca tip socketa (pogodno nakon exec funkcije, za utvrdjivanje tipa). Ovo je read-only opcija.

- TCP_MAXSEG – definise maksimalnu velicinu segmenta (MSS opcija) za TCP.

- TCP_KEEPALIVE – specificira vreme u sekundama nakon kog se salju signali za utvrdjivanje zivota konekcije. Po defaultu, ovo vreme je dva sata (7200). Ova opcija ima smisla jedino kada je ukljucena SO_KEEPALIVE opcija;


Osim funkcija getsockopt() i setsockopt() za postavljanje i ocitavanje nekih opcija vezanih za sockete koriste se i funkcije fcntl() i ioctl(). O njima ovde nece biti reci;

Elementarni UDP soketi



#include

#include
ssize_t recvfrom( int sockfd, void *buff, size_t nbytes, int flags,struct sockaddr *from, socklen_t *addrlen);

ssize_t sendto( int sockfd, const void *buff, size_t nbytes, int flags,const struct sockaddr *to, socklen_t addrlen);
Opis:

Funkcije se koriste za slanje i prijem datagrama preko UDP protokola. Prva tri parametra obe funkcije su identicna kao kod read() i write() sistemskih poziva. Cetvrti parametar podrazumeva neke dodatne opcije, i u najvecem broju slucajeva ce biti nula (videti napomene). Poslednja dva parametra funkcije recvfrom() su dvosmerni argumenti: prilikom poziva se from postavlja na adresu adresne strukture u koju cemo smestati podatke o posiljaocu. Parametar addrlen pokazuje na ceo broj koji predstavlja velicinu adresne strukture. Prilikom povratka, funkcija u from upisuje adresu posiljaoca, a u addrlen stvarnu velicinu popunjene strukture. Kod sendto() je situacija jednostavnija: parametar to pokazuje na strukturu u kojoj je zapisana adresa primaoca, a addrlen je velicina ove strukture. Funkcije vracaju broj upisanih/procitanih bajtova. U slucaju greske vracaju -1. Slanje praznih datagrama je takodje legitimno, pa je zato povratna vrednost 0 za recvfrom() takodje legitimna. To ne znaci da je zatvorena konekcija, jer kod UDP-a nema konekcije. UDP socketi se kreiraju socket() funkcijom sa SOCK_DGRAM vrednoscu drugog parametra. Server koristi bind() funkciju za dodelu porta. Pri tom se moze koristiti i specificiranje odredjene IP adrese (u slucaju hosta sa vise od jedne IP adrese) ali se na taj nacin server ogranicava na prijem i slanje datagrama samo sa jedne adrese. Klijent najcesce ne poziva bind() vec se prilikom prvog poziva sendto() njemu dodeljuje slobodan port. IP adresa se u tom slucaju dodeljuje svaki put kada se sendto() pozove, tj. ona nije fiksna. Neke vazne napomene:

-Ako server nije u funkciji aplikacija najcesce nije u mogucnosti to da sazna, jer se greska o nedostupnosti porta javlja tek kasnije kada se datagram vrati kroz mrezu.

-Funkcija sendto() vraca vrednost kada datagram bude isporucen datalink layar-u, tj. kada bude poslat preko odgovarajuceg mreznog interfejsa. Uspesan povratak sendto() funkcije ne znaci da je datagram stigao gde treba. -sendto() moze da upisuje podatke uvek, s obzirom da nema nikakav bafer za slanje. Prijemni bafer je, s druge strane ogranicene velicine, pa datagrami koji pristignu u trenutku kada je bafer pun bivaju ispusteni. UDP nema nikakvu kontrolu toka.

-ako klijent posalje zahtev serveru, a zatim blokira u recvfrom() pozivu, cekajuci odgovor od servera, postoji mogucnost da server ne primi klijentov datagram. Kako ne postoji mogucnost da klijent to sazna, on ce biti zauvek blokiran cekajuci odgovor koji nikada nece stici. Problem mogu biti i IP adrese u slucaju host-ova sa vise IP adresa. Tada se moze desiti da klijent i server razmenjuju poruke sa razlicitih IP adresa, tj. naizmenicno menjajuci svoj home IP. Ako je port i IP poznat, tada svako u mrezi moze slati datagrame toj aplikaciji. Zato cesto aplikacija proverava adresu i port da bi bila sigurna da je to datagram od pravog posiljaoca. Medjutim, ako par naizmenicno koristi razlicite IP adrese, moze doci do odbacivanja nekih datagrama koji dolaze sa prave lokacije.

-funkcije sendto() i recvfrom() se mogu koristiti i u slucaju TCP konektovanih socketa, ali pri tom se poslednja dva parametra ignorisu (ipak, treba ih postaviti na nulu). Funkcija ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags); se u tom slucaju moze koristiti (ovo je ekvivalentno sa sendto() sa poslednja dva parametra NULL i 0; takodje write() je ekvivalentan sa send() pri cemu se parametar flags postavlja na 0). Slicno ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags); je identican sa recvfrom() sa poslednja dva parametra jednaka NULL. Takodje read() je identican sa recv() sa flags==0. recv() se moze koristiti samo sa konektovanim socketima.

-Funkcija recvfrom() blokira ukoliko nema primljenih poruka u prijemnom baferu (osim ako socket nije postavljen da bude nonblocking, fcntl() pozivom). Prijemni bafer funkcionise kao FIFO, a poruke cekaju u redu da budu procitane. Ako je poruka koja je na redu za citanje duza od specifirane duzine bafera, suvisni bajtovi se odbacuju. Ovo je u suprotnosti sa ponasanjem TCP socketa, koji komunikaciju predstavlja kao niz bajtova i kao takve tretira sve poruke. Kod TCP-a bi preostali bajtovi bili procitani u narednom pozivu read() funkcije. Suprotno tome, UDP je datagram protokol, i poruke salje i prima u celini.

-Funkcija sendto() blokira samo u slucaju da radi sa TCP socket-om, i to u slucaju kada u baferu za slanje nema dovoljno slobodnog mesta za sve podatke iz aplikativnog bafera. U slucaju UDP socketa, nema blokiranja, jedino je moguce da datagram ne bude isporucen jer je veci od maksimalne velicine UDP datagrama koja se specifciira SO_SNDBUF opcijom (odnosno, bafer za slanje ima svoju velicinu, ali sam bafer ne postoji).

- Funkcija recvfrom() moze imati poslednja dva parametra jednaka nuli (NULL i 0). To se koristi u slucaju da je konektovan socket u pitanju (vidi dole) ili ako nas ne zanima ko salje poruku.

- Funkcija connect() se moze koristiti i u slucaju UDP protokola. Naime, kod TCP-a je connect() funkcija uspostavljala konekciju izmedju klijenta i servera. Kod UDP-a nema konekcije, pa tako i connect() ne funkcionise na isti nacin kao u prethodnom slucaju. connect() se kod UDP-a koristi ukoliko zelimo da komuniciramo samo sa jednim parom ( tj da vise puta pozivamo recvfrom() i sendto() sa istom IP adresom i portom). U tom slucaju je jednostavnije pozvati connect() cime se samo zapamti od strane kernela adresa para sa kojim komuniciramo. Nakon toga mozemo pozivati funkcije send() i write() umesto sendto(), i read() i recv() umesto recvfrom(). Osim jednostavnosti, dobijamo i na efikasnosti, jer bi kernel inace pri svakom pozivu sendto() ili recvfrom() najpre zapamtio adresu, a zatim izvrsio transfer, i na kraju “prekinuo” konekciju. Mnogo je efikasnije iskopirati adresu kernelu jednom, zatim vise puta izvrsiti transfer, i na kraju diskonektovati socket. Diskonektovanje UDP socketa se vrsi pozivom connect() funkcije sa sin_family (odnosno sin6_family za Ipv6) clanom u adresnoj strukturi postavljenim na AF_UNSPEC. Takodje, connect() se moze pozivati i za vec konektovani socket, kako bi mu se promenila zapamcena IP adresa para. Ono sto je jos prednost konektovanog UDP socketa je to sto se u tom slucaju aplikaciji isporucuju greske koje nastaju usled nemogucnosti dostave datagrama (to su asinhrone greske, tj. greske koje nastaju kasnije, nakon uspesnog povratka iz sendto() funkcije). Ovo se cesto desava kada server nije u funkciji, o cemu je vec bilo reci. U slucaju konektovanog socket-a, sve poruke koje stignu sa neke druge lokacije, osim one na koju je konektovan socket, bivaju odbacene. Na taj nacin se osigurava da socket komunicira sa jednim, i samo jednim parom.

- Argument flags predstavlja bitsku disjunkciju sledecih flagova:

- MSG_DONTROUTE – sprecavanje rutiranja (kao i kod opcije SO_DONTROUTE, s tim sto u ovom slucaju vazi samo za taj poziv sendto() ili send() funkcije a ne za kompletan prenos na socket-u).

- MSG_DONTWAIT – sprecava blokiranje i vraca -1 (errno==EAGAIN). Slicno, isti efekat se postize postavljanjem O_NONBLOCK flaga za socket fajl deskriptor, s tim sto MSG_DONTWAIT vazi samo za tu ulazno-izlaznu operaciju, a ne za kompletan prenos na socket-u.

- MSG_OOB – citanje i pisanje prioritetnih (out-of-band) podataka. Za detalje pogledati UNPv1.

- MSG_PEEK – koristi se samo prilikom citanja. Omogucava citanje podataka iz prijemnog bafera, ali ih ne uklanja iz bafera, tako da naredni poziv recv() funkcije vraca iste podatke (u slucaju TCP-a, medjutim, naredni poziv moze vratiti vise podataka, jer je TCP tok bajtova, pa novopristigli bajtovi mogu biti procitani sa ranije pristiglim bajtovima istim pozivom recv() funkcije.U slucaju UDP-a oba poziva ce vratiti isti rezultat jer je isti datagram i dalje prvi na redu).

- MSG_WAITALL – normalno, read() funkcija moze vratiti i manje bajtova nego sto se zahteva. To se najcesce desava zato sto trenutno ima manje dostupnih bajtova za citanje. Ovaj flag omogucava da recv() blokira sve dok se ne procita onoliko bajtova koliko je specificirano. Ipak, i u ovom slucaju recv() moze vratiti manje bajtova, ako poziv bude prekinut signalom, dodje do greske ili prekida konekcije.



Konverzija adresa i imena



#include
struct hostent *gethostbyname( const char *hostname);
Opis:

Funkcija prihvata ime domena, inicira pretragu DNS servera za datim domenom, a zatim vraca pronadjene podatke u obliku hostent{} strukture. Funkcija vraca NULL u slucaju greske (npr. ako domen nije pronadjen).

Greska se ne postavlja kao vrednost errno promenljive, vec postoji posebna h_errno promenljiva (definisana u netdb.h). Struktura hostent ima sledeci oblik:

struct hostent {

char *h_name;

char **h_aliases;

int h_addrtype;

int h_length;

char **h_addr_list;

};

#define h_addr h_addr_list[0];

Poslednja direktiva ubacena je zbog kompatibilnosti sa starijim softverom, i ne treba je koristiti.Clan h_name je string koji predstavlja kanonsko ime host-a. Svaki host moze imati i alias-e, a ime koje smo mi dali prilikom poziva funkcije moze biti i alias, kao i kanonsko ime. Niz stringova zadat clanom h_aliases predstavlja spisak svih pronadjenih aliasa za dati domen. Ovaj niz zavrsava se NULL pokazivacem. Clan h_addrtype, oznacava verziju IP protokola, moze biti AF_INET, ili AF_INET6; clan h_length je ili 4 (za IPv4), ili 16 (za Ipv6), i predstavlja velicinu adrese u bajtovima. Poslednji clan strukture predstavlja niz pokazivaca na adresne strukture in_addr{}, odnosno in6_addr{} (ovo je vazno naglasiti, jer je neophodno cast-ovanje char * -tipa u tip in(6)_addr *). i ovaj niz se zavrsava NULL pokazivacem. Vazno pitanje je kada se vrsi pretraga IPv4 adresa, a kada IPv6 adresa? Pod UNIX-om funkcionise named deamon proces koji ima zadatak da funkcionise kao resolver. Resolver funkcije poput gethostbyname() i ostalih kojima se bavimo u ovom poglavlju konsultuju named proces, koji proverava fajl /etc/hosts, a zatim, ukoliko ne pronadje trazeni host, kontaktira DNS server ciji se IP nalazi u fajlu /etc/resolv.conf. Nakon zavrsenog pretrazivanja rezultat se predaje resolver funkcijama koje ga vracaju procesu. Zapisi koji se pretrazuju u bazi DNS-a su ili A-zapisi (za IPv4) ili AAAA-zapisi (za IPv6). Ako je postavljena resolver opcija RES_USE_INET6, tada se trazi AAAA-zapisi, a u suprotnom se trazi A-zapis. Pri tom, u prvom slucaju ako pronadjeni host nema AAAA-zapis, tada se procesu vraca IPv4 adresa (ako je ima) konvertovana u IPv6 adresu (IPv4-mapped). Ukoliko ne pronadje ni A-zapis, tada vraca gresku. Opcija RES_USE_INET6 se postavlja na jedan od sledecih nacina:

- programskim kodom:

#include

res_init();

_res.options|=RES_USE_INET6;

Ovaj kod se postavlja na pocetku, pre koriscenja resolver funkcija, i postavlja opciju samo za pozivajuci proces, a ne i za ostale procese na host-u.

- postavljanjem promenljive okruzenja RES_OPTIONS=inet6; to se moze uciniti iz shell-a postavljanjem promenjljive, a zatim pozivanjem export komande. Ovo se cesto radi automatski, tako sto se sve to ubaci u .bash_profile, ili .bashrc fajl. Ovakav nacin postavlja opciju za sve procese koji ce biti pokrenuti Iz tog shell-a.

- fajl /etc/resolv.conf moze sadrzati liniju

options inet6

u ovom slucaju ce odgovarajuca opcija biti vazeca za sve procese na tom host-u.

#include
struct hostent *gethostbyname2(const char*hostname, int family);
Opis:

Funkcija omogucava dodatnu fleksibilnost, u smislu da ima mogucnost rada sa IPv6 protokolom. Naime, drugi parametar je ili AF_INET, ili AF_INET6, u zavisnosti od protokola. Ukoliko je postavljen AF_INET, tada se pretrazuju iskljucivo A-zapisi. Ukoliko se ne pronedje IPv4 adresa, vraca se greska (NULL pokazivac i h_errno postavljen ne odgovarajucu vrednost). U zavisnosti od toga da li je RES_USE_INET6 opcija resolvera ukljucena, pronadjena adresa bice konvertovana u IPv6 adresu, i kao takva vracena procesu. Ukoliko je postavljen AF_INET6, tada se trazi AAAA zapis. Ukoliko se zapis ne pronadje, dolazi do greske (IPv4 se ne trazi, tj. nema mapiranja). U ovom slucaju je nebitno da li je postavljena opcija resolvera.


#include
struct hostent *gethostbyaddr(const char *addr, size_t len, int family);
Opis:

Funkcija obavlja isti posao kao i prethodna, s tim sto ocekuje kao argument IP adresu host-a. U ovom slucaju se pretraga vrsi po trecoj vrsti zapisa: u pitanju su PTR-zapisi. Prvi parametar je u stvari pokazivac na strukturu in_addr{}, ili in6_addr{} u zavisnosti od protokola. Drugi parametar je velicina te strukture (4, odnosno 16). Treci parametar je AF_INET za IPv4, odnosno AF_INET6, za IPv6. Moguci ishodi su sledeci:

- Ako je family==AF_INET, tada se pretrazuju IPv4 adrese. Pri tom je len==4. Ukoliko je postavljena RES_USE_INET6 opcija, tada se povratna adresa mapira1 u IPv6 adresu.

- Ako je family==AF_INET6, a len==16, i ukoliko je predata adresa IPv4-mapirana IPv6 adresa, ili je IPv4 kompatibilna tada se pretrazuju IPv4 adrese, pri cemu se trazi adresa predstavljena nizim 32-bitnim delom IPv6 adrese.. i ovde se u slucaju RES_USE_INET6 opcije povratna vrednost adrese mapira u IPv6 adresu; U slucaju da je data IPv6 adresa koja nije mapirana niti kompatibilna sa IPv4 adresom, trazi se IPv6 adresa, i povratna adresa upisana u hostent{} strukturi je uvek IPv6 adresa.

Dakle, opsti zakljucak je da je povratni tip adrese u slucaju pretrazivanja IPv6 adresa obavezno IPv6 adresa, a u slucaju IPv4 adresa rezultat zavisi od opcije resolvera. Pri tom se i u slucaju family==AF_INET6 moze vrsiti IPv4 pretrazivanje, ako je u pitanju mapirana ili IPv4-kompatibilna IPv6 adresa.

Funkcija vraca NULL u slucaju greske.


#include
int gethostname(char *name, size_t namelen);
Opis:

Funkcija u string upisuje ime host-a. String je duzine najvise namelen i funkcija upisuje ‘\0’ ukoliko ima mesta. Funkcija zavisno od implementacije moze upisati punu adresu (FQDN, npr. alas.matf.bg.ac.yu) ili samo ime hosta (npr. alas). Vraca 0 za OK, -1 u slucaju greske.



#include

int uname(struct utsname *name);
Opis:

Ova funkcija ispunjava strukturu utsname {}. U njoj se nalaze podaci o sistemu. Ova struktura ima oblik:

#define _UTS_NAMESIZE 16

#define _UTS_NODESIZE 256

struct utsname {

char sysname[_UTS_NAMESIZE]; /* ime op. sistema (npr. Linux) */

char nodename[_UTS_NODESIZE]; /* ime hosta (isto kao i kod gethostname() ) */

char release[_UTS_NAMESIZE]; /* glava verzija op. sistema */

char version[_UTS_NAMESIZE]; /* dodatna informacija o verziji */

char machine[_UTS_NAMESIZE]; /* tip hardvera */

};

Funkcija vraca 0 za OK -1 za gresku. Prethodne dve funkcije se cesto koriste u kombinaciji sa prethodnim skupom funkcija, da bi se odredila IP adresa lokalnog host-a.



#include

struct servent *getservbyname(const char* servname, const char* protoname);

struct servent *getservbyport(int port, const char *protoname);
Opis:

Svaki server koji radi na racunaru zauzima odgovarajuci port. Kako ne bismo morali da pamtimo brojeve portova, u fajlu /etc/services cuvaju se informacije o imenima odgovarajucih servisa i njima pridruzenim portovima. Ovaj fajl takodje sadrzi informacije o protokolu (UDP ili TCP). Gornje funkcije barataju sa informacijama iz ovog fajla. Prva funkcija na osnovu datog imena servisa (npr. “daytime”), i protokola (“udp” ili “tcp”) odredjuje port na kome server radi. Informacije o serveru se upisuju u strukturu servent{} :



struct servent {

char *s_name; /* ime servisa */

char **s_aliases; /* niz stringova koji predstavljaju aliase servisa */

int s_port; /* broj porta */

char *s_proto; /* protokol “tcp” ili “udp” */

};

Parametar servname moze biti postavljen na NULL. To znaci da nas ne zanima protokol.U tom slucaju, ukoliko servis postoji i u UDP i u TCP varijanti, koji ce port biti vracen zavisi od implementacije (sto najcesce nije bitno, jer se najcesce koriste isti brojevi porta za oba protokola). Druga funkcija vraca iste informacije, ali na osnovu broja porta, i imena protokola. Broj porta mora biti u mreznom rasporedu bajtova. Napomene o protokolu su iste kao i za prethodnu funkciju. Funkcije vracaju NULL u slucaju greske.


#include

void herror(const char *s);

char *hstrerror(int errnum);
Opis:

Funkcije barataju h_errno promenljivom koja je vec pomenuta (poput perror() i strerror() za standardne errno-greske). herror() ispisuje string s pracen porukom o gresci koja je postavljena h_errno promenljivom. hstrerror()



vraca string koji sadrzi poruku o gresci koja odgovara zadatoj vrednosti h_errno promenljive. Vraca NULL u slucaju greske. Ove funkcije treba koristiti umesto standardnih kada radimo sa gethostbyXXX() funkcijama.

1 IPv6 adresa je 128-bitni binarni broj, koji se u heksadekadnom obliku standardno zapisuje kao osam cetvorocifrenih heksadekadnih brojeva razdvojenih dvotackama (x:y:z:u:v:w:q:r). Mapirana IPv6 adresa je ona adresa kod koje su nizih 32 bita IPv4 adresa, zatim slede 16 jedinica, a zatim sve ostale nule. U heksadekadnom zapisu se ovo najcesce zapisuje kao ::ffff:a.b.c.d, gde je a.b.c.d IPv4 adresa u dekadnom zapisu. IPv4-kompatibilna IPv6 adresa je ona kod koje se 96 visih bitova postavljaju na nulu, a nizih 32 predstavljaju IPv4 adresu. Ovo se u heksadekadnom obliku predstavlja kao ::a.b.c.d . Vodece dvotacke u oba slucaja zamenjuju vodece nule (mogu se zapisati i vodece nule, tj. 0:0:0:0:0:0:a.b.c.d ili 0:0:0:0:0:ffff:a.b.c.d ali je lakse bez njih  ). Funkcije inet_ntop() i inet_pton() iz prvog poglavlja rade sa ovim formatima za IPv6 adrese.




Yüklə 92,17 Kb.

Dostları ilə paylaş:




Verilənlər bazası müəlliflik hüququ ilə müdafiə olunur ©genderi.org 2024
rəhbərliyinə müraciət

    Ana səhifə