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
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.
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.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.
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
Di norma è definito dall'header come unsigned integer, ed ha il solo compito di supportare sockaddr_storage
per il casting dei puntatori.
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
.
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 |
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 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 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;
}