Documente Academic
Documente Profesional
Documente Cultură
Socluri
1. Socluri
1.1. Introducere . . . . . . . . . . . . . . . . . .
1.2. Concepte . . . . . . . . . . . . . . . . . . .
1.3. Apeluri sistem . . . . . . . . . . . . . . . .
1.4. Exemple simple . . . . . . . . . . . . . . . .
1.4.1. Exemplul 1: transfer de fisiere . . .
1.4.2. Exemplul 2: utilizarea datagramelor
1.4.3. Exemplul 3: server concurent . . . .
1.4.4. Exercitii . . . . . . . . . . . . . . . .
1.5. Aplicatii . . . . . . . . . . . . . . . . . . . .
1.5.1. Fire de executie . . . . . . . . . . . .
1.5.2. Distribuitor de mesaje . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
9
9
10
13
17
20
24
26
37
38
38
45
Partea I
Socluri
Capitolul 1
Introducere
Am v
azut n capitolul anterior ca nivelul transport din modelul OSI
pune la dispozitia nivelurilor superioare servicii capat la capat de comunicatie. Nivelurile superioare (peste nivelul aplicatie) sunt ndeobste implementate de c
atre aplicatii ce ruleaza deasupra sistemului de operare. Sistemul de operare este cel care asigura, de obicei, functionalitatea la nivelurile
inferioare. Prin urmare, este necesar sa existe o metoda de a folosi serviciile
nivelului transport de c
atre aplicatiile utilizatorilor. Exista cateva astfel de
interfete (API), dintre care probabil cea mai comuna este Berkeley sockets,
ap
arut
a mai nt
ai n sistemele BSD iar mai apoi raspandita pe alte variante
de UNIX si pe alte sisteme de operare.
Exist
a o sumedenie de carti, ghiduri de programare si alte resurse referitoare la socket programming (programarea cu socluri), de aceea ne propunem s
a d
am mai degrab
a o serie de exemple comentate, din care sa reiasa
modul de lucru cu socluri n diverse situatii, decat un material de referinta.
Pentru mai multe detalii despre Berkeley sockets, vezi bibliografia.
Intelegerea exemplelor date n acest capitol necesita cunoasterea limbajului C. De asemenea, este bine ca cititorul sa fie familiarizat cu programarea
n Linux/UNIX. In particular, programele urmatoare folosesc apeluri sistem
10
CAPITOLUL 1. SOCLURI
si functii de bibliotec
a pentru a manipula fisiere, pentru a crea procese si
pentru a trata semnale. In ceea ce priveste cunostintele despre retelele de
calculatoare, sunt suficiente not, iunile date n capitolul precedent. Insa cu
c
at mai bine nt, elegem comportamentul retelelor de calculatoare cu atat
mai usor ne este s
a scriem programe distribuite ce se vor comporta corect
n toate situatiile.
Ca si n restul c
artii, ne vom axa pe suita de protocoale TCP/IP, desi
soclurile pot fi folosite cu diverse alte arhitecturi de retea. In terminologia
Berkeley sockets, aceste arhitecturi se numesc domenii (domains) sau familii
de protocoale (protocol families). Astfel, TCP/IP se numeste domeniul
Internet (Internet domain). Un exemplu de alt domeniu este domeniul
UNIX, care permite comunicarea ntre procese ce ruleaza pe acelasi sistem.
Exist
a diferente ntre diversele implementari pe diferite platforme, sau
de la o versiune la alta. De obicei aceste diferente nu sunt mari n ceea ce
priveste functionalitatea de baza a soclurilor, dar recomandam cititorului
s
a consulte paginile de manual sau documentatia aferenta sistemului de
operare folosit. Exemplele date n acest capitol au fost scrise si testate sub
Linux, cu un nucleu versiunea 2.4.20. Cu eventuale mici modificari, ele ar
trebui s
a functioneze corect si pe alte platforme.
Vom utiliza termenii domeniu , familie de protocoale si arhitec
tur
a de retea interschimbabil. De asemenea, vom utiliza atat termenul de
soclu c
at si varianta engleza, socket. Sockets, la plural, va fi folosit
1.2.
Modul de lucru cu sockets este foarte asemanator cu lucrul cu fisiere. Un soclu reprezint
a un capat de comunicatie. La fel ca si n cazul
fisierelor, putem scrie si citi un soclu prin intermediul unui descriptor iar
aceste operat, ii vor fi traduse de sockets n operatii la nivelul protocoalelor
1.2. CONCEPTE
11
de retea.
Mecanismul soclurilor suporta, n principiu, mai multe familii de protocoale. Diversele versiuni de sockets implementeaza seturi distincte de
domenii. Implementarea pe care o utilizam n acest capitol (vezi sectiunea
1.1) foloseste, printre altele: TCP/IP, IPv6, Novel IPX, Appletalk, cat si
familia UNIX utilizat
a exclusiv pentru comunicarea ntre procesele de pe
aceeasi masin
a. Acestor familii de protocoale le corespunde cate o constanta.
De exemplu, pentru TCP/IP avem PF INET iar pentru domeniul UNIX
avem PF UNIX .
Diversele arhitecturi de retea folosesc si diverse scheme de adresare.
Pentru a p
astra o interfata de programare unica, proiectantii mecanismului
de socluri au definit o structura generica1 :
struct s o c k a d d r {
sa family t sa family ;
char s a d a t a [ 1 4 ] ;
}
C
ampul sa data contine datele de adresare, n formatul specific diverselor familii de protocoale. Desi lungimea este fixata aici la 14 octeti, pentru
fiecare arhitectur
a de retea sunt definite structuri separate ce pot depasi,
unde este cazul, dimensiunea struct sockaddr . Din acest motiv, toate
apelurile sistem si functiile de biblioteca ce au ca parametru o adresa vor
mai avea un parametru, ce reprezinta lungimea structurii ce se transmite
efectiv.
Toate aceste structuri au primul camp, sa family, identic. Pentru TCP/IP (v4) este definit
a urmatoarea structura:
Aceast
a structur
a poate varia usor, la r
andul ei, de la un sistem la altul.
12
CAPITOLUL 1. SOCLURI
struct s o c k a d d r i n {
/ a d d r e s s f a m i l y : AF INET /
sa family t
sin family ;
/ p o r t i n n e t w o r k b y t e o r d e r /
u int16 t
sin port ;
/ i n t e r n e t a d d r e s s /
struct i n a d d r s i n a d d r ;
};
/ I n t e r n e t a d d r e s s . /
struct i n a d d r {
/ a d d r e s s i n n e t w o r k b y t e o r d e r /
u int32 t
s addr ;
};
C
ampul sin port reprezinta portul TCP sau UDP, ce identifica procesul.
C
ampul sin addr este o structura de tip in addr ce are un singur camp,
s addr, care are 4 octeti si care reprezinta o adresa IP. Octetii constituenti
at
at ai porturilor, c
at si ai adreselor trebuie sa fie ordonati n ordinea folosita
de retea ( network byte order). Dupa cum se stie, un numar ntreg ce se
reprezint
a pe mai multi octeti se poate pastra n memoria calculatorului
n cel putin dou
a feluri: cu octetii mai semnificativi la nceput (la adresele
mai mici) sau la sf
arsit (la adresele mai mari). Spre exemplu, numarul 515
(515 = 2 * 256 + 3), daca l reprezentam pe doi octeti, se poate pastra
ca (2, 3) sau ca (3, 2). Numarul 2 este cel mai semnificativ octet, iar
num
arul 3 cel mai put, in semnificativ. Ambele reprezentari sunt folosite.
De exemplu, platformele Intel folosesc ordinea cu cel mai putin semnificativ
octet nainte. Prin conventie, n retea numerele sunt reprezentate astfel
nc
at cel mai semnificativ octet este primul. Prin urmare, este necesar
s
a transform
am aceste numere din reprezentarea interna a masinii n cea
extern
a, a retelei, atunci cand transmitem numere si invers atunci cand le
reception
am.
Exist
a c
ateva functii de biblioteca care ne ajuta sa facem acest lucru:
uint32
uint16
uint32
uint16
t
t
t
t
htonl ( uint32
htons ( u i n t 1 6
ntohl ( uint32
ntohs ( u i n t 1 6
t
t
t
t
hostlong ) ;
hostshort ) ;
netlong ) ;
netshort ) ;
13
1.3.
Putem mp
arti protocoalele de comunicatie n doua mari categorii: orientate pe conexiune sau fara conexiune. Aceasta distinctie este importanta
din mai multe puncte de vedere. Mai ntai, depinde de natura aplicatiei
dac
a este bine s
a utiliz
am un tip de protocol sau altul. Apoi, functiile si
apelurile de sistem difer
a ntr-un caz si n altul (desi vom vedea ca nu asa
de mult).
Apelul sistem
socket
Acest apel sistem foloseste la crearea unui soclu. Prototipul sau este:
i n t s o c k e t ( i n t domain , i n t type , i n t p r o t o c o l ) ;
care avem nevoie pentru a identifica soclul creat atunci cand folosim alte
apeluri sistem. In caz de eroare, socket ntoarce valoarea -1 si pozitioneaza
corespunz
ator variabila globala errno .
14
Apelul sistem
CAPITOLUL 1. SOCLURI
bind
Dup
a crearea unui soclu, acestuia nu i corespunde nici o adresa. Se
spune c
a el nu este legat. Cu ajutorul apelului sistem bind asociem unui
soclu o adres
a de masina si o adresa de proces (vezi ??), sau n termeni
TCP/IP o adres
a IP si un port.
i n t bind ( i n t s o c k f d , struct s o c k a d d r my addr , s o c k l e n t
addrlen ) ;
nseamn
a c
a el va fi legat la toate interfetele. Un socket cu adresa locala
INADDR ANY poate receptiona pachete si conexiuni de retea sosite c
atre
oricare din adresele statiei, iar n cazul n care se initiaza o conexiune sau
se trimit datagrame cu adresa locala legata la 0.0.0.0, se va folosi adresa
Apelul sistem
connect
15
distant
a, proces care este determinat de parametrul serv addr. Spre exemplu, pentru TCP are loc procesul descris n sectiunea ??.
connect se
termin
a doar atunci c
and legatura a fost stabilita, sau n cazul n care apare
o eroare.
Pentru protocoalele fara conexiune, cum ar fi UDP, nu se transmit nici
un fel de mesaje procesului aflat la distanta, ci doar se nregistreaza valoarea
serv addr pentru a fi folosita atunci cand procesul care a apelat connect
scrie sau citeste date prin apeluri sistem cum ar fi read sau write . Apelul
sistem connect revine imediat n acest caz. Fara connect , aceste apeluri
nu ar putea fi folosite ntrucat sistemul nu ar sti cui trebuie sa trimita datele.
Ultimul parametru specifica lungimea lui serv addr.
Apelul sistem
listen
In urma apel
arii lui listen , sistemul de operare stie ca procesul care l-a
apelat doreste s
a accepte conexiuni. Din acest moment, atunci cand sosesc
mesaje care stabilesc conexiuni catre soclul s, acestea sunt memorate ntr-o
coad
a de asteptare p
an
a cand sunt acceptate (vezi apelul sistem accept
, mai jos). Lungimea acestei cozi este data de parametrul backlog. Daca
mai multe cereri de stabilire a unei conexiuni sosesc fara a fi acceptate si
num
arul lor dep
aseste valoarea data de backlog, cererile excedentare sunt
ignorate sau respinse (functie de protocolul utilizat).
Apelul sistem
accept
Acest apel sistem este folosit de catre un proces atunci cand doreste sa
preia o cerere de la un proces client din coada de conexiuni (vezi listen
mai sus). Fireste, accept este folosit doar pentru protocoale orientate pe
conexiune.
i n t a c c e p t ( i n t s , struct s o c k a d d r addr , s o c k l e n t
addrlen ) ;
16
CAPITOLUL 1. SOCLURI
accept creeaz
a un nou socket si ntoarce descriptorul corespunzator.
Acest nou socket este conectat la procesul client a carui cerere de cone
xiune a fost preluat
a din coada. In cazul n care o astfel de cerere nu exista,
accept se blocheaz
a n as, teptarea unei conexiuni.
Acest apel sistem va completa spatiul dat prin parametrul addr cu
adresa (si portul) procesului client. Numarul maxim de octeti pe care
accept l poate scrie la adresa addr este transmis prin parametrul addrlen. Acest parametru este la randul lui un pointer (si are ca valoare adresa
unei variabile de tip socklen t ), deoarece accept va completa la adresa
respectiv
a num
arul de octet, i efectiv scrisi la adresa addr. Pentru ca lucrurile s
a fie mai clare, urm
ariti folosirea acestui apel sistem n exemplul 1 pe
care l d
am mai jos.
Apelurile sistem
read
si
write
Aceste apeluri sistem sunt folosite n general pentru operat, ii de intrare/iesire. Ele pot fi folosite si cu sockets, cu urmatoarele observatii:
nu pot fi folosite pentru sockets de tip SOCK DGRAM (protocoale
f
ar
a conexiune) decat n cazul n care s-a folosit n prealabil apelul
sistem connect ;
spre deosebire de operatiile cu fisiere, atunci cand sunt folosite cu
sockets,
read si
write se pot termina nainte ca toate datele
solicitate s
a fie citite sau scrise (vezi subcapitolul 1.4).
Apelul sistem
Apelul sistem
close
close nchide un descriptor.
int c l o s e ( int fd ) ;
Dac
a tipul soclului care se nchide este SOCK STREAM , sistemul de
operare va ncerca s
a trimita datele din tampoane ce nu au fost nca trimise.
Apelurile sistem
recvfrom
17
si
sendto
1.4.
Exemple simple
18
CAPITOLUL 1. SOCLURI
#include
#include
#include
#include
#include
#include
<s y s / t y p e s . h>
<s y s / s o c k e t . h>
<n e t i n e t / i n . h>
<s t r i n g . h>
<u n i s t d . h>
<netdb . h>
#include n e t i o . h
i n t s e t a d d r ( struct s o c k a d d r i n addr , char name ,
u i n t 3 2 t inaddr , short s i n p o r t ) {
struct h o s t e n t h ;
memset ( ( void ) addr , 0 , s i z e o f ( addr ) ) ;
addr>s i n f a m i l y = AF INET ;
i f ( name != NULL) {
h = gethostbyname ( name ) ;
i f ( h == NULL)
return 1;
addr>s i n a d d r . s a d d r =
( u i n t 3 2 t ) h>h a d d r l i s t [ 0 ] ;
} else
addr>s i n a d d r . s a d d r = h t o n l ( i n a d d r ) ;
addr>s i n p o r t = h t o n s ( s i n p o r t ) ;
return 0 ;
}
i n t s t r e a m r e a d ( i n t s o c k f d , char buf , i n t l e n ) {
i n t nread ;
int remaining = l e n ;
while ( r e m a i n i n g > 0 ) {
i f (1 ==
( nread = r e a d ( s o c k f d , buf , r e m a i n i n g ) ) )
return 1;
i f ( nread == 0 )
break ;
r e m a i n i n g = nread ;
buf += nread ;
}
return l e n r e m a i n i n g ;
19
}
i n t s t r e a m w r i t e ( i n t s o c k f d , char buf , i n t l e n ) {
i n t nwr ;
int remaining = l e n ;
while ( r e m a i n i n g > 0 ) {
i f (1 == ( nwr = w r i t e ( s o c k f d , buf , r e m a i n i n g ) ) )
return 1;
r e m a i n i n g = nwr ;
buf += nwr ;
}
return l e n r e m a i n i n g ;
}
Dac
a cp nu este NULL, parametrul urmator ( s addr ) este ignorat, iar daca
este NULL, adresa va fi luata din s addr , care trebuie sa aiba octetii n
ordinea masinii (host byte order ). Acest ultim caz este efectiv folosit n
exemplele date doar atunci cand dorim sa utilizam adresa INADDR ANY .
Ultimul argumet,
n ordinea masinii.
port , reprezint
a portul si trebuie sa fie reprezentat
20
CAPITOLUL 1. SOCLURI
Trebuie avut n vedere faptul ca exemplele de mai jos sunt doar niste
exemple si nu aplicatii reale, deoarece ele nu trateaza o multime de situatii.
Multe teste de eroare au fost omise, mai ales n primele exemple, pentru
claritatea codului. De asemenea, tratarea semnalelor lipseste n cele mai
multe cazuri cu des
av
arsire. Protocoalele descrise si implementate nu sunt
suficient de riguroase. Acele programe care creeaza procese noi nu apeleaza
wait pentru a prelua starea fiilor. Atunci c
and se scriu programe reale,
trebuie s
a se tin
a cont de nenumarate aspecte legate de tratarea erorilor,
interactiunea cu utilizatorii s, i cu sistemul de operare. Nu ne-am propus nsa
ca n spatiul acestei c
arti sa abordam astfel de aspecte.
1.4.1.
#include
#include
#include
#include
#include
#include
#include
#include
#include
<s t d i o . h>
<u n i s t d . h>
< s t d l i b . h>
<s y s / t y p e s . h>
<s y s / s o c k e t . h>
<s y s / s t a t . h>
<n e t i n e t / i n . h>
< f c n t l . h>
n e t i o . h
5678
i n t main ( void ) {
i n t fd , s o c k f d , con nfd ;
char buf [ 1 0 2 4 ] ;
21
22
CAPITOLUL 1. SOCLURI
close ( sockfd ) ;
exit (0);
Serverul se leag
a la adresa 0.0.0.0 ( INADDR ANY ), ceea ce nseamna
c
a accept
a conexiuni pe oricare din adresele masinii locale si foloseste portul
5678. Aceast
a valoare nu este total arbitrara: ea trebuie sa fie mai mare
de 1023 si este bine s
a fie mai mare de 5000. Porturile sub 1024 se numesc
privilegiate si nu pot fi folosite decat de superuser (root) si sunt folosite
n general de serviciile standard, cum ar fi e-mail-ul sau FTP. Programele
care las
a sistemul s
a le aloce un port (specificand valoarea 0) primesc n
mod conventional2 porturi cu valori ntre 1024 si 4999. De aici nevoia de a
aloca pentru server un port cu valoare peste 5000, pentru ca sa nu nimerim
nt
ampl
ator peste un port alocat deja.
In continuare, programul asteapta conexiuni de la clienti care urmeaza
s
a transfere c
ate un fisier, pana la limita de 100 de conexiuni. Atunci cand
se stabiliste o nou
a conexiune, serverul creaza un fisier nou, n directorul curent, cu numele fisierXXX (unde XXX este 001, 002 etc.), n care copiaza
In unele distributii Linux (RedHat), porturile alocate aplicatiilor sunt implicit ntre 32768 si 61000.
Alte distributii, cum ar fi Debian, se comport
a normal. Diverse sisteme de operare pot s
a adere sau nu la conventia
descris
a. Acest comportament se poate modifica, n Linux, scriind fisierul
/proc/sys/net/ipv4/ip local port range sau prin sysctl modific
and variabila
net.ipv4.ip local port range.
< s t d l i b . h>
<s y s / t y p e s . h>
<s y s / s o c k e t . h>
<s y s / s t a t . h>
<n e t i n e t / i n . h>
<arpa / i n e t . h>
< f c n t l . h>
n e t i o . h
23
24
CAPITOLUL 1. SOCLURI
p r i n t f ( F i s i e r u l a f o s t t r i m i s cu s u c c e s \n ) ;
exit (0);
Dup
a conectare, clientul trimite serverului fisierul al carui nume i este
dat ca parametru.
1.4.2.
<s t d i o . h>
<s t r i n g . h>
<u n i s t d . h>
< s t d l i b . h>
<s y s / t y p e s . h>
<s y s / s o c k e t . h>
<s y s / s t a t . h>
<n e t i n e t / i n . h>
n e t i o . h
5678
2048
i n t main ( void ) {
int sockfd ;
char buf [MAXBUF] ;
struct s o c k a d d r i n l o c a l a d d r ;
i n t nread ;
i f (1 == ( s o c k f d = s o c k e t ( PF INET , SOCK DGRAM, 0 ) ) ) {
p r i n t f ( E r o a r e l a s o c k e t ( ) \ n ) ;
exit (1);
}
s e t a d d r (& l o c a l a d d r , NULL, INADDR ANY,
SERVER PORT ) ;
25
i f (1 ==
bind ( s o c k f d , ( struct s o c k a d d r )& l o c a l a d d r ,
sizeof ( local addr ))) {
p r i n t f ( E r o a r e l a bind ( ) \ n ) ;
exit (1);
}
while ( 0 < ( nread = r e a d ( s o c k f d , &buf , MAXBUF) ) ) {
p r i n t f ( %. s \n , nread , buf ) ;
}
exit (0);
}
singur
a operatie write iar ulterior prin doua apeluri sistem. Observati ca
serverul va afisa:
abcd
ab
cd
Acest lucru se datoreaza faptului ca apelul sistem read , atunci cand
este folosit cu socluri de tip SOCK DGRAM , revine n momentul n care se
receptioneaz
a o datagrama. La randul lui, apelul sistem write , folosit cu
socluri de tip SOCK DGRAM , va determina sistemul de operare sa trimita
o datagram
a pentru fiecare apel al sau.
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
<s t d i o . h>
<s t r i n g . h>
<u n i s t d . h>
< s t d l i b . h>
<s y s / t y p e s . h>
<s y s / s o c k e t . h>
<s y s / s t a t . h>
<n e t i n e t / i n . h>
<arpa / i n e t . h>
n e t i o . h
26
CAPITOLUL 1. SOCLURI
int sockfd ;
struct s o c k a d d r i n l o c a l a d d r , r e m o t e a d d r ;
i f (1 == ( s o c k f d = s o c k e t ( PF INET , SOCK DGRAM, 0 ) ) ) {
p r i n t f ( E r o a r e l a s o c k e t ( ) \ n ) ;
exit (1);
}
s e t a d d r (& l o c a l a d d r , NULL, INADDR ANY, 0 ) ;
i f (1 ==
bind ( s o c k f d , ( struct s o c k a d d r )& l o c a l a d d r ,
sizeof ( local addr ))) {
p r i n t f ( E r o a r e l a bind ( ) \ n ) ;
exit (1);
}
s e t a d d r (& remote addr , SERVER ADDRESS, 0 ,
SERVER PORT ) ;
i f (1 ==
c o n n e c t ( s o c k f d , ( struct s o c k a d d r )& remote addr ,
sizeof ( remote addr ) ) ) {
p r i n t f ( E r o a r e l a c o n n e c t ( ) \ n ) ;
exit (1);
}
w r i t e ( s o c k f d , abcd , s t r l e n ( abcd ) ) ;
w r i t e ( s o c k f d , ab , s t r l e n ( ab ) ) ;
w r i t e ( s o c k f d , cd , s t r l e n ( cd ) ) ;
exit (0);
1.4.3.
Am v
azut n primul exemplu un server care preia fisiere de la client, i
aflati la distant
a, fisiere pe care le scrie pe disc. Daca at, i avut curiozitatea
s
a rulati mai multi clienti deodata, ati constatat fie ca transferurile nu se
efectueaz
a dec
at pe r
and, fie chiar ca sunt refuzate unele conexiuni, daca
num
arul de clienti este mai mare. Acest lucru se datoreaza modului n care
este scris programul server. El deserveste o singura conexiune la un moment
dat. Acest mod de lucru se numeste server iterativ. Daca dorim ca serverul
s
a deserveasc
a mai multi clienti simultan, el trebuie scris ntr-o maniera
ce se numeste server concurent. De fapt, acesta este motivul pentru care
apelul sistem accept creeaza un nou soclu. In momentul n care se accepta
27
o nou
a conexiune, serverul concurent creeaza un nou proces care va deservi
clientul, iar procesul original (parinte) continua sa asculte soclul original
si s
a preia noi cereri.
Mai nt
ai avem un fisier antet, comun pentru toate programele din acest
exemplu:
#i f n d e f EX3 H
/ INDENTOFF /
#define EX3 H
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
EX3 SUCCESS
EX3 GOAHEAD
EX3 BYE
EX3 READERR
EX3 LONGLINE
EX3 FILECREA
EX3 FILEWRERR
EX3 EARLYEOF
EX3 FNAMNOTSET
EX3 INVCMD
0
1
2
3
4
5
6
7
8
9
i n t r e a d l i n e ( int , char , i n t ) ;
/ INDENTON /
#endif
<s t d i o . h>
<u n i s t d . h>
< s t d l i b . h>
<s y s / t y p e s . h>
<s y s / s o c k e t . h>
<s y s / s t a t . h>
<n e t i n e t / i n . h>
< f c n t l . h>
<s t r i n g . h>
n e t i o . h
ex3 . h
5678
1024
28
CAPITOLUL 1. SOCLURI
29
30
CAPITOLUL 1. SOCLURI
s t r c p y ( f i l e n a m e , cmd ) ;
r e p l y ( connfd , EX3 SUCCESS ) ;
continue ;
}
i f ( 0 == strncmp ( cmd , data , n ) ) {
if (! file name ) {
r e p l y ( connfd , EX3 FNAMNOTSET ) ;
continue ;
}
g e t f i l e ( connfd , f i l e n a m e , buf ) ;
return ;
}
/ comanda n e c u n o s c u t a /
r e p l y ( connfd , EX3 INVCMD ) ;
} while ( 1 ) ;
}
i n t main ( void ) {
i n t s o c k f d , con nfd ;
struct s o c k a d d r i n l o c a l a d d r , r e m o t e a d d r ;
socklen t rlen ;
p i d t pid ;
i f (1 ==
( s o c k f d = s o c k e t ( PF INET , SOCK STREAM, 0 ) ) ) {
p r i n t f ( Nu am putut c r e a s o c l u l \n ) ;
exit (1);
}
s e t a d d r (& l o c a l a d d r , NULL, INADDR ANY,
SERVER PORT ) ;
i f (1 ==
bind ( s o c k f d , ( struct s o c k a d d r )& l o c a l a d d r ,
sizeof ( local addr ))) {
p r i n t f ( E r o a r e l a bind ( ) \ n ) ;
exit (1);
}
i f (1 == l i s t e n ( s o c k f d , 5 ) ) {
p r i n t f ( E r o a r e l a l i s t e n ( ) \ n ) ;
exit (1);
}
r l e n = sizeof ( remote addr ) ;
31
while ( 1 ) {
co nnf d =
accept ( sockfd ,
( struct s o c k a d d r )& remote addr ,
&r l e n ) ;
i f ( con nfd < 0 ) {
p r i n t f ( E r o a r e l a a c c e p t ( ) \ n ) ;
exit (1);
}
pid = f o r k ( ) ;
switch ( p i d ) {
case 1:
p r i n t f ( E r o a r e l a f o r k ( ) \ n ) ;
exit (1);
case 0 :
/ p r o c e s f i u /
close ( sockfd ) ;
e x 3 p r o t o ( c onn fd ) ;
exit (0);
default :
/ p r o c e s p a r i n t e /
c l o s e ( co nnf d ) ;
}
}
/ a i c i nu ar t r e b u i sa a j u n g /
exit (0);
}
32
CAPITOLUL 1. SOCLURI
pos++)
switch ( r e a d ( connfd , ( void ) pos , 1 ) ) {
case 1:
/ e r o a r e /
return EX3 READERR;
case 0 :
/ c o n e x i u n e i n c h e i a t a /
return EX3 EARLYEOF ;
default :
i f ( pos == DELIM1) {
#i f d e f DELIM2
flag = 1;
break ;
}
i f ( pos == DELIM2) {
if (! flag )
break ;
( pos 1 ) = \0 ;
#e l s e
pos = \0 ;
#endif
return EX3 SUCCESS ;
}
#i f d e f DELIM2
if ( flag )
flag = 0;
#endif
break ;
}
/ numele f i s i e r u l u i e p r e a l u n g /
return EX3 LONGLINE ;
}
O problem
a ar fi c
a s-ar putea ntampla sa citim mai mult decat o
singur
a linie deodat
a. De fapt, aceasta nu este adevarata problema. Am
33
prelucra sirul de caractere pana la newline dupa care am pastra restul pentru linia urm
atoare. Intr-adevar deranjant ar fi ca serverul sa se blocheze
astept
and mai multe caractere decat are linia trimisa. Fiind o aplicatie interactiv
a, acest fapt ar constitui o problema, deoarece nu am primi raspunsul
la o comand
a imediat.
Felul n care este rezolvata problema n codul nostru nu este nsa ideal.
Pentru fiecare caracter efectuam un apel sistem read , care necesita o
schimbare de context ntre modul utilizator si modul nucleu, fapt care ia
destul de mult timp. In mod obisnuit acest lucru nu este ceva deosebit
de grav, dar pot exista situatii unde sa dorim sa reducem cat mai mult
nc
arcarea sistemului. Astfel de situatii sunt, spre exemplu, acelea n care
sistemul folosit este lent sau ncarcat, sau atunci cand dorim sa obtinem
performante ridicate. In sectiunea 1.5.1 vom da o alta solutie.
Un alt element nou n acest exemplu este existenta unui protocol de
comunicatie ntre client si server. Desigur, acest protocol, de nivel aplicatie,
este foarte simplu. De asemenea, am putea spune ca si n cazul primului
exemplu exist
a un protocol aplicatie, dar ar fi exagerat.
Protocolul din acest exemplu ar putea fi descris n felul urmator:
(Clientul deschide conexiunea, iar serverul o accepta.)
Serverul intr
a n modul comanda. In acest mod, serverul as, teapta
linii text (caractere ASCII ce se termina cu newline) ce reprezinta
comenzi.
Clientul trimite astfel de comenzi si preia un cod de stare de la server.
Pentru fiecare comanda, serverul raspunde printr-o linie de stare.
O comand
a este formata dintr-un sir de litere urmat eventual de argumente. Argumentele sunt despartite ntre ele si de comanda prin exact
un caracter spatiu3 . Comanda se termina prin caracterul newline. O linie
de stare este format
a din doua caractere, pe primele pozitii din linie, care
formeaz
a un cod de stare, un caracter spatiu si un text explicativ aferent.
Codul de stare este numeric, cu valori ntre 0 si 99 si se reprezinta prin
dou
a caractere ASCII ntre 0 si 9. Scopul urmarit este acela de a putea
tip
ari linia de stare direct pe ecran, motiv pentru care s-a inclus s, i textul
explicativ. Astfel, operatorul uman poate urmari mai usor ce se ntampla.
3
tal
a
34
CAPITOLUL 1. SOCLURI
Acest lucru este inspirat din protocolul SMTP folosit la transferul mesajelor
electronice.
Un alt beneficiu al alegerii acestui protocol este acela ca putem folosi
comanda telnet adres
a port pentru a verifica serverul sau chiar pentru
a-l utiliza la crearea fisierelor text.
Comenzile acceptate sunt:
filename {nume-fi
sier } Seteaza numele fisierului, asa cum va fi creat pe
masina serverului. Serverul raspunde prin codul 00 (OK) sau 04
00
01
03
04
05
06
07
08
09
35
<s t d i o . h>
<u n i s t d . h>
< s t d l i b . h>
<s y s / t y p e s . h>
<s y s / s o c k e t . h>
<s y s / s t a t . h>
<n e t i n e t / i n . h>
<arpa / i n e t . h>
< f c n t l . h>
<s t r i n g . h>
n e t i o . h
ex3 . h
5678
1024
36
CAPITOLUL 1. SOCLURI
( s o c k f d = s o c k e t ( PF INET , SOCK STREAM, 0 ) ) ) {
p r i n t f ( E r o a r e l a s o c k e t ( ) \ n ) ;
exit (1);
}
s e t a d d r (& l o c a l a d d r , NULL, INADDR ANY, 0 ) ;
i f (1 ==
bind ( s o c k f d , ( struct s o c k a d d r )& l o c a l a d d r ,
sizeof ( local addr ))) {
p r i n t f ( E r o a r e l a bind ( ) \ n ) ;
exit (1);
}
s e t a d d r (& remote addr , argv [ 1 ] , 0 , SERVER PORT ) ;
i f (1 ==
c o n n e c t ( s o c k f d , ( struct s o c k a d d r )& remote addr ,
sizeof ( remote addr ) ) ) {
p r i n t f ( C o n e c t a r e a l a s e r v e r a e s u a t \n ) ;
exit (1);
}
/ t r i m i t e numele f i s i e r u l u i /
s n p r i n t f ( buf , MAXBUF, f i l e n a m e %s%s ,
argv [ a r g c 1 ] , d e l i m i t a t o r ) ;
s t r e a m w r i t e ( s o c k f d , ( void ) buf , s t r l e n ( buf ) ) ;
/ a s t e a p t a c o n f i r m a r e a /
r e t = r e a d l i n e ( s o c k f d , buf , MAXBUF) ;
i f ( r e t != EX3 SUCCESS) {
p r i n t f ( C l i e n t : E r o a r e r a s p u n s \n ) ;
exit (1);
}
ack = a t o i ( buf ) ;
switch ( ack ) {
case EX3 SUCCESS :
break ;
default :
p r i n t f ( %s \n , buf ) ;
exit (1);
}
s n p r i n t f ( buf , MAXBUF, data%s , d e l i m i t a t o r ) ;
s t r e a m w r i t e ( s o c k f d , ( void ) buf , s t r l e n ( buf ) ) ;
r e t = r e a d l i n e ( s o c k f d , buf , MAXBUF) ;
i f ( r e t != EX3 SUCCESS) {
p r i n t f ( C l i e n t : E r o a r e r a s p u n s \n ) ;
37
exit (1);
}
ack = a t o i ( buf ) ;
switch ( ack ) {
case EX3 GOAHEAD:
break ;
default :
p r i n t f ( %s \n , buf ) ;
exit (1);
}
/ t r i m i t e f i s i e r u l /
while ( 0 < ( nread = r e a d ( fd , ( void ) buf , MAXBUF) ) ) {
s t r e a m w r i t e ( s o c k f d , ( void ) buf , nread ) ;
}
i f ( nread < 0 ) {
p r i n t f ( C l i e n t : E r o a r e c i t i r e d i n f i s i e r \n ) ;
exit (1);
}
shutdown ( s o c k f d , SHUT WR) ;
c l o s e ( fd ) ;
r e a d l i n e ( s o c k f d , buf , MAXBUF) ;
p r i n t f ( %s \n , buf ) ;
exit (0);
}
1.4.4.
Exercitii
1. Exemplul 2 transfera siruri de caractere predefinite. El nu este deosebit de util n aceasta forma. Rescrieti exemplul astfel ncat serverul
si clientul s
a poat
a fi folositi de catre doi utilizatori pentru a schimba
mesaje. Pentru aceasta, atat serverul cat si clientul vor citi intrarea
standard si vor trimite liniile citite prin ret, ea. Programul aflat la
distant
a va prelua din retea aceste linii de text si le va afisa la iesirea
standard. Faceti n asa fel ncat mesajele afisate sa nu perturbeze
utilizatorii care scriu la randul lor mesaje!
2. Modificati exemplul 3 astfel ncat serverul sa accepte comanda size.
Aceast
a comand
a va specifica lungimea fisierului ce se doreste transferat. Serverul va citi exact atatia octeti dupa comada data, dupa care
trece din nou n modul comanda, de unde va putea trimite un nou
38
CAPITOLUL 1. SOCLURI
fisier. Rezultatul trebuie sa fie posibilitatea de a trimite mai multe
fisiere pe aceeasi conexiune.
3. Scrieti o aplicatie distribuita care monitorizeaza cantitatea de date transferate n retea pe un numar de statii de lucru. Serverul va
prelua datele de la mai multi clienti si va prezenta situatia la cerere
sau periodic prin afisarea numarului total de octeti transferati de
c
atre toti clientii monitorizati si a listei acelor 5 statii de lucru care
au utilizat ret, eaua mai intens.
Datele despre utilizarea retelei le puteti obtine, pentru sistemele Linux, din fisierul /proc/net/dev.
4. Creati un dictionar accesibil prin retea. Serverul va servi definitiile
pentru cuvintele cerute de catre client. Definiti un protocol n stilul
folosit n exemplul 3 astfel ncat serverul sa poata fi folosit si cu telnet
pe post de client. Implementati comenzi pentru a primi definitia unui
cuv
ant, pentru a adauga un nou cuvant la dictionar, pentru a cauta
un subsir de caractere si pentru s, tergerea unui cuvant din lista de
cuvinte.
5. Scrieti un program server si un client corespunza tor care sa descarce
prin retea o ierarhie de directoare, cu fisierele din ea.
1.5.
1.5.1.
Aplicatii
O aplicatie cu fire de executie
S
a presupunem c
a dorim sa strangem ntr-un singur loc, pe un server,
informatiile de jurnalizare de la mai multe programe aflate pe calculatoare
diferite. S
a scriem deci o aplicatie ce consta dintr-un server ce va culege
linii de text de la mai multi clienti si le va scrie ntr-un fisier (jurnal) si
dintr-un client care trimite prin retea serverului liniile ce i parvin de la alte
programe de pe acelas, i calculator cu el. Un program ce va dori sa scrie n
jurnal se va lega la intrarea standard a clientului.
Avem un num
ar destul de mare de clienti ce se vor conecta la server. Cu
un server concurent ce utilizeaza cate un proces pentru fiecare conexiune,
s-ar putea nt
ampla s
a ncarcam destul de mult sistemul pe care ruleaza.
O variant
a prin care s
a reducem aceasta ncarcare este sa folosim, n locul
proceselor, fire de executie.
1.5. APLICAT
II
39
<s t d i o . h>
<u n i s t d . h>
< s t d l i b . h>
<s y s / t y p e s . h>
<s y s / s o c k e t . h>
<s y s / s t a t . h>
<n e t i n e t / i n . h>
< f c n t l . h>
<s t r i n g . h>
<p t h r e a d . h>
n e t i o . h
#define BUFSZ
#define SERVER PORT
1024
( short ) 5 6 7 8
p t h r e a d m u t e x t mutex ;
i n t threadNumber = 0 ;
int fd ;
int r e a d l i n e i n i t ( int s f d ) {
int val ;
s o c k l e n t l e n = sizeof ( int ) ;
g e t s o c k o p t ( s f d , SOL SOCKET, SO RCVLOWAT, &v a l ,
&l e n ) ;
i f ( v a l != 1 ) {
/ i n c e a r c a sa s e t e z i /
l e n = sizeof ( int ) ;
s e t s o c k o p t ( s f d , SOL SOCKET, SO RCVLOWAT,
( void )& v a l , l e n ) ;
g e t s o c k o p t ( s f d , SOL SOCKET, SO RCVLOWAT, &v a l ,
&l e n ) ;
40
CAPITOLUL 1. SOCLURI
i f ( v a l != 1 )
/ nu p o t s e t a v a l o a r e a
/
return 1;
}
return 0 ;
}
i n t r e a d l i n e ( i n t connfd , char l i n e , i n t idx ,
char buf , i n t maxlen ) {
int ret , n ;
f o r ( ; i d x < maxlen ; i d x += r e t ) {
r e t = r e a d ( connfd , buf + idx , maxlen i d x ) ;
i f ( r e t <= 0 )
return r e t ;
f o r ( n = i d x ; n < i d x + r e t ; n++)
i f ( buf [ n ] == \n ) {
/ c o p i a z a l i n i a /
memcpy ( l i n e , buf , n + 1 ) ;
/ muta ce a mai ramas /
memmove( buf , buf + n + 1 , r e t n 1 ) ;
idx = r e t n 1;
return n + 1 ;
}
}
/ sa d e p a s i t tamponul /
return 1;
}
/ f u n c t i a urmatoare e s t e c o r p u l f i e c a r u i f i r de
e x e c u t i e /
void e x 4 p r o t o ( void a r g ) {
int r e t ;
char l i n e [ BUFSZ ] ;
char buf [ BUFSZ ] ;
int idx = 0 ;
i n t co nnf d = ( i n t ) a r g ;
while ( 0 <
( ret =
r e a d l i n e ( connfd , l i n e , &idx , buf ,
1.5. APLICAT
II
41
BUFSZ ) ) ) {
w r i t e ( fd , buf , r e t ) ;
/ s c r i e l i n i a i n
j u r n a l /
}
c l o s e ( co nnf d ) ;
f r e e ( c onn fd ) ;
fsync ( fd ) ;
/ s i n c r o n i z e a z a cu
d i s c u l /
p t h r e a d m u t e x l o c k (&mutex ) ;
threadNumber ;
p r i n t f ( %d f i r e a c t i v e \n , threadNumber ) ;
p t h r e a d m u t e x u n l o c k (&mutex ) ;
return NULL;
}
i n t main ( i n t argc , char argv [ ] ) {
i n t s o c k f d , co nnf d ;
struct s o c k a d d r i n l o c a l a d d r , r e m o t e a d d r ;
socklen t rlen ;
pthread t thread ;
pthread attr t attr ;
char f i l e n a m e = j o u r n a l ;
p t h r e a d a t t r i n i t (& a t t r ) ;
p t h r e a d a t t r s e t d e t a c h s t a t e (& a t t r , 1 ) ;
p t h r e a d m u t e x i n i t (&mutex , NULL ) ;
i f ( argc > 2) {
p r i n t f ( U t i l i z a r e : %s f i s i e r \n , argv [ 0 ] ) ;
exit (1);
}
i f ( a r g c == 2 )
f i l e n a m e = argv [ 2 ] ;
fd =
open ( f i l e n a m e , O CREAT | O APPEND | O WRONLY,
00644);
i f ( f d == 1) {
p r i n t f ( Nu am putut d e s c h i d e j u r n a l u l \n ) ;
exit (1);
}
i f (1 ==
42
CAPITOLUL 1. SOCLURI
( s o c k f d = s o c k e t ( PF INET , SOCK STREAM, 0 ) ) ) {
p r i n t f ( Nu am putut c r e a s o c l u l \n ) ;
exit (1);
}
i f (1 == r e a d l i n e i n i t ( s o c k f d ) ) {
p r i n t f ( I n i t i a l i z a r e a r e a d l i n e a e s u a t \n ) ;
exit (1);
}
s e t a d d r (& l o c a l a d d r , NULL, INADDR ANY,
SERVER PORT ) ;
i f (1 ==
bind ( s o c k f d , ( struct s o c k a d d r )& l o c a l a d d r ,
sizeof ( local addr ))) {
p r i n t f ( E r o a r e l a bind ( ) \ n ) ;
exit (1);
}
i f (1 == l i s t e n ( s o c k f d , 5 ) ) {
p r i n t f ( E r o a r e l a l i s t e n ( ) \ n ) ;
exit (1);
}
r l e n = sizeof ( remote addr ) ;
while ( 1 ) {
co nnf d = ( i n t ) m a l l o c ( s i z e o f ( i n t ) ) ;
i f ( con nfd == NULL) {
p r i n t f ( E r o a r e l a m a l l o c ( ) \ n ) ;
exit (1);
}
co nnf d =
accept ( sockfd ,
( struct s o c k a d d r )& remote addr ,
&r l e n ) ;
i f ( co nnf d < 0 ) {
p r i n t f ( E r o a r e l a a c c e p t ( ) \ n ) ;
exit (1);
}
i f ( 0 !=
p t h r e a d c r e a t e (& thread , &a t t r , e x 4 p r o t o ,
( void ) co nnf d ) ) {
p r i n t f ( Nu am putut c r e a f i r nou\n ) ;
exit (1);
}
1.5. APLICAT
II
43
p t h r e a d m u t e x l o c k (&mutex ) ;
threadNumber++;
p r i n t f ( %d f i r e a c t i v e \n , threadNumber ) ;
p t h r e a d m u t e x u n l o c k (&mutex ) ;
}
exit (0);
}
trebuie s
a aib
a un parametru de tip void . Intamplator, un pointer se
reprezint
a tot pe 4 octeti ca si un int . Evident arg nu va fi folosit ca
si adres
a ci valoarea lui va fi tratata ca ntreg. Acest artificiu simplifica
transmiterea parametrului n cazul de fata, dar nu poate fi folosit oricand.
Am modificat si modul n care citim o linie de text din retea. Felul n
care este scris
a functia readline se bazeaza pe o caracteristica anume a
apelurilor sistem de citire, atunci cand sunt invocate pentru socluri. Astfel, o operatie read (valabil pentru toate apelurile sistem de citire) nu
se va bloca dac
a exist
a date disponibile. Numarul minim de octeti care
vor fi asteptati de sistem poate fi controlat pe unele sisteme prin optiunea
SO RCVLOWAT 4 . Aceast
a optiune se poate citi si scrie cu getsockopt respectiv cu setsockopt . Nu toate implementarile poseda o astfel de optiune si
de asemenea nu toate implementarile permit si setarea unei valori pe langa
citirea ei. Implicit, valoarea lui SO RCVLOWAT este 1 ceea ce nseamna
ca read nu se va bloca daca exista cel putin un octet disponibil.
Un client ce citeste linii de la intrarea standard si le transmite serverului
de jurnalizare este prezentat mai jos:
#include
#include
#include
#include
#include
#include
#include
#include
4
<s t d i o . h>
< s t d l i b . h>
<s t r i n g . h>
<u n i s t d . h>
<s y s / t y p e s . h>
<s y s / s o c k e t . h>
<s y s / s t a t . h>
<n e t i n e t / i n . h>
44
CAPITOLUL 1. SOCLURI
5678
1024
1.5. APLICAT
II
45
s t r e a m w r i t e ( s o c k f d , ( void ) buf ,
s t r l e n ( buf ) ) ) {
p r i n t f ( E r o a r e l a s c r i e r e \n ) ;
exit (1);
}
}
exit (0);
}
1.5.2.
Distribuitor de mesaje.
pe evenimente
Programare orientat
a
Cel mai eficient, dar si cel mai complex mod de a scrie un program
ce lucreaz
a simultan cu mai multe fluxuri de date este acela de a utiliza
apelul sistem select . Acesta verifica pentru un set de descriptori daca
operatiile de scriere sau citire5 pot fi efectuate imediat, fara blocare. Cu
alte cuvinte, select as, teapta evenimente ce consta n schimbarea starii
acelor descriptori. Dup
a fiecare apel sistem select , trebuie sa verificam
toti descriptorii interesanti (care fac parte din set), iar daca starea lor s-a
modificat, s
a efectu
am operatiile necesare.
Programul de mai jos transmite datagramele UDP sosite, pe un port
local, unui client a c
arui adresa si port sunt date n linia de comanda. De
asemenea, programul se detasaza de terminal devenind daemon. Pentru
aceasta execut
a o serie de operatii, sugerate n [Ste90].
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
5
<s t d i o . h>
<u n i s t d . h>
< s t d l i b . h>
<s i g n a l . h>
<s y s / t y p e s . h>
<s y s / s e l e c t . h>
<s y s / s o c k e t . h>
<s y s / s t a t . h>
<s y s / i o c t l . h>
<n e t i n e t / i n . h>
<s y s l o g . h>
46
CAPITOLUL 1. SOCLURI
1500
/ s e t u r i l e de d e s c r i p t o r i u r m a r i t i /
fd set rd set , wr set ;
int s f d ;
void s i g t e r m h a n d l e r ( i n t s ) {
s y s l o g (LOG INFO , e x 5 s e r v e r s t o p p e d ) ;
exit (0);
}
void daemonize ( void ) {
i n t i , maxfd ;
int fd ;
maxfd = g e t d t a b l e s i z e ( ) ;
f o r ( i = 0 ; i < maxfd ; i ++)
close ( i );
c h d ir ( / ) ;
switch ( f o r k ( ) ) {
case 1:
s y s l o g (LOG ERR, E r o a r e l a f o r k ( ) \ n ) ;
case 0 :
/ f i u /
break ;
default :
/ p a r i n t e /
exit (0);
}
setpgid (0 , 0);
f d = open ( / dev / t t y , O RDWR) ;
i f ( f d >= 0 ) {
i o c t l ( fd , TIOCNOTTY) ;
1.5. APLICAT
II
c l o s e ( fd ) ;
s y s l o g (LOG INFO , e x 5 s e r v e r s t a r t e d ) ;
}
}
void m a i n l o o p ( struct s o c k a d d r i n remote ) {
int r e t ;
char buf [ BUFFSZ ] ;
char r p o s = buf ;
char wpos = buf ;
char l a s t = buf + BUFFSZ ;
i n t f r e e = BUFFSZ ;
int a v a i l = 0 ;
for ( ; ; ) {
FD ZERO(& r d s e t ) ;
FD ZERO(& w r s e t ) ;
if ( free )
FD SET( s f d , &r d s e t ) ;
if ( avail )
FD SET( s f d , &w r s e t ) ;
r e t = s e l e c t ( s f d + 1 , &r d s e t , &w r s e t ,
NULL, NULL ) ;
i f ( r e t == 1) {
i f ( e r r n o == EINTR)
continue ;
s y s l o g (LOG ERR, e r r o r a t s e l e c t ( ) ) ;
exit (1);
}
i f ( FD ISSET ( s f d , &w r s e t ) ) {
ret =
s e n d t o ( s f d , wpos , a v a i l , 0 ,
( void )& remote , s i z e o f ( remote ) ) ;
i f (1 == r e t ) {
s y s l o g (LOG ERR, e r r o r w r i t e ( ) i n g ) ;
}
a v a i l = r e t ;
wpos += r e t ;
47
48
CAPITOLUL 1. SOCLURI
i f ( a v a i l == 0 && wpos == l a s t ) {
wpos = r p o s = buf ;
f r e e = BUFFSZ ;
}
}
i f ( FD ISSET ( s f d , &r d s e t ) ) {
r e t = r e a d ( s f d , rpos , f r e e ) ;
i f (1 == r e t ) {
s y s l o g (LOG ERR, e r r o r r e a d ( ) i n g ) ;
exit (1);
}
f r e e = r e t ;
a v a i l += r e t ;
r p o s += r e t ;
}
}
}
i n t main ( i n t argc , char argv [ ] ) {
int p o r t l , p o r t r ;
struct s o c k a d d r i n l o c a l ;
struct s o c k a d d r i n remote ;
i f ( a r g c != 4 ) {
p r i n t f ( U t i l i z a r e : %s p o r t l a d r e s a p o r t r \n ,
argv [ 0 ] ) ;
exit (1);
}
p o r t l = a t o i ( argv [ 1 ] ) ;
p o r t r = a t o i ( argv [ 3 ] ) ;
i f ( p o r t l < 0 | | p o r t r < 0) {
p r i n t f ( Port i n c o r e c t ) ;
exit (1);
}
daemonize ( ) ;
1.5. APLICAT
II
s i g n a l (SIGTERM, s i g t e r m h a n d l e r ) ;
s i g n a l ( SIGPIPE , SIG IGN ) ;
o p e n l o g ( ex5 , 0 , LOG WARNING) ;
s f d = s o c k e t ( PF INET , SOCK DGRAM, 0 ) ;
i f ( s f d == 1) {
s y s l o g (LOG ERR, Could not c r e a t e s o c k e t ) ;
exit (1);
}
s e t a d d r (& l o c a l , NULL, INADDR ANY, p o r t l ) ;
i f (1 ==
bind ( s f d , ( struct s o c k a d d r )& l o c a l ,
sizeof ( l o c a l ))) {
s y s l o g (LOG ERR, Could not bind t o l o c a l ) ;
exit (1);
}
i f (1 == s e t a d d r (&remote , argv [ 2 ] , 0 , p o r t r ) ) {
s y s l o g (LOG ERR, Wrong a d d r e s s ) ;
exit (1);
}
m a i n l o o p ( remote ) ;
exit (0);
}
49
50
CAPITOLUL 1. SOCLURI
Bibliografie
[Ste90] W. Richard Stevens. UNIX Network Programming. Prentice Hall,
Inc., 1990.
51