Un esempio di client server - STB1019/SkullOfSummer GitHub Wiki

Fonte il materiale disponibile all'interno della repository è una rielaborazione del lavoro realizzato da Daniel Scocco

Nozioni primitive

Non ci sarà alcuna spiegazione in merito al funzionamento dell'iterazione client-server, ne tanto meno ai protocolli utilizzati; quanto segue, permette di comprendere come implementare la logica in C.

Librerie

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

sys/socket.h

Fonte man (2 e 7) socket

Il modulo socket implementa la comunicazione tra due nodi finali (mittente/destinatario). Pertanto non è possibile interagire con gli intermediari. Il modulo mette a disposizione un descrittore rappresentetativo della comunicazione.

socket(int socket family,int socket type, int protocol)

In some documentation, you'll see mention of a mystical "PF_INET". This is a weird etherial beast that is rarely seen in nature, but I might as well clarify it a bit here. Once a long time ago, it was thought that maybe a address family (what the "AF" in "AF_INET" stands for) might support several protocols that were referenced by their protocol family (what the "PF" in "PF_INET" stands for). That didn't happen. Oh well. So the correct thing to do is to use AF_INET in your struct sockaddr_in and PF_INET in your call to socket(). But practically speaking, you can use AF_INET everywhere. cit. AP vs PF, sezione 4.1

Pertanto all'interno dello script non sarà presente alcun riferimento alla famiglia di protocolli (PF), ma solo alla famiglia degli indirizzi (AF).

Socket Family indica il dominio della comunicazione, ovvero quale famiglia di protocolli si debba usare tra quelli definiti nell'header del socket:

Nome Obiettivo
AF_INET IPv4
AF_INET6 IPv6

Per la lista completa si rimanda alla fonte

Socket type specifica la semantica della comunicazione, ovvero il tipo di socket che si vuole implementare:

Nome Modalità
SOCK_STREAM Connessione sequenziale, affidabile e basata su stream di byte, (non implica FULL DUPLEX, ma solo che destinatario e mittente possono comunicare con lo stesso socket).
SOCK_DGRAM Connessione a pacchetti
SOCK_SEQPACKET Connessione sequenziale, affidabile e basata sulla trasmissione di pacchetti di dimensione massima fissata; la lettura di ciascun pacchetto ricevuto, necessita di una propria chiamata di sistema.

Per la lista completa si rimanda alla fonte

Protocol indica quale protocollo specifico si debba utilizzare. Di norma, ad ogni tipologia di protocollo esiste una sola implementazione e ne consegue che di default si utilizzi il valore 0. Se fosse necessario esplicitare quale protocollo implementare, è possibile scegliere tra quelli proposti nel file /etc/protocols.

protocollo numero alias descrizione
hopopt 0 HOPOPT IPv6 Hop-by-Hop Option [RFC1883]
ipv6 41 IPv6 Internet Protocol, version 6
ipv6-route 43 IPv6-Route Routing Header for IPv6
ipv6-frag 44 IPv6-Frag Fragment Header for IPv6
ipv6-icmp 58 IPv6-ICMP ICMP for IPv6
ipv6-nonxt 59 IPv6-NoNxt No Next Header for IPv6
ipv6-opts 60 IPv6-Opts Destination Options for IPv6

Tipologie di protocollo IPv6 otteneibile con il comando cat /etc/protocols | grep IPv6

sa_family_t

Di norma è definito dall'header come unsigned integer, ed ha il solo compito di supportare sockaddr_storage per il casting dei puntatori.

netinet/in.h

Fonte Riferimento

Il modulo definisce le strutture per comunicare usando i protocolli internet (iniziale minuscola, ndr).

Per il nostro script sono necessarie le definizioni di sockaddr_in, sockaddr_storage e socket_t.

sockaddr_in

La struttura sockaddr_in è utilizzata per salvare la configurazione del socket; è doveroso assicurarsi la validità dei parametri inseriti, affinchè sia possibile istanziare la comunicazione nelle diverse fasi. Al suo interno sono definiti i seguenti attributi:

tipo nome attributo Desc
sa_family_t sin_family AF_INET, definita in sys/socket.h
in_port_t sin_port Numero della porta
struct in_addr sin_addr Indirizzo IP

sockaddr_storage

Riferimento

Definito nell'header del socket.h (o quanto meno questa è l'indicazione secondo lo standard), dovrebbe essere:

  • sufficientemente grande per contenere tutte le specifiche di protocollo supportate

  • risolvere le problematiche di allineamento dei dati inseriti, che affliggerebbe qualsiasi puntatore a questa struttura. Senza un accurato allineamento, ogni puntatore dovrebbe puntare a differenti byte di memoria, a seconda di quale specifica di protocollo viene utilizzata.

sockaddr_storage dovrebbe contenere strutture di tipo sa_family_t e ss_family. Se sockaddr_storage viene utilizzato con un cast come sockaddr, l'attributo di tipo ss_family dovrebbe contenere le informazioni che consentono di mappare il sockaddr ricevuto nell'attributo di tipo sa_family. In alternativa, quando sockaddr_storage viene utilizzato con un cast della specifica del protocollo, l'attributo di tipo ss_family contiene le informazioni per mappare secondo la specifica desiderata, nella struttura di tipo sa_family_t.

disponibile nella repository

Server

  /****************** SERVER CODE ****************/
  /*Ref. http://www.programminglogic.com/example-of-client-server-program-in-c-using-sockets-and-tcp/*/

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

  #ifndef PARAM_BUFFER_SIZE
  #define PARAM_BUFFER_SIZE 1024
  #endif

  #ifndef PARAM_MESSAGE
  #define PARAM_MESSAGE "Hello World\n"
  #endif

  #ifndef PARAM_SERVER_ADDRESS
  #define PARAM_SERVER_ADDRESS "127.0.0.1"
  #endif

  #ifndef PARAM_SERVER_PORT
  #define PARAM_SERVER_PORT 7891
  #endif

  #ifndef PARAM_SOCKET_LIMIT
  #define PARAM_SOCKET_LIMIT 5
  #endif


int main(){
  int welcomeSocket, newSocket;
  char buffer[PARAM_BUFFER_SIZE];
  struct sockaddr_in serverAddr;
  struct sockaddr_storage serverStorage;
  socklen_t addr_size;

    /*---- Create the socket. The three arguments are: ----*/
    /* 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) */
  welcomeSocket = socket(AF_INET, SOCK_STREAM, 0);

    /*---- Configure settings of the server address struct ----*/
    /* Address family = Internet */
  serverAddr.sin_family = AF_INET;
    /* Set port number, using htons function to use proper byte order */
  serverAddr.sin_port = htons(PARAM_SERVER_PORT);
    /* Set IP address to localhost */
  serverAddr.sin_addr.s_addr = inet_addr(PARAM_SERVER_ADDRESS);
    /* Set all bits of the padding field to 0 */
  memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);  

    /*---- Bind the address struct to the socket ----*/
  bind(welcomeSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));

    /*---- Listen on the socket, with 5 max connection requests queued ----*/
  if(listen(welcomeSocket,PARAM_SOCKET_LIMIT)==0)
    printf("Listening\n%s:%d\n",PARAM_SERVER_ADDRESS,PARAM_SERVER_PORT);
  else
    printf("Error\n");

    /*---- Accept call creates a new socket for the incoming connection ----*/
  addr_size = sizeof serverStorage;
  newSocket = accept(welcomeSocket, (struct sockaddr *) &serverStorage, &addr_size);

    /*---- Send message to the socket of the incoming connection ----*/
  strcpy(buffer,PARAM_MESSAGE);
  send(newSocket,buffer,strlen(PARAM_MESSAGE),0);

  return 0;
}

Client

  /****************** CLIENT CODE ****************/
  /*Ref. http://www.programminglogic.com/example-of-client-server-program-in-c-using-sockets-and-tcp/*/

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

  #ifndef PARAM_BUFFER_SIZE
  #define PARAM_BUFFER_SIZE 1024
  #endif

  #ifndef PARAM_CLIENT_ADDRESS
  #define PARAM_CLIENT_ADDRESS "127.0.0.1"
  #endif

  #ifndef PARAM_CLIENT_PORT
  #define PARAM_CLIENT_PORT 7891
  #endif

  int main(){
    int clientSocket;
    char buffer[PARAM_BUFFER_SIZE];
    struct sockaddr_in serverAddr;
    socklen_t addr_size;

    /*---- Create the socket. The three arguments are: ----*/
    /* 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) */
    clientSocket = socket(PF_INET, SOCK_STREAM, 0);
    
    /*---- Configure settings of the server address struct ----*/
    /* Address family = Internet */
    serverAddr.sin_family = AF_INET;
    /* Set port number, using htons function to use proper byte order */
    serverAddr.sin_port = htons(PARAM_CLIENT_PORT);
    /* Set IP address to localhost */
    serverAddr.sin_addr.s_addr = inet_addr(PARAM_CLIENT_ADDRESS);
    /* Set all bits of the padding field to 0 */
    memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);  

    /*---- Connect the socket to the server using the address struct ----*/
    addr_size = sizeof serverAddr;
    connect(clientSocket, (struct sockaddr *) &serverAddr, addr_size);

    /*---- Read the message from the server into the buffer ----*/
    recv(clientSocket, buffer, PARAM_BUFFER_SIZE, 0);

    /*---- Print the received message ----*/
    printf("Data received: %s",buffer);   

    return 0;
  }
⚠️ **GitHub.com Fallback** ⚠️