Back to site
Since 2004, our University project has become the Internet's most widespread web hosting directory. Here we like to talk a lot about web servers, web development, networking and security services. It is, after all, our expertise. To make things better we've launched this science section with the free access to educational resources and important scientific material translated to different languages.

Uvod u C

Uvod u mrežne funkcije u C


Sadržaj


Uvod

U ovom uputstvu, ja ću pokušati da objasnim upotrebu i sintaksu nekih osnovnih UNIX mrežnih funkcija u C. Ako želite da saznate više o Windows Sockets programiranju, bojim se da su WinSock sredstva u StarDust sada nestala - svi pokazivači ka sličnim stranicama će biti dobrodošli!

Možda ćete želeti da proverite
Internet programming crash course. Takođe možete posetiti Networking ABC Everything Networking, od kućnog umrežavanja 2 računara do XP umrežavanja.

Postoje neki pokazni programi koje ću objasniti kako da se napišu od početka. Ako želite da saznate više o funkciji, možda ćete imati on-line priručnike (koristite 'man function') ili će vam možda vaši administratori sistema pružiti uputstva.

Možete videti ili preuzeti izvorni kod ali ne mogu da garantujem da će raditi na svim sistemima, pa ako vam neki kod ne radi, molimo Vas pošaljite mi email sa detaljima vašeg sistema (Operativni sistem, Arhitektura i sl) i šta ne radi, a ja ću videti da li mogu da ga popravim.

Takođe bih bio zahvalan da mi pošaljete email ukoliko RADI na vašem sistemu (ili ako ste uspeli da modifikujete delove i da ga namestite da radi), ako biste mi poslali email sa izmenama, onda bih mogao uključiti vaše izmene u izvorni kod.
Neke od platformi koje su testirane:

  • Linux, kernel 2.2.x, gcc2
  • Linux, kernel 2.4.x, gcc3
  • Mac OSX 10.3 - reference na malloc.h mora da se promene u sys/malloc.h i kao i 'ar', takođe je potrebno da pokrenete "ranlib" na biblioteka datoteci libnet.a (izgrađena od izvora mrežnih biblioteka)
  • HPUX - treba da dodate -lsocket kada povezujete izvršni program kao što je pomenuto u nastavku.

Sa nekim verzijama UNIX, pokazni programi se neće povezati, i žaliće se na stvari kao što su nedefinisano povezivanje simbola, prihvatiti, slušati i soket.

Takve greške će verovatno izgledati ovako:

Undefined                       first referenced
 symbol                             in file
socket                              /var/tmp/ccLkjMcu.o
accept                              /var/tmp/ccLkjMcu.o
bind                                /var/tmp/ccLkjMcu.o
listen                              /var/tmp/ccLkjMcu.o
ld: fatal: Symbol referencing errors. No output written to a.out

Ovo obično ukazuje na to da bi trebalo da se povežete sa dodatnom bibliotekom dodavanjem -lsocket u cc redu.
Na primer: cc -o netapp netapp.c -lsocket

Ovaj tutorijal pretpostavlja da znate kako da vršite programiranje u C.

Ako vam je ovo uputstvo bar malo interesantno, molimo Vas kontaktirajte me i javite mi. Ono nije ni blizu završeno još, ali u svakom slučaju mi pošaljite email, čak i ako ćete samo reći "Hej! dovršite taj tutorijal" :-)
Povratak na sadržaj


Kreiranje soketa

Prva stvar koju treba uraditi je da kreirate soket kojim možete da manipulišete na više načina. Da biste napravili soket, možete da koristite socket() funkciju.

Includes:

#include <sys/types.h>
#include <sys/socket.h> 

Syntax:

int socket(int af, int type, int protocol);

C source:

int socket_desc;

  socket_desc=socket(AF_INET,SOCK_STREAM,0);
  if (socket_desc==-1)
    perror("Create socket");

socket() vraća a socket descriptor koji se može koristiti u drugim mrežnim komandama. Ovo će stvoriti podnožje koje koristi DARPA Internet adrese, a način povezivanja je byte stream koji je sličan pipe-u. Alternativa byte streams je datagram ali ovo uputstvo ga ne pokriva.
Sa serverima, prvo kreirano podnožje je često poznato kao " master socket". Pre nego što soket počne da razmenjuje podatke, on mora biti povezan sa drugim soketom. Ako deluje kao master socket, on mora biti vezan za broj porta tako da klijenti znaju gde da "pronađu" soket i povežu se sa njim.
Ako uspe, soket() vraća važeći soket-deskriptor; u suprotnom on vraća -1 i postavlja errno da ukaže na grešku. perror() ili strerror() može da se koristi da pretvori errno vrednost u čoveku čitljivi string.

Povratak na sadržaj


Priključivanje soketa na port

Da podesite master soket, potrebno je da povežete soket-deskriptor na mnogo načina. Da biste napravili soket, možete da koristite soket() funkciju kao što je opisano iznad u Stvaranje soketa.

Uključuje:

#include <sys/socket.h>
#include <netinet/in.h>

Syntax:

int bind(int s, struct sockaddr *addr, int addrlen);

C source:

struct sockaddr_in address;

/* type of socket created in socket() */
  address.sin_family = AF_INET;
  address.sin_addr.s_addr = INADDR_ANY;
/* 7000 is the port to use for connections */
  address.sin_port = htons(7000);
/* bind the socket to the port specified above */
  bind(socket_desc,(struct sockaddr *)&address,sizeof(address));

Ako je uspešno, bind() vraća 0; u suprotnom vraća -1 i postavlja errno da ukaže na grešku.
Port naveden u izvornom kodu iznad (port 7000) je mesto gde server može da se poveže. Da biste to proverili, kompajlirajte program `sockbind" iz direktorijuma izvornog koda i pokrenite ga. Dok se pokreće, kucajte:

telnet localhost 7000

i trebalo bi da dobijete

Pokušava...

za nekoliko sekundi, a zatim

telnet: Nije moguće povezivanje sa udaljenim hostom: Povezivanje odbijeno

kada serverski program završi. Ovo ukazuje da je server ok. Ako je konekcija odmah odbijena, verovatno postoji problem sa serverom.

Povratak na sadržaj


Traženje konekcija

Pre nego što bilo koje konekcije mogu biti prihvaćene, soketu mora biti rečeno da traži veze i maksimalan broj veza na čekanju koristeći listen()

Includes:

#include <sys/socket.h>

Syntax:

int listen(int s, int backlog);

C source:

  listen(socket_desc,3);

red iznad određuje da može biti do 3 veze na čekanju. Ako zahtev za vezu stigne kada već postoje 3 veze na čekanju, klijent dobija timeout grešku.

listen() se primenjuje samo na nepovezane sokete tipa SOCK_STREAM. Ako soket nije vezan za lokalni port pre nego što se listen() poziva, sistem automatski vezuje lokalni port za soket da pretražuje.
Ako je uspešno, listen() vraća 0; u suprotnom vraća -1 i postavlja errno da ukaže na grešku.

Povratak na sadržaj


Prihvatanje veze

Da biste zapravo rekli serveru da prihvati vezu, morate da koristite funkciju accept()

Includes:

#include <sys/socket.h>

Syntax:

int accept(int s, struct sockaddr *addr, int *addrlen);

C source:

int addrlen;
struct sockaddr_in address;

  addrlen = sizeof(struct sockaddr_in);
  new_socket = accept(socket_desc, (struct sockaddr *)&address, &addrlen);
  if (new_socket<0)
    perror("Accept connection");

accept() se koristi sa soketima baziranim na konekcijama kao što su streams. Parametri su soket-deskriptor od master soketa praćen sockaddr_in structure i veličinom structure. Ako je uspešno, accept() vraća pozitivan ceo broj koji je soket-deskriptor za prihvaćeni soket. Ukoliko dođe do greške, -1 se vraća i errno se postavlja da označi uzrok.

Postoje neki primeri izvornog koda u direktorijumu, program koji kompajlira se zove 'accept'. Dok se pokreće, kucajte:

telnet localhost 7000

i trebalo bi da dobijete

pokušava...
Priključen na localhost.
Escape znak '^]'.

a onda posle 10 sekundi, trebalo bi da prekine vezu. Još jednom, ako se veza odmah odbije, verovatno postoji problem sa serverom.

Povratak na sadržaj


Završne veze

Verovatno jedna od najlakših stvari koje treba uraditi sa soketom. Ovo se radi korišćenjem close()

Includes:

#include <unistd.h>

Syntax:

int close(int sockdes);

C source:

  close(new_socket);

close() zatvara soket-deskriptor označen od strane sockdes. Nakon
uspešnog završetka, close() vraća vrednost 0; u suprotnom, vraća -1 i postavlja errno da ukaže na grešku.

Povratak na sadržaj


Slanje podataka konekciji

Prihvatanje veze ne bi imalo nikakvog smisla bez mogućnosti za slanje ili prijem podataka. Slanje bez prijema može da se koristi za informacioni server koji uvek vraća fiksnu poruku.

Sadrži:

#include <sys/socket.h>

Syntax:

int send(int s, const void *msg, int len, int flags);

C source:

char *message="This is a message to send\n\r";

  send(new_socket,message,strlen(message),0);

Poruka treba da ima \n\r umesto samo \n ili \r jer inače tekst koji se pojavljuje na nekim klijentima može izgledati čudno. npr. tekst sa samo \n će se pojaviti na sledeći način:

Ovo je poruka
                 ovo je drugi red
                                            i ovo je treći.

umesto:

Ovo je poruka
a ovo je drugi red
i treći.

send() se koristi da se prenese poruka na drugi soket i može se koristiti samo kada je soket u konektovanom stanju. Soket-deskriptor koji određuje na koji soket će biti poslata poruka je 's' u sintaksi iznad. 'msg' ukazuje na bafer koji sadrži poruku, a dužina poruke je data od strane len, u bajtovima.
Podržane vrednosti zastave su nula, ili
MSG_OOB (za slanje out-of-band podataka) - write() poziv poslat na soket ponaša se na potpuno isti način kao i send(), sa zastavama postavljenim na nulu.
Nakon uspešnog završetka, send() vraća broj poslatih bajtova. U suprotnom, vraća -1 i postavlja errno da ukaže na lokalno-otkrivene greške. `Accept' program je modifikovan da pošalje pozdravnu poruku konekciji pre nego što zatvori soket. Da biste videli kako se to radi, pogledajte izvorni kod za `send' program.

Povratak na sadržaj


Prijem podataka iz konekcije

Prihvatanje konekcije ne bi imalo nikakvog smisla bez mogućnosti slanja ili prijema podataka. Samo prijem može jedino da se koristi kao metod prikupljanja podataka.

Includes:

#include <sys/socket.h>

Syntax:

int recv(int s, void *msg, int len, int flags);

C source:

int bufsize=1024;        /* a 1K buffer */
char *buffer=malloc(bufsize);

  recv(new_socket,buffer,bufsize,0);

Parametar zastava se može podesiti na MSG_PEEK, MSG_OOB, oba ili na nula. Ako je postavljen na MSG_PEEK, svi podaci koji se vrate do korisnika i dalje se tretiraju kao da nisu bili pročitani, odnosno sledeći recv() ponovo čita iste podatke.
Read() poziv poslat na soket ponaša se na potpuno isti način kao recv(), sa zastavama postavljenim na nula.
Ako je uspešno, recv() vraća broj primljenih bajtova, u suprotnom, vraća -1 i postavlja errno da ukaže na grešku. recv() vraća 0 ako soket blokira i ako veza sa udaljenim node nije uspela.

Povratak na sadržaj


Postavljanje soket opcija

Omogućavanje određenih soket operacija zahteva manipulaciju soket opcijama korišćenjem setsockopt()

Includes:

#include <sys/socket.h>

Syntax:

  int setsockopt(int s, int level, int optname,
                 const void *optval, int optlen);

C source:

#define TRUE   1
#define FALSE  0

int socket_desc;     /* master socket returned by socket() */
int opt=TRUE;        /* option is to be on/TRUE or off/FALSE */

  setsockopt(socket_desc,SOL_SOCKET,SO_REUSEADDR,
              (char *)&opt,sizeof(opt));

SOL_SOCKET precizira da je opcija `socket level' opcija , ovo je definisano u <sys/socket.h>
Soket je prepoznat od strane socket descriptor s.
Opcija SO_REUSEADDR je validna samo za AF_INET sokete.
Postoje dve vrste opcija: boolean i ne-boolean. Boolean opcije su ili postavljene ili nisu postavljene i takođe mogu da koriste optval i optlen da proslede informacije. Ne-boolean opcije uvek koristite optval i optlen da proslede informacije.

Povratak na sadržaj


Rukovanje sa više od jedne veze

Da biste omogućili da se soket čita bez čekanja ako nema inputa, soket mora biti postavljen bez blokiranja pomoću sledećeg fragmenta koda.

fcntl(mastersocket, F_SETFL, FNDELAY);

ili

fcntl(mastersocket, F_SETFL, O_NONBLOCK);

Ako ovo od gore daje ne-nula rezultat, operacija nije uspela i errno treba postaviti na odgovarajuće vrednosti.

Korišćenje select da pratite više soketa (ili samo jedan) je prilično jednostavno i prikazano je u kodu ispod. Imajte na umu da je ovo nepotpun kod kao i da kreiranje master soketa nije uključeno (vidi prethodne detalje).

fd_set readfds;

/* napravite listu soketa za proveru aktivnosti */
FD_ZERO(&readfds);

/* odredite mastersocket - tj. tražite nove veze */
FD_SET(mastersocket, &readfds);

/* čekajte vezu, zauvek ako treba */
new_conns=select(max_conns, readfds, NULL, NULL, NULL);

if ((new_conns<0) && (errno!=EINTR)) {
  /* došlo je do greške sa select() */
}
if (FD_ISSET(mastersocket,&readfds)) {
  /* Otvori novi soket */
}

Naravno, ovo gore će sačekati samo aktivnosti na master soketu. Ono što treba da uradite je da ga pokrenete unutar petlje koja ponavlja sve dok se server ne ugasi.
Svi novonastali soketi takođe treba da se prate (osim ako su prihvaćene veze zatvorene nakon slanja poruke).

Kao primer toga, pogledajte uzorak program multi.c koji prihvata do tri veze i šalje podatke iz jednog soketa u druge. To je skoro pa najosnovniji chat server.

Povratak na sadržaj


Konvertovanje hostname u mrežnu adresu

Includes:

#include <netdb.h>

Syntax:

  struct hostent *gethostbyname(const char *name);

C source:

  struct hostent *hent;

  hent = gethostbyname("www.foobar.net");

Hostent struktura:

struct hostent {
    char    *h_name;        /* official name of host */
    char    **h_aliases;    /* alias list */
    int     h_addrtype;     /* host address type */
    int     h_length;       /* length of address */
    char    **h_addr_list;  /* list of addresses */
}

Neke od mrežnih funkcija zahtevaju strukturu koja sadrži mrežnu adresu, a ponekad i broj porta za povezivanje na ili sa njega. Najlakši način da konvertujete hostname u mrežnu adresu je pomoću gethostbyname() funkcije.

Gethostbyname() vraća strukturu hostent tipa - ova struktura sadrži ime hosta, niz alternativnih imena, a takođe i čitav niz mrežnih adresa (po mrežnom uređenju bajtova).

Povratak na sadržaj


Uspostavljanje odlazne veze

Da biste uspostavili vezu sa drugim soketom (slično telnet-u), koristite funkciju connect().

#include <sys/types.h>
#include <sys/socket.h>

int  connect(int  sockfd, struct sockaddr *serv_addr, int addrlen );

Napravite soket pomoću socket(), konvertujte ime hosta u IP adresu pomoću gethostbyname() i nakon toga pokrenite connect() call passing odgovarajućih struktura koje sadrže IP adresu i port za povezivanje sa njim.

  struct hostent     *he;
  struct sockaddr_in  server;
  int                 sockfd;

/* resolve localhost to an IP (should be 127.0.0.1) */
  if ((he = gethostbyname("localhost")) == NULL) {
    puts("error resolving hostname..");
    exit(1);
  }

/*
 * copy the network address part of the structure to the 
 * sockaddr_in structure which is passed to connect() 
 */
  memcpy(&server.sin_addr, he->h_addr_list[0], he->h_length);
  server.sin_family = AF_INET;
  server.sin_port = htons(7000);

/* connect */
  if (connect(sockfd, (struct sockaddr *)&server, sizeof(server))) {
    puts("error connecting..");
    exit(1);
  }

Korišćenje connect() ... biće toga više kada/ako se pozabavim sa njim.

Povratak na sadržaj

Published (Last edited): 03-05-2013 , source: http://shoe.bocks.com/net/