Am avut 367379 vizite de la lansarea siteului.




           
                                   Referinte socluri BSD
                                   =====================
            -Apeluri si comunicatia intre procese sub sistemul de operare-     
                                      -Berkeley Unix-
 
            Scopul acestui articol este acela de a crea o privire de ansamblu
 asupra comunicatiilor interprocese sub Berkeley Unix. Va fi acordata o aten-
 tie speciala acelor apeluri de sistem care sunt in legatura cu crearea,
 gestiunea si utilizarea soclurilor. De asemenea va fi discutata problema
 semnalelor si a apelurilor din alte sisteme de operare, ceea ce va folosi
 celor ce lucreaza in proiectarea retelelor. Mai multe informatii despre
 toate apelurile de sistem mentionate mai jos pot fi gasite in Manualul
 Programatorului Unix.
 
            1. Crearea soclurilor
               ------------------               
            Cel mai general mecanism de comunicatie interproces oferit de Berkeley
 Unix este soclul ('socket'- engl.). Un soclu este elemtul primar pentru
 comunicatie. Doua procese pot comunica creand socluri si trimitand mesaje
 intre ele, prin intermediul acestora. Exista mai multe tipuri de socluri,
 diferite prin modul in care este definit spatiul de adresa si prin tipul de
 comunicatie ce se poate stabili intre socluri. Un soclu este definit in mod
 unic de o tripla <domeniu, tip, protocol>. Pentru a folosi un soclu la
 distanta, trebuie sa-i asignam un nume. Forma pe care acest nume o
 reprezinta este determinata de domeniul de comunicatii sau de familia de
 adrese de care apartine soclul. De asemenea exista un tip abstract sau stil
 de comunicatie asociat cu fiecare soclu. Acesta determina semantica comuni-
 catiei pentru soclul respectiv. In sfarsit, exista un protocol specific care
 este utilizat impreuna cu soclurile. Un soclu poate fi creat cu apelul 
 sistem "socket", specificand adresa de familie dorita (domeniul), tipul
 soclului si protocolul folosit de acesta :
 
                  int socket_descriptor,domain,type,protocol;             
                 
                  socket_descriptor=socket(domain,type,protocol);
 
 Acest apel returneaza un intreg scurt, pozitiv, numit descriptor de soclu,
 care poate fi utilizat ca parametru pentru a apela (adresa) soclul in apeluri
 sistem ulterioare. Descriptorii de soclu sunt similari descriptorilor de fi-
 siere returnati de apelul sistem "open". Fiecare apel "open" sau "socket"
 va returna cel mai mic intreg nefolosit. Astfel, un numar dat semnifica fie
 un fisier deschis, fie un soclu sau nimic (dar niciodata amandoi termenii).
 Descriptorii de soclu sau cei de fisier pot fi utilizati interschimbabil in
 multe apeluri sistem.
            De exemplu, apelul sistem "close" e utilizat pentru a distruge
 soclurile.
 
            1.1. Domenii 
                 -------
            Domeniul de comunicatie sau familia de adrese de care apartine un soclu
 specifica un anumit format de adrese. Toate operatiile cu un soclu creat vor
 interpreta adresa furnizata in concordanta cu acest format specificat.
 Diferitele formate de adrese sunt definite ca constante in fisierul header
 <sys/socket.h> (includeti neaparat <sys/types.h> inainte de <sys/socket.h>).
 Ca exemple avem AF_UNIX (nume de cale Unix), AF_INET (adrese Internet DARPA)
 si AF_OSI (asa cum sunt specificate de standardele internationale OSI).
 AF_UNIX si AF_INET sunt cele mai importante familii de adrese. Forma generala a
 unei adrese este reprezentata de structura "sockaddr" definita in
 <sys/socket.h> :
 
                  struct sockaddr {
                           short sa_family; /* familia de adrese */
                           char sa_data[14];/* pana la 14 octeti ai adresei directe */
                          }
 
            Cand se creaza un soclu, initial el nu are o adresa asociata. Oricum,
 deoarece un proces sau un "host" de la distanta nu poate gasi un soclu daca
 nu are o adresa, este important sa legam o adresa de soclul creat. Un soclu
 nu are un nume pana cand nu este legat explicit de o adresa prin apelul
 sistem "bind".
 
                  status=bind(sock,address,addrlen)
                  int status; /* status returneaza 0 pentru succes, -1 in caz
                                                   contrar */
                  int sock; /* descriptor returnat de apelul socket(,,) */
                  struct sockaddr *address;
                          int addrlen; /* dimensiunea adresei in octeti */
 
            Acest apel esueaza daca adresa este deja folosita, adresa nu este in
 formatul corect pentru familia de adrese specificata, sau soclul are deja
 asignata o adresa.
 
            1.1.1. Domeniul UNIX
                   -------------
            In domeniul UNIX, un soclu e adresat de un nume de cale UNIX, care
 poate avea o lungime de pana la 108 caractere. Legarea unui nume de cale de
 un soclu rezida in alocarea unui INODE si a unei intrari a unui nume de cale
 in fisierul sistem. Aceasta necesita scoaterea numelui de cale din structura
 de fisiere (utilizand apelul sistem "unlink") cand soclul este distrus.
 Fisierul creat este utilizat numai pentru a furniza un nume soclului si nu
 joaca nici un rol in transferurile de date. Cand utilizam socluri in domeniul
 UNIX, este de preferat sa utilizam numai nume de cai pentru directoare
 (ca /tmp) legate direct de discul local. Domeniul UNIX permite comunicatia
 numai pentru procese ce ruleaza pe aceiasi masina. Structura "sockaddr_un"
 utilizata pentru a defini formatul de adresa UNIX poate fi gasita in
 <sys/un.h>.
 
                  struct sockaddr_un {
                           short sun_family; /* AF_UNIX */
                           char sun_path[108-4]; /* nume de cale */
                          }
 
 Nota : Cand specificati lungimea adresei de domeniu UNIX pentru apeluri sis-
 tem, utilizati "sizeof(struct sockaddr_un)". Utilizand lungimea unui
 "sockaddr" apelul va esua.
 
            1.1.2. Domeniul Internet
                   -----------------
            In domeniul DARPA Internet, adresa este alcatuita din doua parti - o
 adresa de "host" (care consista intr-un numar de retea si un numar de "host")
 si un numar de port (cunoscut sub numele de "transport suffix"). Aceasta
 adresa de "host" permite comunicatia proceselor de pe diferite masini.
 In schimb numarul de port functioneaza ca un "mail box" care permite adrese
 multiple pe acelasi "host". Structura care descrie o adresa in domeniul In-
 ternet este definita in fisierul <netinet/in.h>.
 
                  struct sockaddr_in {
                           short sin_family; /* AF_INET */
                           u_short sin_port; /* numarul de port */
                           struct in_addr sin_addr; /* evzi mai jos */
                           char sin_zero[8]; /* neutilizat */
                          }
                  struct in_addr {
                           union {
                               struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
                               struct { u_short s_w1,s_w2; } S_un_w;
                               u_long S_addr;
                              } S_un;
                           #define s_addr S_un.S_addr /* poate fi folosit in majorita-
                                                                        tea codurilor tcp/ip */
                          }
 
 De multe ori este folositor de a se lega un serviciu specific la un port
 mai des folosit, permitand proceselor la distanta sa localizeze usor serverul
 folosit. Exemple de porturi des folosite sunt portul 79 pentru serviciul
 "finger" si portul 513 pentru login-ul de la distanta. Nucleul rezerva
 primele 1024 numere de port pentru folosinta proprie.  In directorul
 /etc/services exista o baza de date de servicii ale retelei care poate fi in-
 terogata utilizand apelurile sistem "getservbyname" si "getservbyport" (a se
 vedea "getservent(3*n)" si "services(5)"). Fiecare din aceste apeluri retur-
 neaza un pointer la structura "servent" definita in <netdb.h>. Daca campul
 port din parametrii adresei este specificat ca fiind zero, sistemul va
 asigna un numar de port nefolosit. Numarul de port asignat poate fi gasit cu
 apelul sistem "getsockname". In domeniul Internet, protocoalele UDP si TCP
 pot utiliza acelasi numar de port. Nu va aparea nici un conflict de nume de-
 oarece porturile legate de socluri cu protocoale diferite nu pot comunica.
 Porturile cu un numar mai mic de 1024 sunt rezervate - numai procesele ce ru-
 leaza ca "superuser" se pot lega la ele.
 Adresa de "host" Internet este specificata prin 4 octeti. Ei sunt reprezen-
 tati tipizat printr-o notatie cu '.', a.b.c.d. Octetii de adresa sunt re-
 prezentati prin numere intregi, separate prin puncte, de la rangul mai
 mare la cel mai mic. Aceasta ordonare se numeste "network order" si reprezinta
 ordinea in care adresele sunt transmise in retea. De exemplu, adresa Internet
 pentru "host"-ul garfield.cs.wisc.edu este 128.105.1.3 care corespunde intre-
 gului fara semn 80690103 in hexa sau 2154365187 in zecimal. Cu toate astea,
 unele "host"-uri (cum ar fi VAX-urile) au ordinea octetilor inversata, trimi-
 tand mai intai octetii mai putin semnificativi. Cand un cuvant este transmis
 de al un astfel de "host" (ori cand un program C trateaza un cuvant ca o sec-
 venta de octeti) octetul cel mai putin semnificativ este transmis primul
 (are adresa cea mai mica). Astfel este necesar sa se inverseze octetii de
 adresa memorati intr-un intreg, inainte de a-i transmite. Rutinele sistem
 "htonl" si "ntohl" sunt folosite la conversia intregilor "long" (32 de biti)
 de la un "host" pentru o retea si viceversa. Similar, "htos" si "ntohs"
 inverseaza octetii intregilor scurti (16 biti) in cazul numerelor de porturi.
 Apelurile sistem care returneaza sau cer adrese Internet si numere de port,
 (cum ar fi "gethostbyname" sau "bind", care a fost descris mai inainte),
 lucreaza numai in format "network order", deci in mod normal nu trebuie sa
 va faceti probleme despre acestea. Dar, daca va trebui sa vizualizati aceste
 valori sau sa le folositi intr-un program, va trebui sa le convertiti de la /
 la ordinea de "host".
 O adresa de "host" Internet se compune in doua parti - un numar de retea si o
 adresa locala. Exista trei formate de adrese Internet (vezi inet(3*n)), si
 fiecare interpreteaza diferit cei 32 de biti de adresa. Adresa de 'Clasa A'
 are numarul de retea compus dintr-un singur octet (cel mai semnificativ) si o
 adresa de "host" de 3 octeti. Adresele de 'Clasa B' au cate 2 octeti atat
 pentru numarul de retea cat si pentru numarul de "host", iar adresele de
'Clasa C' au 3 octeti pentru numarul de retea si unul pentru numarul de "host"
 (astfel o retea de 'Clasa C' poate avea maxim 256 de "host"-uri. Bitii cei mai
 semnificativi dintr-o adresa ii determina clasa: adresele incepand cu bitul "0"
 sunt adrese de clasa A; "network address" ocupa primul octet al adresei, cei-
 lalti 3 servind la identificarea"host"-ului. Daca primii doi biti ai unei adre-
 se sunt "10" atunci este vorba de o adresa de clasa B; pentru acest tip,
 "network address" ocupa primii doi octeti ai adresei ramanand doi octeti pentru
 identificarea "host"-ului.Cand primii trei biti sunt "110" adresa respectiva
 apartine clasei C. "Network address" ocupa acum primii 3 octeti ai adresei,
 iar "host address" va folosi doar ultimul octet. Sub standardul IPv4 mai exista
 si clasa D, avand drept element de identificare primii 3 biti ai primului octet
 care trebuie sa fie "111". Aceasta este o adresa de tip "multicast" si nu se mai
 supune regulii "network address"/"host address". Drept exemplu vom considera
 adresele utilizabile in reteaua U.T. "Gh. Asachi" Iasi:
   -adresa destinatie pentru serverul SIGMA : 193.226.26.110 apartine clasei C;
   -adresa destinatie pentru serverul EUREKA: 193.226.26.106 apartine clasei C.
 Astfel pot exista 128 de retele de clasa A si 2^14=16348 retele de clasa B.
 Apelurile sistem "gethostbyaddr" si "gethostbyname" pot fi folosite pentru
 a vizualiza adresa de "host" (vezi gethostent(3*n) si host(5)). Fiecare din
 aceste apeluri returneaza un pointer la o structura "hostent" definita in
 <netdb.h>. Adresele de "host" sunt returnate in format "network order",
 format valabil pentru un apel "bind". Pentru a copia adresa de "host" in
 structura sockaddr_in trebuie utilizata rutina sistem "memcpy". Daca in apelul
 "bind" se foloseste ca parametru o adresa de "host" 0 (se foloseste variabila
 INADDR_ANY), va fi furnizata automat adresa de "host" locala. Un alt
 motiv pentru a utiliza variabila INADDR_ANY este acela ca un "host"  poate
 avea mai multe adrese Internet ; adresele asignate in acest mod vor recepta
 orice mesaj cu o adresa Internet valida.
 
            1.2. Stiluri de comunicatie
                 ----------------------
            Campul de tip al apelului "socket" specifica stilul de comunicatie
 care va fi folosit in comunicatiile pe soclu. Aceste tipuri sunt definite
 ca constante in <sys/socket.h>. Tipurile urmatoare sunt in mod frecvent folo-
 site: SOCK_STREAM (stream), SOCK_DGRAM (datagram), SOCK_RAW (interfata proto-
 col "raw"), SOCK_RDM (reliable datagram) si SOCK_SEQPACKET (stream cu pachete
 sectionate). Fiecare dintre aceste tipuri este suportat de cate un protocol
 diferit. Pentru inceput ne vom concentra asupra constantelor SOCK_STREAM si
 SOCK_DGRAM.
 
            1.2.1. Soclurile DATAGRAM
                   ------------------
            Tipul SOCK_DGRAM furnizeaza un model de comunicatie de tip datagram.
 Un serviciu datagram se realizeaza fara conectare si este nesigur
 (unreliable-engl.). Mesaje independente (si de obicei scurte), numite
 datagrame, sunt acceptate de protocolul de transport si trimise la adresa
 specificata. Aceste mesaje se pot pierde, copia, sau pot fi receptionate
 in alta ordine,astfel ca nu exista garantia sigurantei comunicatiei.
            O caracteristica importanta a datagramelor este aceea ca limitele mesa-
 jelor sunt mentinute la receptie. Aceasta inseamna ca datagrame individuale
 (mesaje trimise cu apeluri separate) vor fi memorate separat cind vor fi
 receptionate. Un apel "revcfrom" pentru un soclu datagram va returna numai
 urmatorul datagram disponibil. Tipul SOCK_DGRAM poate fi utilizat in domeniul
 UNIX sau Internet. Cind este folosit in domeniul Internet el este suportat de
 protocolul de transport Internet numit UDP. Apelul :
 
                   sock = socket(AF_INET, SOCK_DGRAM, 0)
 
 va returna un soclu datagram UDP.
 
            1.2.2. Soclurile STREAM
                   ----------------
            Tipul STREAM_SOCK asigura un model de comunicatii de tip stream. Acest
 serviciu este sigur si este orientat pe conexiune. Data este transmisa intr-
 -un stream cu octet de control al fluxului si in duplex total (pe conexiuni
 separate). Protocolul de transport suportat de acest tip de soclu asigura
 faptul ca datele sunt transmise in ordine fara pierderi, erori sau duplicate.
 Altfel, el abandoneaza conexiunea si raporteaza eroarea utilizatorului.
 Limitele mesajelor nu sunt pastrate de soclul stream. Inainte ca data sa fie
 transmisa sau receptionata pe un astfel de soclu soclul trebuie sa fie trecut
 in modul de conexiune activa sau pasiva a soclului utilizind apelurile sistem
 "listen", "accept",sau "connect" discutate in sectiunea 2. Abstractizarea
 SOCK_STREAM poate fi utilizata atit in domeniul UNIX cit si in domeniul
 Internet. In domeniul Internet acesta este suportat de protocolul de transport
 TCP.Apelul
 
                 sock = socket(AF_INET, SOCK_STREAM, 0)
 
 returneaza un soclu stream TCP.
 
            1.3. Protocoale
                 ----------
            Un protocol este un set de conventii de comunicatie care stabileste
 reguli pentru schimbul de informatii intre doua parti. Protocoalele de
 transport de date care suporta stilurile de comunicatii descrise mai sus sunt
 implementate in nucleul UNIX. Aceste protocoale realizeaza semanticile definite
 de tipul soclului. "User Datagram Protocol" (UDP),"Transmition Control
 Protocol" (TCP) si "Internet Protocol" (IP) apartin toate familiei de proto-
 coale Internet. Fiecare membru al acestei familii de protocoale suporta un tip
 diferit (vezi TCP (4*p), UDP (4*p) si IP (4*p)). UDP suporta socluri datagram,
 TCP suporta socluri stream, iar tipul SOCK_RAW asigura o interfata "raw" cu IP.
 TP,protocolul de transport bazat pe conexiuni ISO suporta abstractizarea
 SOCK_SEQPACKET, desi nu este implementat in Berkeley UNIX.
            La ora actuala doar un singur protocol este suportat de un tip de soclu
 intr-un domeniu dat. Protocolul ce va fi utilizat este specificat in cimpul
 de protocol al apelului "socket". Sunt trei cai de specificare al unui pro-
 tocol : prima, daca este furnizat un 0, sistemul furnizeaza protocolul im-
 plicit pentru acel domeniu si tip de soclu ; aceasta este cea mai folosita
 metoda. A doua metoda,este cea folosind constante predefinite ca IPPROTO_UDP,
 care pot fi gasite in < netinet/in.h>. A treia metoda necesita consultarea
 bazei de date de protocoale din dirctorul /etc/protocols prin apelurile
 "getprotobyname" si "getprotobynumber" (vezi getprotoent(3*n) si protocols(5)).
 
            2. Stabilirea conexiunilor
               -----------------------
            Soclurile stream trebuie sa stabileasca o conexiune inainte de transferul
 datelor. Un soclu stream poate fi in doua stari : activa sau pasiva. Un so-
 clu este initial activ si devine pasiv in momentul unui apel "listen". Numai
 soclurile pasive pot fi mentionate intr-un apel "connect" si numai soclurile
 pasive pot fi mentionate intr-un apel "accept". Aceste apeluri de stabiliri
 de conexiuni sunt folosite numai pentru socluri stream, dar si soclurile da-
 tagram pot fi folosite in apeluri "conect" pentru a stabili in mod permanent
 destinatia pentru viitoarele apeluri "send".
 
            2.1. Conexiunea activa
                 -----------------
            Un soclu activ stabileste o conexiune cu un soclu pasiv utilizind apelul
 sistem "connect".
 
                        status = connect(sock, name, namelen )
                        int sock;
                        struct sockaddr* name;
                        int namelen;
 
            Parametrul "name" este adresa pentru un soclu la distanta, interpretata
 in concordanta cu domeniul de comunicatie cu care a fost creat soclul. Daca
 domeniul este AF_UNIX, acesta trebuie sa fie structura sockaddr_un; daca
 domeniul este AF_INET, el trebuie sa fie o structura sockaddr_in.
 
            2.2. Conexiunea pasiva
                 -----------------
            Un soclu devine un capat pasiv al unei conexiuni, dupa un apel "listen"
 
                        status = listen(sock, queuelen) int sock, queuelen ;
 
 "Listen" initializeaza o coada pentru asteptarea cererilor de conexiune.
 Parametrul queuelen specifica maxim permis de conexiuni ca pot fi introduse
 in coada. Lungimea maxima a cozii este limitata implicit de sistem la valoarea
 5. Constanta SOMAXCONN din <sys/socket.h> defineste acest maxim. O conexiu-
 ne poate fi stabilita utilizind apelul sistem "accept".
 
                        new_socket = accept(old_sock, namelen)
                        int new)socket, old)sock; /* numele soclului pereche in
                                                              noua conexiune */
 
                        int *namelen ; /* lungimea numelui in octeti */
 
 "Accept" scoate prima cerere de conexiune primita din coada si returneaza un
 descriptor pentru noul soclu cu care se conecteaza. Acest soclu are ace-
 leasi proprietati ca si vechiul soclu. Adresa soclului de la capatul activ
 este returnata in parametrul "name". "Namelen" este valoarea parametrului
 care trebuie initializat cu lungimea adresei structurii care este pasata;
 la retur va fi setat cu lungimea actuala a adresei returnate. Vechiul soclu
 ramane neafectat si poate fi utilizat pentru alte conexiuni. Daca nu avem
 nimic in coada, apelul "accept" se blocheaza.Daca e necesar, poate fi exe-
 cutat un apel "select" pentru a vedea daca exista vreo cerere de conectare
 (vezi sectiunea 4.2.). Un soclu cu conexiunile facute va trece pe starea
 gata de citire.
 
            3. Transferul datelor
               ------------------
            Perechile de apeluri (read, write), (recv, send), (recvfrom, sendto)
 (recvmsg, sendmsg) si (readv, writev) pot fi utilizate pentru transferurile
 de date pe soclu.Apelurile cele mai adecvate depind de functionalitatea ceru-
 ta. "Send' si "secv" sunt utilizate de obicei cu socluri stream. Pot fi uti-
 lizate si cu socluri datagram daca transmitatorul a facut inainte un apel
 "connect" sau daca receptorul nu este interesat sa stie cine este transmitato-
 rul. "Sendto" si "recvfrom" sunt utilizate cu socluri datagram. "Sendto"
 permite specificarea destinatiei datagramului, in timp ce "recvfrom" retur-
 neaza numele soclului departat care transmite mesajul. "Read" si "write"
 pot fi utilizate cu orice tip de socluri. Aceste apeluri pot fi alese din
 motive de eficienta. "Writev" si "readv" fac posibila depunerea
 si luarea datelor in/din buffere separate. "Sendmsg" si "recvmsg" permit de-
 punerea/luarea datelor ca si posibilitatea de a schimba drepturi de acces.
 Apelurile "read", "write", "readv" si "writev" pot primi ca prim argument
 fie un descriptor de soclu, fie unul din fisier ; toate celelalte apeluri
 necesita un descriptor de soclu.
                         
                        count = send(sock, buf, buflen, flags)
                        int count, sock, buflen, flags;
                        char *buf;
 
                        count = recv(sock, buf, buflen, flags)
                        int count, sock, buflen, flags;
                        char *buf;
 
                        count = sendto(sock, buf, buflen, flags, to, tolen)
                        int count, sock, buflen, flags, tolen;
                        char *buf;
                        struct sockaddr *to;
 
                        count=recvfrom(sock, buf, buflen, flags, from, fromlen)
                        int count, sock, buflen flags, *fromlen;
                        char *buf;
                        struct sockaddr *from;
 
            Pentru apelurile de transmisie, "count" returneaza numarul de octeti
 acceptati de suportul de transport sau -1 daca a fost detectata local vreo
 eroare. O valoare pozitiva returnata nu reprezinta o indicatie a succesului
 transferului de date. Chiar daca apelul "send' nu s-a blocat, el poate sa
 nu fi acceptat toti octetii din bufferul de date (vezi sectiunea 4.1.).
 Valoarea returnata trebuie sa fie verificata astfel incit daca au ramas oc-
 teti netransmisi, ei sa fie transmisi in final, daca este necesar. Pentru
 apeluri de receptie, "count" returneaza numarul de octeti receptionat sau -1
 daca s-a detectat o eroare. Primul parametru pentru fiecare apel este un
 descriptor de soclu valid. Parametrul buf este un pointer al bufferului de
 date al apelantului. In apelurile "send", parametrul buflen reprezinta numa-
 rul de octeti ce sunt transmisi ; in apelurile "receive', el indica dimensi-
 unea bufferului de receptie si numarul maxim de octeti pe care apelantul poa-
 te sa-i receptioneze. Parametrul "to" in apelul "sendto" specifica adresa
 destinatarului (in conformitate cu familia de adrese de care apartine), iar
 "tolen" specifica lungimea ei. Parametrul "from" in apelul "recvfrom" speci-
 fica adresa sursei mesajului. "Fromlen" este parametrul valoare/rezultat ca-
 re da initial dimensiunea structurii pointate de "from" si apoi este modificat
 pentru a returna lungimea actuala a adresei.
            Parametrul "flags", care este de obicei 0 ca argument, permite operatii
 peciale pe soclurile stream.Este posibila transmiterea de date "out-fo-band"
 sau culegerea mesajului receptionat fara a-l citi propriu-zis. Fanioanele
 MSG_OOB si MSG_PEEK sunt definite in <sys/socket.h>. Datele "out-of-band"
 sunt date cu prioritate mare (cum ar fi un caracter de intrerupere) pe care
 utilizatorul ar dori sa le proceseze inaintea tuturor datelor din stream.
 Daca sunt prezente date "out-of-band" poate fi trimis catre utilizator un
 semnal SIGURG. Semantica curenta pentru date "out-of-band" este determinata
 de protocolul folosit. Protocoalele ISO le trateaza ca date expeditive, pe
 cind portocoalele Internet le trateaza ca date urgente.
            Daca oricare dintre aceste apeluri (ca si oricare altul) este intrerupt
 de un semnal, ca SIGALRM sau SIGIO, apelul va returna -1 si variabila errno va
 fi setata la EINTR (variabilele definite in <errno.h>. Apelul sistem va fi
 repornit automat, insa inainte trebuie resetata variabila errno la 0.
 
            4. Sincronizare
               ------------
            In mod implicit, toate apelurile de citire/scriere se blocheaza : apelu-
 rile de citire nu se termina pina cind macar un octet de date este disponibil
 pentru citire, iar apelurile de scriere se blocheaza pina cind exista un
 spatiu suficient in buffer pentru a accepta citiva sau toti octetii care au
 fost transmisi. Unele aplicatii trebuie sa serviseze mai multe conexiuni in
 retea in acelasi timp, executind operatii pe conexiune cind li s-a permis.
            Sunt trei tehnici care pot suporta astfel de aplicatii : socluri fara 
 blocare semnalizari asincrone si apelul sistem select. Apelul sistem "select"
 este de departe cel mai des folosit, soclurile fara blocare nu sunt de obi-
 cei folosite, iar semnalizarile asincrone sunt rar folosite.
 
            4.1. Optiuni de soclu
                 ----------------
            Apelurile sistem "getsockopt" si "setsockopt" pot fi utilizate pentru a
 inspecta/seta optiuni speciale asociate soclurilor. Acestea pot fi optiuni
 generale pentru toate soclurile sau pentru implementari specifice de socluri.
 Exemple de optiuni luate din <sys/sockets.h> sunt SO_DEBUG si SO_USEADDR
 (permite reutilizarea adreselor locale).
            "Fnctl" si "ioctl" sunt apeluri sistem care fac posibil controlul
 fisierelor soclurilor si perifericelor intr-o mare varietate de moduri.
 
                        status = fnctl(sock, command, argument)
                        int status, sock, command, argument;
                        status = ioctl(sock, request, buffer)
                        int status, sock, request;
                        char *buffer;
           
            Parametrii "command" si "argument" ai apelului fnctl pot fi luati ca si
 constante din <fnctl.h>. Constantele predefinite pentru parametrul "request"
 din apelul "ioctl" se gasesc in <sys/ioctl.h>. Acest parametru specifica cum
 va fi utilizat argumentul "buffer".
            Fiecare din aceste apeluri poate fi utilizat pentru a activa pe soclu
 semnalizarile asincrone. Oricind soseste o data intr-un astfel de soclu va fi
 livrat procesului un semnal SIGIO. Procesul trebuie sa aiba deja un handler
 pentru acest semnal (vezi sectiunea 6.1).Acest handler poate apoi sa citeas-
 ca datele din soclu.Executia programului va continua de la punctul de intre-
 rupere. Secventa de apel :
 
                        fnctl(sock, F_SETOWN,getpid());
                        fnctl(sock, F_SETFL, FASYNC);
 
 activeaza intrarile/iesirile asincrone pe soclul dat. primul apel este nece-
 sar pentru a specifica procesul de semnalizat. Al doilea semnalizeaza des-
 criptorului de stare al fanionului sa deblocheze SIGIO.
            Aceiasi secventa de apel poate fi utilizata pentru a face un soclu sa nu
 se blocheze, singura modificare fiind ca se foloseste fanionul FNDELAY in
 locul fanionului FASYNC, in al doilea apel. In acest caz, daca o operatie
 de citire/scriere s-ar bloca in mod normal, este returnat -1 si variabila
 sistem externa errno este setata la EWOULDBLOCK. Acest cod de eroare poate
 fi verificat si se poate lua o masura necesara, functie de cerinte.
 
            4.2. Multiplexarea descriptorilor de fisiere
                 ---------------------------------------
      Apelul sistem "select" face posibila multiplexarea sincrona a descriptori-
 lor de fisier si de soclu. Poate fi utilizata pentru a determina cind sunt
 date de citit sau cind este posibil de a transmite date.
 
                         nfound=select(numdes, readmask, writemask, exceptmask, timeout)
                         int nfound, numdes;
                         fd_set *readmask, *writemask, *exceptmask;
                         struct timeval *timeout;
 
 Mastile din acest apel sunt parametri valoare/rezultat prin care sunt indi-
 cati fiecare descriptor de soclu sau fisier. Posibilitatea unei operatii
 specifice - citire, scriere, sau prezenta unei conditii de exceptie - este
 investigata prin setarea bitului corespunzator pentru acest soclu in masca co-
 respunzatoare. Poate fi utilizat un pointer nul daca conditia data nu este
 de interes. De exemplu, daca "writemask" e zero, soclurile nu vor fi verifi-
 cate pentru scriere.
 Tipul "fd_set"este definit in <sys/types.h> ca o structura ce contine un sin-
 gur camp, care este un sir de intregi. Sirul este interpretat ca o masca de
 biti, cu cate un bit pentru fiecare descriptor de fisier/soclu posibil. (Re-
 prezentarea mastii ca o structura si nu ca un simplu sir permite ca valorile
 "fd_set" sa fie asignate fara a necesita apelul "memcpy"). In <sys/types.h>
 sunt definite 4 macrouri pentru setarea, resetarea, si testarea bitilor in
 masca.
    
                        FD_SET(n,p) /* seteaza bitul n */
                        FD_CLEAR(n,p) /* reseteaza bitul n */
                        result=FD_ISSET(n,p) /* testeaza bitul n */
                        FD_ZERO(p) /* reseteaza toti bitii */
                        int n;
                        fd_set *p;
    
 Intr-o masca data pot fi setati mai multi biti, dar fiecare trebuie sa cores-
 punda unui descriptor valid. Parametrul "numdes" indica faptul ca trebuie exa-
 minati bitii de la zero la numdes-1. Constanta predefinita  FD_SETSIZE, defi-
 nita in <sys/types.h>, indica numarul maxim de descriptori ce pot fi reprezen-
 tati de o structura "fd_set". Astfel, setarea "numdes = FD_SETSIZE" ne va a-
 sigura ca toti descriptorii vor fi urmariti. Parametrul "timeout" este un
 pointer la o structura "timeval" definita in <sys/time.h>. Este utilizata pen-
 tru a specifica intervalul de timp maxim pe care il va astepta apelul inainte
 de a returna valoare. Daca timeout = 0 (pointer NULL) "select" se va bloca pe
 timp nedefinit. Daca timeout pointeaza spre o structura timeval iniitializata
 cu zero, atunci apelul este returnat imediat, chiar daca nu exista descripto-
 ri cata. "Select" se intoarce cind o conditie a fost descoperita pentru unul
 sau mai multe socluri sau cind timpul specificat s-a scurs. Valoarea de retur
 "nfound" indica numarul de conditii satisfacute. Mastile sunt modificate pen-
 tru a indica acele socluri pentru care conditia a fost indeplinita.
      Motivul pentru care descriptorii de fisier au fost inscrisi in masti ce
 merg cu apelul select este de a raspunde de o activitate interactiva. De exem-
 plu :
                        FD_SET(fileno(stdin),readmask)
 
 poate fi utilizat pentru a verifica daca a fost tastat ceva pe terminal.      
 
            5. Distrugerea conexiunilor  
               ------------------------
      O conexiune poate fi distrusa folosind apelul "close" pentru a inchide
 unul din soclurile implicate :
                       
                        status = close(sock)
                        int status, sock;
 
 Semantica precisa a inchiderii conexiunii este determinata de protocolul res-
 ponsabil. distrugerea se poate produce fara pierderi de date (sigur) sau cu
 pierderea datelor in tranzit.
      Apelul "shut_down" poate fi folosit sa inchida selectiv un soclu full-dup-
 lex(cu conexiuni separate.
                         
                        status = shut_down(sock, how)
                        int status, sock, how;
 
 Parametrul "how" specifica fie ca data nu va mai fi trimisa din soclu (0)
 sau ca data nu va mai fi receptionata (1), ori ca soclul va fi inchis com-
 plet (2).
      In cazul soclurilor create in domeniul UNIX trebuie folosit apelul
 "unlink" pentru a indeparta numele de calea la care a fost legat soclul, din
 structura de fisiere:
                       
                        status = unlink(pathname)
                        char* pathname;
 
 Aceste nume de cale nu sunt scoase automat cind soclul este inchis.
 
            6. Semnale     
               -------
      Berkeley UNIX asigura un set de semnale care pot fi livrate unui proces
 din diverse motive, cum ar fi o intrerupere de la tastatura sau aparitia unei
 erori pe bus. Aceste semnale - spre exemplu SIGIO si SIGALRM - sunt definite
 in <signal.h>. De obicei, actiunea ior implicita este aceea ca livrarea
 acestor semnale determina terminarea procesului in cauza. Aceasta actiune
 poate fi schimbata astfel incit semnalul sa fie captat sau ignorat. Daca
 semnalul este captat, un handler de semnal este declarat la locatia la care
 se transfera controlul la momentul intreruperii. Sosirea unui semnal va fi
 astfel similara unei intreruperi  hardware. Cind un semnal este livrat unui
 proces, starea semnalului este salvata, semnalul este blocat pentru viitoa-
 rele incercari de semnalizare, iar controlul programului este transferat
 handler-ului desemnat. Daca din handler se iese normal, semnalul se poate
 activa din nou, iar executia programului se reia din punctul in care fusese
 intrerupta. Daca semnalul e blocat totusi se activeaza, el este trecut in
 coada pentru o tratare ulterioara.
 
            6.1. Handler de semnal
                 -----------------
      Un handler de semnal poate fi declarat fie cu semnalul "signal", fie cu
 "sigvec". "Signal" este o versiune simplificata a apelului mai general
 "sigvec".
 
                        oldhandler=signal(sig,handler)
                        int sig;
                        int *handler(), *oldhandler();
 
      Parametrul "sig" este o constanta predefinita ce poate fi gasita in
 <sig.h> si descrie semnalul. Handler este numele rutinei care va fi apelata
 cand semnalul va fi transmis procesului. SIG_DFL (actiune implicita) si
 SIG_IGN (ignorare) pot fi de asemenea specificate ca argumente ale acestui
 parametru. Valoarea returnata va fi handler-ul anterior, daca exista.
 Rutina handler este de forma :
                          
                        sighandler (sig, code, scp)
                        int sig, code;
                        struct sigcontext *scp;
 
      Handler-ul pentru semnalul SEGIO este de preferat sa scoata toate datele
 ce pot fi in soclu inainte de iesirea din rutina. Aceasta inlatura posibili-
 tatea pierderii accidentale a datelor din cauza unui semnal pierdut. Daca se
 manifesta mai mult de un eveniment pentru un semnal, cind acesta este blocat,
 numai un semnal este salvat pentru a fi transmis procesului. 
 
            6.2. Blocarea semnalelor
                 -------------------
      Exista o masca globala care specifica ce semnal este blocat la un moment
 dat. Apelul sistem "sigblock" poate fi utilizat pentru blocarea semnalelor,
 in timp ce apelurile "sigsetmask" si "sigpause" pot fi utilizate pentru a de-
 bloca semnalele prin restaurarea mastilor originale. In <signal.h> exista un
 macro "sigmask" care face usoara setarea mastii de semnal pentru apelul
 "sigblock". El este definit ca :
                          
                        #define sigmask(m) (1<<((m)-1))
                        oldmask = sigblock(mask)
                        int oldmask, mask;
 
                        oldmask = sigsetblock(mask)
                        int oldmask, mask;
 
                        result = sigpause(mask)
                        int result, mask; /* apelul returneaza intotdeauna EINTR */
 
 Pentru "sigblock", parametrul "mask" specifica acele semnale ce trebuie blo-
 cate. "sigsetmask" si "sigpause" seteaza "mask" ca fiind masca de semnale cu-
 renta. Apelul "sigsetmask" se termina imediat, in timp ce "sigpause" asteap-
 ta sosirea unui semnal. Pentru sectiuni critice, unde este necesar a bloca
 un semnal ca SIGIO, poate fi folosita urmatoarea secventa :
 
                        newmask = sigmask(SIGIO);
                        oldmask = sigblock(newmask);
                   .........
                   .........
                        sigsetmask(oldmask);
 
            7. Timer-e
               -------
 
      Exista doua apeluri sistem care pot fi folosite pentru transmiterea unui
 semnal SIGALRM procesului apelant, acestea fiind "alarm" si "setitimer".
 "Alarm", care face ca un singur semnal SIGALRM sa fie transmis la un proces
 cind timpul specificat expira, nu asigura o rezultie mai mica de 1 secunda.
 "Setitimer', pe de alta parte, asigura intreruperi de clock periodice, (via
 SIGALRM), la intervale regulate si are o rezolutie de minimum 10 ms. Aceasta
 este ideala pentru actualizarea timer-elor de program.
 
                        status = setitimer(which, value, oldvalue)
                        int status, which;
                        struct itimeval,*value, *oldvalue;
 
 Este necesar sa apelam numai o data "setitimer" si ea va trimite semnale
 SIGALRM la intervale regulate. Trebuie, in acest caz, declarat un handler
 pentru a prelucra acest semnal, altfel acesta va termina procesul. Parametrul
 "which" specifica care este intervalul de timp intre apeluri. In <sys/time.h>
 sunt definite doua posibilitati : ITIMER_REAL si ITIMER_VIRTUAL. ITIMER_REAL
 va decrementa timer-ul un timp real, iar ITIMER_VIRTUAL va decrementa timer-
 -ul numai cind procesul este activ. Ceilalti doi parametrii sunt pointeri la
 o structura definita in <sys/time.h> :
 
                        struct itimerval {
                            struct timeval it_interval;/* interval timer */
                            struct timeval it_value;/* valoare curenta */
                            }
                        struct timeval {
                            long tr_sec;/* secunde */
                            long tr_usec;/* milisecunde */
                            }
   
     "Setitimer" seteaza intervalul pentru timer la valoarea specificata de
 "value" si returneaza valoarea anterioara in "oldvalue". Un semnal SIGALRM
 (SIGVALRM pentru ITIMER_VIRTUAL) va fi trimis procesului cind valoarea de timp
 specificata de de "value->it_itvalue" devine zero. Timer-ul va fi incarcat
 apoi cu valoarea specificata in "value->it_interval",dupa care ciclul se reia.
 Astfel, pentru a asigura o intrerupere de ceas periodica, este necesar numai
 apelul "setitimer", cu "value->it_interval" si  "value->it_value"incarcate cu
 valorile dorite. Daca "it_interval" este zero, timer-ul va activa intreruperea
 numai o data. O valoare zero in "it_value" va inactiva timer-ul. Exista 3
 macrouri in <sys/time.h> care sunt folositoare in manipulaera structurilor
 "timeval". Acestea sunt "timerisset", "timerclear" si "timercmp". 
    
            8. Programe exemplu
               ----------------
    Urmatoarele 2 programe exemplifica folosirea soclurilor stream sub TCP.
 Primul program, "client.c", stabileste conexiunea la un port si un "host"
 specificate ca argumente in linia de comanda, si apoi asteapta intr-o bucla,
 trimitand pe soclu tot ce vine de la tastatura si afisand toate caracterele ce
 vine pe conexiune. Bucla se termina cand la tastatura va fi apasat "Ctrl+D"
 (caracterul EOF). Al doilea program, "server.c", creaza un server primitiv,
 cu ecou.El asteapta o conexiune TCP la un port specificat in linia de comanda
 si citeste date de pe soclul creat. Datele sunt afisate si apoi sunt trimise
 inapoi conexiunii de unde au venit. Aceste programe utilizeaza apelul "select"
 cu o masca de citire :"client.c" il utilizeaza pentru a alege intre tastatura
 si conexiunea de retea, pe cand "server.c" il utilizeaza pentru a alege intre
 conexiunile existente si cererile pentru noi conexiuni. Amandoua programele
 blocheaza iesirile in mod neconditionat. Un client ce trimite un bloc mare de
 date la server si apoi nu reuseste sa citeasca de la server ecoul poate cauza
 agatarea server-ului. De fapt, deoarece un client poate fi blocat in trimiterea
 de date catre server, si astfel nu poate receptiona ecoul, blocarea totala este
 posibila. Aceste probleme pot fi rezolvate (cu costul maririi complexitatii
 programului),buffer-ind datele intern si utilizand masti de scriere in ape-
 lurile "select".
 
            8.1. CLIENT.C
                 --------
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#incluse <netinet/in.h>
#include <errnon.h>
#include <ctype.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
 
main(argc, argv)
int argc;
char *argv[];
{
            struct hostent *hostp;
            struct servent *servp;
            struct sockadrr_in server;
            int sock;
            static struct timeval timeout = { 5, 0 }; /* 5 secunde  */
            fd_set rmask, xmask, mask;
            char buf[BUFSIZ];
            int nfound, bytesread;
 
            if (argc != 3) {
                        (void) fprintf(stderr, "usage: %s service host\n",argc[0]);
                        exit(1);
            }
            if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
                        perror("socket");
                        exit(1);
            }
            if (isdigit(argv[1] [0])) {
            static struct servent s;
            servp = &s;
            s.s_port = htons((u_short)atoi(argv[1]));
            } else if ((servp = getservbyname(argv[1],"tcp")) == 0) {
                        fprintf(stderr,"%s:unknown service\n",argv[1]);
                        exit(1);
            }
            if ((hostp = gethostbyname(argv[2])) == 0) {
                        fprintf(stderr,"%s:unknown host\n",argv[2]);
                        exit(1);
            }
            memset((void *) &server, 0, sizeof server);
            server.sin_family = AF_INET;
            memcpy((void *) &server.sin_addr,hostp->h_addr,hostp->h_length);
            server.sin+port = servp->s_port;
            if (connect(sock, (struct sockkaddr *)&server,sizeof server) < 0) {
                        (void ) close (sock);
                        perror("connect");
                        exit(1);
            }
            FD_ZERO(&mask);
            FD_SET(sock, &mask);
            FD_SET(fileno(stdin), &mask);
            for(;;) {
               rmask=mask;
               nfound=select(FD_SETSIZE,&rmask,(fd_set *)0,(fd-set *)0,&timeout);
               if (nofound < 0) {
                   if (errno == EINTR) {
                            printf("interrupted system call\n");
                            continue;
                            }
                   /* Exista o problema! */
                   perror("select");
                   exit(1);
               }
               if (nfound == 0) {
                            /* timer ajuns la zero  */
                            printf("Please type something!\n");
                            continue;
               }
               if (FD_ISSET(fileno(stdin),&rmask)) {
                            /* data de la tastatura */
                            if (!fgets(buf, sizeof buf, stdin)) {
                                   if (ferror(stdin)) {
                                       perror("stdin");
                                       exit(1);
                                      }
                                   exiot(0);
                            }
                            if (write(sock, buf, strlen(buf)) < 0) {
                                   perror("write");
                                   exit(!);
                                   }
 
                            }
                if (FD_ISSET)(sock,&rmask)) {
                             /* data din retea */
                             bytesread = read(sock, buf, sizeof buf);
                             buf[bytesread]='\0';
                             printf("&s: got %d bytes:%s\n", argv[0], bytesread, buf);
                        }
                }
}/* main - client.c */
 
            8.2. SERVER.C
                 --------
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
 
main(argc, argv)
int argc;
char *argv[];
{
     struct servent *servp;
     struct sockaddr_in server, remote;
     int request_sock, new_sock;
     int nfound, fd, maxfd, bytesread, addrlen;
     fd_set rmask, mask;
     static struct timeval timeout = { 0, 500000 };/* 0.5 secunde */
     char buf[BUFSIZ];
 
     if (argc != 2) {
            (void ) fprintf(stderr,"usage: %s service\n",argv[0]);
            exit(1);
     }
     if ((request_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
            perror("socket");
            exit(1);
     }
     if (isdigit(argv[1] [0])) {
            static struct servent s;
            servp = &s;
            s.s_port = htons((u+short)atoi(argv[1]));
      } else if ((servp = getservbyname(argv[1],"tcp")) == 0) {
              fprintf(stderr,"%s: unknown service \n");
              exit(1);
      }
      memset((void *) &server, sizeof server);
      server.sin_family = AF_INET;
      server.sin_addr.s_addr = INADDR_ANY;
      server.sin_port = servp->s_port;
      if (bind(request_sock,(struct sockaddr *)&server, sizeof server) <0) {
            perror("bind");
            exit(1);
      }
      if (listen(request_sock, SOMAXCONN) < 0) {
            perror("listen");
            exit(1);
      }
      FD_ZERO(&mask);
      FD_SET(request_sock, &mask);
      maxfd = request_sock;
      for(;;) {
               rmask = mask;
               nfound = select(maxfd+1, &rmask, (fd_set *)0, (fd_set *)0, &timeout);
               if (nfound < 0) {
                        if (errno == EINTR) {
                            printf("interrupted system call\n");
                            continue;
                            }
                            /* Exista o problema! */
                            perror("select");
                            exit(1);
                }
                if (nfound == 0) {
                        /* timeout */
                        printf(".");fflush(stdout);
                        continue;
                }
                if (FD_ISSET(request_sock, &rmask)) {
                        /* o noua legatura este disponibila pe soclu */
                        addrlen = sizeof(remote);
                        new_sock = accept(request_sock,
                                   (struct sockaddr *)&remote, &addrlen);
                        if (new_sock < 0) {
                                   perror("accept");
                                   exit(1);
                        }
                        printf("connection from host %s,port &d, socket %d\n",
                                   inet_ntoa(remote.sin_addr), ntohs(remote.sin_port),
                                   new_sock);
                        FD_SET(new_sock, &mask);
                        if (new_sock > maxfd)
                                   maxfd = new_sock;
                                   FD_CLR(request_sock, &rmask);
                }
                for (fd=0; fd <=maxfd ;fd++) {
                        /* testarea soclurilor ce au date disponibile */
                        if (FD_ISSET(fd, &rmask)) {
                                   /* Proceseaza datele */
                                   bytesread = read(fd, buf, sizeof buf - 1);
 
                                   if (bytesread<0) {
                                               perror("read");
                                               /*citire esuata */
                                   }
                                   if (bytesread<=0) {
                                               printf("server: end of file on &d\n,fd);
                                               FD_CLR(fd, &mask);
                                               if (close(fd)) perror("close");
                                               continue;
                                   }
                                   buf[bytesread] = '\0';
                                   printf("%s :%d bytes from %d: %s\n"),
                                               argv[0], bytesread, fd, buf);
                                   /* datele sunt trimise inapoi */
                                   if (write (fd, buf, bytesread) != bytesread)
                                               perror("echo");
                        }
                }
               }
} /* main - server.c */
 
 
 
 
 
Home   |   Web Faq   |   Radio Online   |   About   |   Products   |   Webmaster Login

The quality software developer.™
© 2003-2004 ruben|labs corp. All Rights Reserved.
Timp de generare a paginii: 0.005 secunde
Versiune site: 1.8 SP3 (build 2305-rtm.88542-10.2004)