Reţele de calculatoare

Conf. Dr. Carmen Timofte (Stanciu)

 

 

Tema 2. Programare pe socket

Comunicaţie client-server la nivel de socket

 

-       Documentaţie

-       Exemple

  1. Introducere
  2. Interfaţa Socket API
  3. Socket-uri în Unix

o   Comunicaţie client-server pe TCP/IP

o   Comunicaţie client-server pe UDP/IP

 

1.    Introducere

 

-       protocoalele TPC/IP de nivel transport oferă servicii ce permit programelor nivelului aplicaţie să comunice între ele prin intermediul mesajelor;

-       când o aplicaţie trimite o cerere către nivelul transport  pentru a trimite un mesaj, protocolul folosit la acest nivel:

o    împarte informaţia în pachete

o   adaugă un antet de pachet care include adresa destinaţiei

o   trimite informaţia nivelului reţea pentru procesare ulterioară

-       transmisa şi recepţia datelor se realizează prin intermediul unor porturi de pe server, care identifică destinaţia specifică a mesajului;

-       nivelul transport este implementat în reţelele TCP/IP prin intermediul a două protocoale:

o   UDP (User Datagram Protocol)–protocol datagramă utilizator –

§  Asigură servicii de tip datagramă nivelului aplicaţie;

§  Nu este fiabil (nu asigură certitudinea livrării datagramelor, nici mecanismele de protecţie la pierderea sau duplicarea datagramelor);

§  Viteză mare de transmisie;

§  Este un serviciu fără conexiune (emiţătorul nu cunoaşte starea receptorului în momentul transmisiei);

§  Pt. transferul datelor foloseşte nişte entităţi abstracte, numite porturi de protocol, identificate prin numere întregi pozitive şi care au asociate nişte cozi de mesaje prin care se transmit mesajele;

§  Se utilizează pt. mesaje mici (sub 8KB) cu viteză mare;

§  Antetul datagramei UDP conţine:

·       Source Port Number- adresa portului sursă;

·       Destination Port Number – adresa portului destinaţie;

·       Lengthlunginea datagramei în bytes;

·       Checksum – suma de control asociată datagramei (foloseşte acelaşi algoritm ca la protocolul IP)

o   TCP (Transmission Control Protocol)- protocol de control al transmisiei

§  Este fiabil (asigură integritatea datelor transmise, mecanisme de protecţie la pierderea sau duplicarea pachetelor, păstrarea numărului de secvenţă, mecanisme de control al fluxului de date în reţea);

§  Asigură transmisia blocurilor continue de date între porturile de protocol asociate aplicaţiilor;

§  Dimensiunea mesajelor nu este limitată;

§  Viteza de transfer mai mică;

§  Este un serviciu cu conexiune (emiţătorul cunoaşte starea receptorului în momentul transmisiei);

 

-       SO oferă programelor la nivel aplicaţie o interfaţă comună pt. aceste 2 protocoale, şi anume interfaţa socket.

 

 

2.    Interfaţa Socket API

Este o interfaţă între un program de aplicaţie şi serviciul de transport (este un standard de facto), fiind furnizat de o bibliotecă socket sau de sistemul de operare. Se foloseşte conceptul de descriptor, fiecare socket fiind tratat asemănător cu un fişier local. Acest descriptor este transmis aplicaţiei la crearea socket-ului şi apoi este utilizat ca argument în apelurile următoare

Primitive de serviciu Socket API

Descriptor = socket(protofamily, type, protocol)

creează un socket

close(socket)

închide un socket

bind(socket, localaddr, addrlen)

leagă socket-ul cu un port

listen(socket,queuesize)

pune socket în mod pasiv

newsock = accept(socket, caddress, caddresslen)

acceptă o cerere de conectare

connect(socket, saddress, saddresslen)

stabileşte legătura cu un server care a făcut accept

send(socket, data, length, flags)

transmite un mesaj

sendto(socket, length, flags, destaddress, addresslen)

transmite un mesaj folosind un socket neconectat

sendmsg(socket, msgstruct, flags)

 

recv(socket, buffer, length, flags)

primeşte un mesaj

recvfrom(socket, buffer, length, flags, sndaddr, saddrlen)

primeşte un mesaj pe un socket neconectat

rcvmsg(socket, msgstruct, flags)

 

Exemplu

În acest exemplu, serverul păstrează evidenţa numărului de clienţi care au accesat resursa şi raportează acest număr la fiecare apel.

f25_1

  Exemplu de folosire a interfeţei sockets.


 

Clientul apelează:

¨   gethostbyname pentru a converti numele unui calculator în adresa IP

¨   getprotobyname pentru a converti numele unui protocol în forma binară folosită de sockets

¨   socket pentru a crea un socket

¨   connect pentru a conecta socket-ul la un server

¨   recv (în mod repetat) pentru transferul tuturor datelor de la server (clientul nu are de unde şti dacă serverul transmite datele ăntr-un singur mesaj sau în mai multe)

¨   close pentru a închide socket-ul.

 

Serverul apelează:

¨   getprotobyname pentru a converti numele unui protocol în forma binară folosită de sockets

¨   socket pentru a crea un socket

¨   bind pentru a specifica portul local pentru socket

¨   listen pentru a plasa socket-ul în modul pasiv, apoi în buclă

¨   accept pentru a ccepta o cerere de conectare şi a crea un socket nou poentru această conexiune

¨   send pentru a trimite date

¨   close pentru a închide noul socket.

Primitive blocante folosite sunt accept, connect (3 way handshake) şi gethostbyname (conectare cu un server de nume).

 

 

 

3.     Socket-uri în Unix

      inovaţie a sistemului  Berkeley UNIX;

      este un punct de comunicaţie prin care un proces poate emite sau recepţiona informaţie sub forma unui flux de bytes;

      este identificat printr-un descriptor, asemănător cu cel pentru fişier

      realizează următoarele operaţii elementare:

o   conectarea la staţia de la distanţă

o   emiterea datelor

o   recepţionarea datelor

o   închiderea unei conexiuni

o   ataşarea la un port

o   aşteptarea cererilor de conexiune emise de staţiile de la distanţă

o   acceptarea cererilor de conexiune la portul local

 

O aplicaţie de reţea include:

-       un program client – care creează un socket pt. a iniţia o conexiune cu o aplicaţie server

-       un program server – care aşteaptă preluarea cererilor clienţilor

 

Comunicaţie client-server prin TCP/IP (socket de tip stream)

 

/* Modul SERVER TCP */

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <stdio.h>

#include <errno.h>

#include <netdb.h>

 

main(int argc, char ** argv)

{

unsigned short port; /* portul client */

char buffer[32];

struct sockaddr_in client, server; /*adresele client, server*/

struct hostent * srvinfo;

int s, namelen, ns;

if(argc!= 2)

{          

            printf("Apel: %s port \n", argv[0]);

            exit(1);

}

 

/*preia numele calculatorului local */

if(gethostname(buffer, 32))

            perror("Eroare la preluarea numelui serverului");

printf("A preluat numele serverului.... \n");

 

printf("Nume server: %s \n", buffer);

 

 

/*preia informatiile despre configurarea hostului*/

if (!(srvinfo=gethostbyname(buffer)))

            perror("Eroare la preluarea informatiilor despre server");

printf("A preluat informatiile despre configurarea hostului.... \n");

port=(unsigned short) atoi(argv[1]);

 

 

/*Creeaza un socket*/

if ((s=socket(AF_INET, SOCK_STREAM, 0)) <0)

{

            perror("Eroare la creare socket");

            exit(3);

}

printf("A creat socketul.... \n");

 

/*Asociaza socketului o adresa de server */

memset(&server, 0, sizeof(server));

server.sin_family = AF_INET;  /*AF_UNIX, AF_INET*/

server.sin_port = htons(port);

server.sin_addr.s_addr = inet_addr("193.226.62.247"); /* adresa Internet locala */

printf("A asociat socketului o adresa de serv.... \n");

 

 

if ( bind ( s, &server, sizeof(server)) <0)

{

            perror("Eroare la obtinerea adresei");

            exit(4);

}

printf("A facut bind.... \n");

 

/*Creeaza coada pentru cererile de conexiune; va prelua maxim 5 cereri de conexiune de la clienti, restul fiind refuzate */

if (listen(s, 5) != 0)

{

            perror("Eroare la obtinerea cozii de cereri");

            exit(5);

}

printf("A facut listen.... \n");

 

 

/* Creeaza un descriptor de socket pt. comunicatia server-client */

namelen = sizeof(client);

if ((ns = accept( s, &client, &namelen)) == -1)

{

            perror("Eroare la acceptarea conexiunii");

            exit(6);

}

printf("A facut accept.... \n");

 

 

/* Receptioneaza mesajul de la client */

if(recv(ns, buffer, 32, 0) == -1)

{

            perror ("Eroare la receptie");

            exit(7);

            }

 

/* Afiseaza mesajul primit de la client */

printf("Mesaj receptionat de la client, pe TCP-gr...., Nume Prenume: %s \n", buffer);

 

/*Trasmite confirmarea clientului */

if (send(ns, buffer, strlen(buffer), 0) <0)

{

            perror("Eroare la transmisie");

            exit(8);

}

printf("A facut send.... \n");

 

 

endhostent(); /* inchide sesiunea TCP/IP */

close(ns);

close(s);

printf("A inchis socket 2 si1 ... \n");

 

exit(0);

}

 

/* Modul CLIENT TCP */

#include <stdio.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <errno.h>

#include <netdb.h>

 

main (int argc, char ** argv)

{

    unsigned short port; /* portul client, acelasi ca cel specificat in modulul server */

    char buffer[32];

    struct sockaddr_in server; /*adresa server*/

    struct hostent * hostnm; /*informatii server */

    int s;

                                                           

    if (argc != 3)

    {                                                      

          printf("Apel: %s hostname port \n", argv[0]);

          exit(1);

    }

 

/*preia numele calculatorului local */

    if (!(hostnm=(struct hostent *) gethostbyname(argv[1])))

                                                           

    {                                                      

            perror("Eroare la preluarea numelui serverului");

            exit(3);

    }

 

    printf("A preluat numele de server.... \n");

   

    port = (unsigned short)atoi(argv[2]);

    strcpy(buffer, "Mesaj socket,TCP-user.....");

 

    /*preia informatiile despre server*/

    server.sin_family = AF_INET; /*AF_UNIX, AF_INET*/

    server.sin_port = htons(port);

    server.sin_addr.s_addr = inet_addr("193.226.62.247"); /* aceeasi ca la server */

 

    printf("A preluat inf. despre server....\n");

   

    if ((s = socket(AF_INET,SOCK_STREAM,0)) < 0)

    {                                                      

            perror("Eroare la creare socket");

            exit(4);

    }

    printf("A creat socket-ul....\n");

 

    if (connect(s, &server, sizeof(server)) < 0)

    {

            perror("Eroare la obtinerea conexiunii");

           exit(5);

    }

    printf("A realizat conexiunea.....\n");

   

    /* Transmite mesajul serverului */

    if (send(s, buffer, strlen(buffer), 0) < 0)

    {

         perror("Eroare la transmisie");

        exit(6);

    }

    printf("A transmis mesajul.....\n");

   

    /* Confirmare de la server */

    if (recv(s, buffer, 32, 0) < 0)

    {

            perror("Eroare la receptie");

            exit(7);

    }

    printf("Confirmarea de la server a fost facuta.... \n");

 

    close(s);

    printf("A inchis socketul.... \n");

 

    exit(0);

}

Comunicaţie client-server prin UDP/IP (socket de tip datagram)

 

/* Modul SERVER UDP */

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <stdio.h>

#include <errno.h>

#include <netdb.h>

 

main()

{

char buffer[32];

struct sockaddr_in client, server; /*adresele client, server*/

int s, namelen;

 

/*Creeaza un socket*/

if ((s=socket(AF_INET, SOCK_DGRAM, 0)) <0)

{

            perror("Eroare la creare socket");

            exit(1);

}

printf("A creat socket-ul....\n");

 

/*Asociaza socketului o adresa de server */

memset(&server, 0, sizeof(server));

server.sin_family = AF_INET;  /*AF_UNIX, AF_INET*/

server.sin_port = 0; /* orice port */

server.sin_addr.s_addr = INADDR_ANY;

 

if ( bind( s, &server, sizeof(server)) <0)

{

            perror("Eroare la obtinerea adresei");

            exit(2);

}

 

printf("A facut bind .....\n");

 

/* Determina portul asignat*/

namelen = sizeof(server);

if (getsockname(s, (struct sockaddr *) &server, &namelen))

{

            perror("Eroare la determinarea portului");

            exit(3);

}

 

printf("\n Portul asignat este: %d\n", ntohs(server.sin_port));

 

namelen = sizeof(client);

 

/* Receptioneaza mesajul de la client */

/*

if (recvfrom (s, buffer, 32, 0,  &client, sizeof(client)) < 0)

*/

if (read(s,buffer,32)<0)

{

            perror ("Eroare la receptie");

            exit(4);

            }

printf("A receptionat mesajul de la client pe UDP, Grupa...., Nume Prenume.... \n");

 

/* Afiseaza mesajul primit de la client */

printf("\n Mesajul receptionat de la client este: %s \n", buffer);

 

/*printf("Parametrii client: \n Nume domeniu: %s \n \Port: %d \n Adresa \Internet: %d \n", (client.sin_family == AF_INET? "AF_INET":"AF_UNIX"), ntohs(client.sin_port), inet_ntoa(client.sin_addr));

 

printf("Parametrii server: \n Nume domeniu: %s \n \Port: %d \n Adresa \Internet: %d \n", (server.sin_family == AF_INET? "AF_INET":"AF_UNIX"), ntohs(server.sin_port), inet_ntoa(server.sin_addr)); */

 

close(s);

printf("A inchis socketul.... \n");

 

exit(0);

}          

 

/* Modul CLIENT UDP */

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <stdio.h>

#include <errno.h>

#include <netdb.h>

 

main(int argc, char ** argv)

{

            unsigned short port; /* portul client, acelasi ca cel specificat in modulul server */

            char buffer[32];

            struct sockaddr_in server; /*adresa server*/

            int s;

 

if(argc!= 2)

{          

            printf("Apel: %s port \n", argv[0]);

            exit(1);

}

 

 

port=(unsigned short) atoi(argv[1]);

strcpy(buffer, "Mesaj socket, UDP- user...");

 

/*preia informatiile despre server*/

server.sin_family = AF_INET;  /*AF_UNIX, AF_INET*/

server.sin_port = htons(port);

server.sin_addr.s_addr = inet_addr("193.226.62.247"); /* aceeasi ca la server */

 

if ((s=socket(AF_INET, SOCK_DGRAM, 0)) <0)

{

            perror("Eroare la creare socket");

            exit(2);

}

printf("A creat socketul.... \n");

 

 

/* Transmite mesajul serverului */

if(sendto(s, buffer, strlen(buffer)+1, 0, &server, sizeof(server)) < 0)

{

perror ("Eroare la transmisie");

            exit(3);

            }

printf("A transmis mesajul serverului.... \n");

 

close(s);

printf("A inchis socketul.... \n");

 

exit(0);

}