Sunteți pe pagina 1din 20

1

Comunicaia n reea prin socket

n mod tipic, dou procese comunic unul cu cellalt pe o singur main prin una din urmtoarele tehnici de comunicare inter-proces:

Pipe-uri Cozi de mesaje Memorie partajat

Cum comunic ns dou procese n cadrul unei reele? Spre exemplu, atunci cnd accesm un site Web, pe staia local procesul care ruleaz este browserul Web, n timp ce pe sistemul aflat la distan, procesul care ruleaz este serverul Web. Avem de-a face i aici cu comunicare inter-proces, dar tehnica prin care procesele comunic unul cu cellalt se bazeaz pe sockei.

Ce este un socket?
Un socket este un capt al comunicaiei dintre dou procese n cadrul unei reele. Mai exact, un socket este o combinaie dintre o adres IP i un port n cadrul unui system care particip la procesul de comunicare.

Astfel, pe fiecare system exist un socket care comunic cu un socket aflat la cellalt capt al comunicaiei, peste reea. Sunt dou modele de comunicare n cadrul unei reele: 1. OSI 2. TCP/IP OSI este un model mai curnd teoretic, n timp ce TCP/IP este modelul de comunicare cel mai popular, foarte mult utilizat n present. Comunicarea n reea utiliznd modelul TCP/IP urmeaz modelul client / server. n cadrul acestei arhitecturi, clientul iniiaz comunicarea iar serverul accept cererea, formndu-se astfel o conexiune. Sockeii pot fi utilizai n limbaje ca Java, C, C++, etc. n cele ce urmeaz, vom aborda comunicarea n reea prin sockei utiliznd limbajul C. Exemplul prezentat const dintr-un server care ruleaz continuu i trimite data/timpul current fiecrui client care se conecteaz la el.

Examplu Socket Server


#include #include #include #include #include #include #include #include #include #include <sys/socket.h> <netinet/in.h> <arpa/inet.h> <stdio.h> <stdlib.h> <unistd.h> <errno.h> <string.h> <sys/types.h> <time.h>

int main(int argc, char *argv[]) { int listenfd = 0, connfd = 0; struct sockaddr_in serv_addr; char sendBuff[1025]; time_t ticks; listenfd = socket(AF_INET, SOCK_STREAM, 0); memset(&serv_addr, '0', sizeof(serv_addr)); memset(sendBuff, '0', sizeof(sendBuff)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(5000);

4
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); listen(listenfd, 10); while(1) { connfd = accept(listenfd, (struct sockaddr*)NULL, NULL); ticks = time(NULL); snprintf(sendBuff, sizeof(sendBuff), "%.24s\r\n", ctime(&ticks)); write(connfd, sendBuff, strlen(sendBuff)); close(connfd); sleep(1); } }

Codul de mai sus reprezint implementarea serverului.


Apelul funciei socket() creeaz un socket anonim n cadrul nucleului i returneaz un numr ntreg, cunoscut ca i descriptorul socketului. Funcia primete ca prim argument domeniul/familia pentru adrese internet de tip IPv4, se utilizeaz AF_INET. Al doilea argument SOCK_STREAM specific utilizarea la niveul transport a unui protocol sigur, cu confirmare de mesaje: TCP

Al treilea argument este in general lsat 0, pentru a permite nucleului s decid care este protocolul implicit utilizat n conexiunea respective. Protocolul implicit este TCP pentru comunicaie orientat pe conexiune. Apelul funciei bind() asociaz valorile specificate n cadul structurii serv_addr cu socketul creat la pasul anterior. Astfel, socketului i se asociaz urmtoarele elemente: o Familia/domeniul de adrese o Interfaa pe care realizeaz ascultarea (n cazul n care maina dispune de mai multe interfee) o Portul pe care serverul atept cererile clienilor. Al doilea argument al funciei listen() specific numrul maxim de clieni din coada de ateptare a serverului (pentru socketul de ascultare). Dup apelul funciei listen(), socketul devine un socket de ascultare complet funcional Apelul accept() suspend serverul n ateptarea cererilor de la clieni; cnd o astfel de cerere sosete, funcia accept () returneaz un descriptor reprezentnd socketul client. Apelul accept() este plasat ntr-o bucl infinit, astfel c serverul ruleaz continuu; suspendarea de 1 sec de la finalul buclei este realizat pentru a nu consuma prea mult timp CPU. Imediat ce serverul primete o solicitare de la un client, va determina timpul/data curent i va trimite datele clientului prin intermediul socketului client al crui descriptor a fost returnat de accept().

Prezentm n continuare programul client.

Socket Client Example


#include #include #include #include #include #include #include #include #include #include <sys/socket.h> <sys/types.h> <netinet/in.h> <netdb.h> <stdio.h> <string.h> <stdlib.h> <unistd.h> <errno.h> <arpa/inet.h>

int main(int argc, char *argv[]) { int sockfd = 0, n = 0; char recvBuff[1024]; struct sockaddr_in serv_addr; if(argc != 2) { printf("\n Utilizare: %s <ip of server> \n",argv[0]); return 1; } memset(recvBuff, '0',sizeof(recvBuff)); if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {

7
printf("\n Error : Nu pot crea socket \n"); return 1; } memset(&serv_addr, '0', sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(5000); if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0) { printf("\n eroare inet_pton \n"); return 1; } if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) { printf("\n Eroare : Conexiune esuata \n"); return 1; } while ( (n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0) { recvBuff[n] = 0; if(fputs(recvBuff, stdout) == EOF) { printf("\n Eroare Fputs \n");

8
} } if(n < 0) { printf("\n Eroare read \n"); } return 0; }

n programul de mai sus, se creeaz un client care se conecteaz la server i recepioneaz date de la acesta.

Socket-ul client este creat prin apelul socket(). Informaii ca IP-ul i portul serverului sunt mpachetate ntr-o structur transmis ca parametru funciei connect(); aceasta ncearc conectarea socketului client cu socketul de ascultare de pe server. Note that here we have not bind our client socket on a particular port as client generally use port assigned by kernel as client can have its socket associated with any port but In case of server it has to be a well known socket, so known servers bind to a specific port like HTTP server runs on port 80 etc while there is no such restrictions on clients. Trebuie s observm faptul c socketul client nu are asociat un anume port: clientul utilizeaz n general un port asignat de ctre nucleu. Pe de alt parte, n cazul serverului, portul trebuie sa fie stabilit i cunoscut de ctre clientii care urmeaz s i adreseze cereri (spre exemplu, un server HTTP ruleaz pe portul 80, clienii neavnd restricii legate de portul asociat socketului prin care realizeaz cererea).

Odat conexiunea stabilit ntre client i server, se realizeaz comunicarea prin reea: o Serverul trimite data+timpul curent pe socketul client, prin intermediul descriptorului acestuia o Clientul citete datele efectund operaii de citire normale, pe propriul socket de conectare la server.

Prezentm n continuare un alt exemplu, un program care permite lansarea n execuie att a unui client ct i a serverului (n procese separate).
Explicaii (nu fac parte din cod, sunt adaugate in acest document) #include #include #include #include #include #include #include #include #include #include #include #include #include #include <arpa/inet.h> <netinet/in.h> <stdio.h> <sys/types.h> <sys/socket.h> <unistd.h> <signal.h> <string.h> <errno.h> <sys/ioctl.h> <sys/socket.h> <netdb.h> <net/if.h> <netinet/if_ether.h>

10
#include #include #include #include #define #define #define #define #define <linux/if_packet.h> <netinet/ip.h> <netinet/in.h> <netinet/ip_icmp.h> IP_LEN 16 MAX_CLIENTS 100 BUFLEN 512 PORT 9930 ETH "eth1"

Aici am memorat IP-ul, masca de subreea i adresa de broadcast char hostIP[IP_LEN], netMask[IP_LEN], broadcastIP[IP_LEN]; Strucrur pentru memorarea clienilor (IP + Nume dat la JOIN) typedef struct { char IP[IP_LEN]; char Name[BUFLEN]; } ClientInfo; ClientInfo clientInfo[MAX_CLIENTS]; Aceste variabile sunt globale pentru a putea fi accesate din handler-ul pentrul semnalul SIGALRM (alarma are asociata rutina rutina_alarm care se activeaza periodic) De asemenea, mesajul joinMessage este global pentru ca se transmite periodic din rutina_alarm struct sockaddr_in si_other;

11
int s, slen=sizeof(si_other); char joinMessage[BUFLEN]; Aceast funcie determina adresa de broadcast, i o memoreaza n variabila global broadcastIP void GetBroadcastIP() { int socketd; struct ifreq ifr; socketd = socket(AF_INET, SOCK_DGRAM, 0); if (socketd <= 0) { perror("socket"); return -1; } strcpy(ifr.ifr_name, ETH); if (0 == ioctl(socketd, SIOCGIFADDR, &ifr)) { strcpy(hostIP, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr)); } if (0 == ioctl(socketd, SIOCGIFNETMASK, &ifr)) { strcpy(netMask, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_netmask)->sin_addr));

12
} if (0 == ioctl(socketd, SIOCGIFBRDADDR, &ifr)) { strcpy(broadcastIP, inet_ntoa(((struct sockaddr_in *)&ifr.ifr_broadaddr)->sin_addr)); } printf("IP: %s Netmask: %s Broadcast: %s\n", hostIP, netMask, broadcastIP); close(socketd); } Iniializm struncturile de date n care se memoreaz clienii void InitServer() { int i; for (i = 0; i < MAX_CLIENTS; i++) clientInfo[i].IP[0]='\0'; } Aici se trateaza TOATE mesajele recepionate de ctre server: JOIN LEAVE MESSAGE

void HandleMessage (char *IP, char* message){ int i, first = -1, index = -1; Aici se determin dac adresa IP primit ca parametru a mai fost

13
memorat, ca urmare a unui mesaj JOIN Daca a fost memorat, index va fi poziia n vectorul de clieni First este prima structur liber! for (i = 0; i < MAX_CLIENTS; i++) { if (strcmp(IP, clientInfo[i].IP)==0) index = i; if (clientInfo[i].IP[0]=='\0') first = i; } Dac mesajul este JOIN... if (strncmp(message,"JOIN",4)==0) { i IP-ul este nou, se adaug clientul in vectorul de structuri (IP, Name) if (index == -1) { strcpy(clientInfo[first].IP,IP); strcpy(clientInfo[first].Name,&message[5]); } printf("\n"); afieaz toi clienii nregistrai prin mesaje JOIN, sub forma: IP (Name) for (i = 0; i < MAX_CLIENTS; i++) if (clientInfo[i].IP[0]!='\0') printf("%s (%s)\n ",clientInfo[i].IP,clientInfo[i].Name); solicit utilizatorului un nou mesaj Prompt(); } Dac mesajul este LEAVE, i IP-ul clientului exist n lista de structuri (clientul a fost nregistrat anterior), se deinregistreaza

14
IP-ul if (strncmp(message,"LEAVE",5)==0) { if (index != -1) clientInfo[index].IP[0]='\0'; } Dac mesajul este de tip MESSAGE, iar IP-ul clientului exist n lista de structuri (clientul a fost nregistrat anterior), se afieaz mesajul sub forma: NUME: mesaj, unde NUME este numele de nregistrare al clientului, precizat la JOIN if (strncmp(message,"MESSAGE",7)==0) { if (index != -1) printf("\n%s:%s\n",clientInfo[index].Name,&message[8]); Prompt(); } } Rutina pentru alarm, trimite periodic mesajul de JOIN al clientului void rutina_alarm(int sig_num) { if (joinMessage[0]!='\0') { if (sendto(s, joinMessage, BUFLEN, 0, &si_other, slen)==-1) Diep("sendto()"); Reactiveaz alarma... alarm(5); } } Rutina de tratare eroare fatal (se iese din aplicaie) void Diep(char *s) {

15
perror(s); exit(1); } Prompt pentru introducere de mesaje void Prompt() { printf("\nMesaj (LEAVE pentru deinregistrare): \n"); } Procesul Server int Server(void) { struct sockaddr_in si_me, si_other; int s, i, slen=sizeof(si_other); char buf[BUFLEN]; char clientIP[IP_LEN]; Iniializare structuri de memorare a clienilor InitServer(); if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) Diep("socket"); memset((char *) &si_me, 0, sizeof(si_me)); si_me.sin_family = AF_INET; si_me.sin_port = htons(PORT); si_me.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(s, &si_me, sizeof(si_me))==-1) Diep("bind"); while(1) { if (recvfrom(s, buf, BUFLEN, 0, &si_other, &slen)==-1) Diep("recvfrom()");

16
strcpy(clientIP,inet_ntoa(si_other.sin_addr)); Rutina de tratare a mesajelor, are ca parametrii IP-ul clientului i Mesajul recepionat HandleMessage(clientIP,buf); } close(s); return (0); } Procesul Client int Client(void) { int broadcast = 1, result; char buf[BUFLEN], msg[BUFLEN]; GetBroadcastIP(); Se creeaz socket if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1) Diep("socket"); memset((char *) &si_other, 0, sizeof(si_other)); si_other.sin_family = AF_INET; si_other.sin_port = htons(PORT); Se pregtete trimiterea datelor pe adresa broadcast if (inet_aton(broadcastIP, &si_other.sin_addr)==0) { fprintf(stderr, "inet_aton() failed\n"); exit(1);

17
} Trebuie setat socketul pentru a putea trimite mesaje broadcast result=setsockopt(s, SOL_SOCKET, SO_BROADCAST,&broadcast, sizeof(broadcast)); if (result !=0) { printf("Eroare broadcast! %d\n", errno); perror("Eroare!"); } printf("Broadcast: %s\n", inet_ntoa(si_other.sin_addr)); Mesaj JOIN... printf("JOIN NAME: "); gets(msg); strcpy(joinMessage,"JOIN:"); strcat(joinMessage,msg); if (sendto(s, joinMessage, BUFLEN, 0, &si_other, slen)==-1) Diep("sendto()"); Instalare alarm, care va trimite mesajul JOIN periodic signal(SIGALRM, rutina_alarm); alarm(5); while (1) { gets(msg); Dac mesajul este LEAVE, se altereaz mesajul JOIN pentru a nu mai fi trimis de alarm. Clientul va fi deinregistrat n server if (strncmp(msg,"LEAVE",5)==0) { strcpy(buf,"LEAVE:"); joinMessage[0]='\0';

18
if (sendto(s, buf, BUFLEN, 0, &si_other, slen)==-1) Diep("sendto()"); } Este construit un mesaj tip MESSAGE, care va fi trimis pe adresa broadcast else { strcpy(buf,"MESSAGE:"); strcat(buf,msg); if (sendto(s, buf, BUFLEN, 0, &si_other, slen)==-1) Diep("sendto()"); } } close(s); return (0); } int main(void) { int pid; Se creeaz un proces fiu n care se va rula Client iar n printe se va rula Server pid = fork();

19
if (pid == 0) Client(); else Server(); } Captur de execuie:

EXECUTING: /root/Projects/Lab3/src/Lab3 ---------------------------------------------IP: 192.168.174.1 Netmask: 255.255.255.0 Broadcast: 192.168.174.255 Broadcast: 192.168.174.255 JOIN NAME: IONUT
Primul mesaj JOIN afiat de Server

192.168.174.1 (IONUT) Mesaj (LEAVE pentru deinregistrare): Test1


Mesaj MESSAGE afiat de Server

IONUT:Test1 Mesaj (LEAVE pentru deinregistrare): Test2

20

IONUT:Test2 Mesaj (LEAVE pentru deinregistrare):


Primul mesaj JOIN trimis de alarm (are ca efect n Server afiarea clienilor nregistrai)...

192.168.174.1 (IONUT) Mesaj (LEAVE pentru deinregistrare):


Al doilea mesaj JOIN trimis de alarm...

192.168.174.1 (IONUT)
Mesaj LEAVE prin care clientul se denregistreaz

Mesaj (LEAVE pentru deinregistrare): LEAVE Comenzi pentru a activa broadcast-ul (fiierul net):
rcSuSEfirewall2 stop echo "0" >/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts sysctl net.ipv4.icmp_echo_ignore_broadcasts

S-ar putea să vă placă și