Copy Link
Add to Bookmark
Report

I socket con il C

eZine's profile picture
Published in 
bakunin
 · 15 Aug 2022

BAKUNIN
bakunin@meganmail.com

MEMBRO DEL TANK COMMANDOS
http://go.to/tankcommandos

Eccoci qui! Un argomento caldo caldo per chi vuole usare il C per comunicare con internet: i socket

PREMESSA PRIMA: Se non sai il C... esistono letture più interessanti.
Se non girate sotto linux... esistono letture molto più interessanti!

PREMESSA SECONDA (anche detta "protocolli"): Come spero voi sappiate, i computer comunicano con "la rete" attraverso l'uso di protocolli. Dentro i protocolli viaggiano dei "pacchetti" di informazioni.

Oggi vi insegnerò qualcosa sulla comunicazione tra CLIENT e SERVER e quindi sulla comunicazione dei protocolli TCP/IP.
Una comunicazione tra CLIENT e SERVER funge in questo modo:

  1. il client comunica dati al server
  2. il server elabora i dati e invia i risultati al client

Questo accade per ogni tipo di client e di server (BROWSER-->porta 80 del server; MIRC-->porta 6667; ecc...).


PREMESSA TERZA (anche detta "quello che non vi spiego"):
Se pensate che io vi spieghi:

  1. il funzionamento dei protocolli
  2. le strutture delle reti
  3. ecc...

---> CAMBIATE LETTURA <----

Io vi spiego solo

  1. COME CREARE UN CLIENT
  2. COME CREARE UN SERVER

ovviamente a livello BASE! Il resto... sarà tutto aggiunta!

-----------=============<<<<<<<<<<<INIZIO>>>>>>>>>>>=============-------------

BASI PER CREARE UN CLIENT

Per creare un client bisogna fare le seguenti operazioni:

  1. creare un socket
  2. regolare il socket con impostazioni tipo INDIRIZZO, PORTA
  3. connetterlo al server
  4. inviare e ricevere dati
  5. chiudere la connessione
  6. chiudere il socket

BASI PER CREARE UN SERVER

Per creare un server bisogna fare le seguenti operazioni:

  1. creare un socket in modalità "ascolto"
  2. regolare una porta
  3. definire il numero massimo di connessioni
  4. fargli attendere le connessioni
  5. associare un figlio ad ogni client che si connette
  6. si ritorna al 4


EX CLARO?

OK!
Incominciamo quindi dalla creazione del socket.
Avete in mette come si inizializza una variabile per un file?
Ecco... un socket è uguale!
Infatti basterà usare al posto di fopen si mette la parolina socket!
ESEMPIO:

\* Se per un file avreste fatto così: *\ 
int il_file;
il_file=fopen("\\ciao.txt","a");

\* Per il socket farete così *\

int il_socket;
il_socket=socket(PF_INET, SOCK_STREAM, 0);

ovviamente il contenuto delle parentesi è diverso... ma ora ve lo spiego!
Lo schema della variabile socket è questo:

int socket (int af, int type, int protocol);

ove

  • l'argomento af specifica il formato dell'indirizzo (PF_INET è quello per internet).
  • il type è il tipo di socket. Può essere:
    • SOCK_STREAM --> comunicazioni via TCP
    • SOCK_DGRAM --> comunicazioni via UDP
    • SOCK_RAW --> quando è il programma a costruire pacchetti

  • protocol è il tipo di protocollo da utilizzare. Io metto 0 perché così viene usato quello più congeniale in relazione al tipo di socket.

Abbiamo creato il nostro primo socket! Vi vedo già emozionati!

Il secondo punto della nostra scaletta era:

Regolare porte e simili

Per fare questo si usa una struttura apposita che è:

struct sockaddr { 
short sa_family;
char sa_data[14];
}

Ma dato che a noi interessano le comunicazioni in internet, la struttura per noi muterà così:

struct sockaddr_in { 
short sin_family;
short sin_port;
struct in_addr sin_addr;
}

ove la struttura in_addr è questa:

struct in_addr { 
long s_addr;
}

Vi risulterà chiaro tutto questo nell'esempio sottostante:

int il_socket; 

unsigned short porta = 25;
struct sockaddr_in address;

il_socket = socket (PF_INET, SOCK_STREAM, 0);

memset(&address,0,sizeof(address));
address.sin_family = AF_INET;
address.sin_port= htons((u_short)porta);

Da qui si vede anche che sin_family è sempre uguale a AF_INET che è il valore relativo agli indirizzi internet.

Ci sono forse delle paroline strane che non conoscete!
Mi sto riferendo a htons e a altre.
Cosa sono?

Detto in 2 parole, il vostro computer con processore INTER (tundì, tundìn [musichetta :) ]) ordina la trasmissione di byte dal meno importante al più importante.

Su internet avviene l'inverso. Se avete un Motorola 68000 non vi preoccupate, poichè questi fungono uguali ad internet, ma per gli Intel si devono usare dei comandi che limitano la possibilità di perdere dati durante il cambio di architettura.

Questi comandi sono 4:

  • long ntohl --> da network a host (casa tua) dati lunghi
  • long ntonl --> da host a netword dati lunghi
  • short ntohs --> da network a host dati corti
  • short ntons --> da host a netword dati corti

ad esempio si usa uno short per il numero della PORTA (2 soli byte), ma si usa uno long per l'indirizzo IP (4 byte).

Se non avete capito niente del funzionamento di questi comandi e dell'esempio prima... forse è meglio che ristudiate il C!

Prima di connettere il nostro nuovo socket vediamo come superare lo scoglio "NON HO L'IP NUMERICO!"

La struttura da utilizzare in questo caso è:

struct hostent *gethostbyname (char *name);

La struttura hostent contiene tutte queste belle informazioni:

struct hostent { 
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
}

quello che ci occorre sono i campi h_length (lunghezza dell'indirizzo nemerico) e h_addr (i byte dello stesso indirizzo).

Vediamo come diventa l'esempio di prima ora!

int il_socket; 

unsigned short porta = 25;
struct sockaddr_in address;
struct hostent *hp;

il_socket = socket (PF_INET, SOCK_STREAM, 0);


hp = gethostbyname("sever.a.tua.scelta.it");

memset(&address,0,sizeof(address));
memcopy((char *)&address.sin_addr, hp->h_addr,hp->h_length);
address.sin_family = AF_INET;
address.sin_port= htons((u_short)porta);

EX CLARO?

CONNETTIAMO!

Usiamo questo:

int connect (int il_socket, struct sockaddr *name, int namelen);

ove il_socket sappiamo già cos'è;
name è una struttura sockaddr_in
namelen è la lunghezza in byte della struttura name.

nel nostro esempio:

connect(il_socket, (struct sockaddr *) &address, sizeof(address))


INVIO DI DATI!

Esistono tantissime funzioni per far questo. Io prediligo read() e write() che solo le stesse per i file. Il loro uso è il solito:

int read(int socket, char *buffer, int numbyte);

e

int write(int socket, char *buffer, int numbyte);

ove buffer punta al buffer dei file e numbyte alla lunghezza dei byte.

Nel nostro esempio:

write(il_socket,"Messaggio",9);


CHIUDIAMO IL SOCKET

Semplicemente:

close(il_socket);

Ora, in chiusura dell'argomento CLIENT ... ci spariamo un bell'esempio!

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

void invia(int sock, char* messaggio) {
if (write(sock,messaggio,strlen(messaggio))<0) {
printf("Impossibile stabilire un contatto.\n");
exit(1);
}

printf("%s\n",messaggio);
return;
}


int main(int argc, char **argv)
{
int il_socket;

unsigned short porta = 25;
struct sockaddr_in address;
struct hostent *hp;

if(argc!=2) {
printf("L'uso corretto di questo programma è:\n%s <host>\n",argv[0]);
exit(1);
}

/*Creazione del socket*/

il_socket = socket(PF_INET, SOCK_STREAM, 0);
if (il_socket == -1) {
printf("Errore nella creazione del socket\n");
exit(1);
}

/*IP numerico*/

if ((hp = gethostbyname(argv[1])) == NULL) {
printf("Errore nell'IP alfanumerico\n");
exit(1);
}

/*Struttura sockaddr_in*/

memset(&address,0,sizeof(address));
memcpy((char *)&address.sin_addr,hp->h_addr,hp->h_length);
address.sin_family = AF_INET;
address.sin_port = htons((u_short)porta);

/*Connessione*/

if (connect(il_socket, (struct sockaddr *) &address,
sizeof(address)) == -1) {
printf("Errore nella connessione del socket\n");
exit(1);
}

/*Invio e ricezione*/


printf("\n----------------------------------\n");
invia(il_socket,"helo dafault");
invia(il_socket, "mail from:<bakunin@meganmail.com>");
invia(il_socket, "rcpt to:<billy@microsoft.com>");
invia(il_socket,"data");
invia(il_socket,"MESSAGGIO DI PROVA!!");
invia(il_socket,".");
invia(il_socket,"quit");

printf("\n----------------------------------\n\nComunicazione avvenuta con successo!\n\n----------------------------------\n\n");


/* Chiusura */

close(il_socket);

}

-------FINE------------

L'utilità di questo programma è assolutamente nulla! Il funzionamento mi sembra abbastanza chiaro!

Manda un'email anonima ad un billy@microsoft.com col mio indirizzo email come mittente fornendo un host che permetta l'invio di smtp anonimi.

Ora passiamo alle aggiunte necessarie per i socket.

Prima cosa il Blinding!

Al server sarà necessario ovviamente un collegamento ad una porta e basta! Nel senso che non se ne fa nulla di un collegamento ad un IP poichè il suo unico scopo è ricevere e non connettersi.

Quindi sarà una cosa tipo:

struct sockaddr_in *addr; 

addr = (struct sockaddr_in *)
malloc(sizeof(struct sockaddr_in));
addr->sin_family = AF_INET;
addr->sin_port=htons(porta);
addr->sin_addr.s_addr=htonl(INADDR_ANY);

Poi il reale BIND:

int bind(int il_socket, struct sockaddr *my_addr, int addrlen);

Come al solito, il_socket è il socket, mr_addr è la struttura sockaddr inizializzata e poi c'è la sua lunghezza.

Ma dato che un server può ricevere più connessioni (quando un sito è visitato da più persone :) ) allora bisogna dirgli quante connessioni fargli fare:

Questo attraverso la funzione Listen():

int listen(int il_socket, int backlog);

ove backlog è un numero :)

L'attesa di connessioni si fa con la funzione accept:

int accept(int il_socket, struct sockaddr *addr, int *addrlen);

questo aspetta ... aspetta ... non fa continuare il prog ... continua
aspettare ... finché non compare una connessione!

Allora il prog va avanti!

Quanto arriva una connessione bisogna fare in modo che il socket si divida un 2:

  1. che aspetti altre connessioni (PADRE)
  2. che parli con il client (FIGLIO)

Preoccupiamoci prima del figlio:

if ((procid = fork()) == -1) { 
perror("fork()");
exit(104);
}
if ((int)procid == 0)
clientdialog(sclient);
else
close(sclient);

Il gioco è questo: Procid ci da il risultato di fork (che è la funzione che divide il socket). Se questo è 0 allora siamo con il figlio ... altrimenti siamo con il padre ... Se siamo con il padre ... questo può chiudere o tenere aperto il client.

VOLETE UN ESEMPIO?

Un bel SERVER:

-----INIZIO------------ 

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>

int CreaSocket(int Porta) {
int il_socket,errore;
struct sockaddr_in temp;

il_socket=socket(AF_INET,SOCK_STREAM,0);
temp.sin_family=AF_INET;
temp.sin_addr.s_addr=INADDR_ANY;
temp.sin_port=htons(Porta);

errore=fcntl(il_socket,F_SETFL,O_NONBLOCK);

errore=bind(il_socket,(struct sockaddr*) &temp,sizeof(temp));
errore=listen(il_socket,7);

return il_socket;
}

void ChiudiSocket(int sock) {
close(sock);
return;
}

int main() {
char buffer[512];
int DescrittoreSocket,NuovoSocket;
int exitCond=0;
int Quanti;

DescrittoreSocket=CreaSocket(1745);
printf("Sto attendendo: ...\n");
while (!exitCond) {
if ((NuovoSocket=accept(DescrittoreSocket,0,0)) !=-1) {
if ((Quanti=read(NuovoSocket,buffer,sizeof(buffer)))<0) {
printf("Impossibile comunicar.e\n");
ChiudiSocket(NuovoSocket);
} else {
buffer[Quanti]=0;
if (strcmp(buffer,"exit")==0)
exitCond=1;
else printf("%s\n",buffer);
}
ChiudiSocket(NuovoSocket);
}
}
ChiudiSocket(DescrittoreSocket);
printf("Fine comunicazione\n");

return 0;
}

-------FINE-----------

Il rispettivo CLIENT:

------INIZIO-------- 

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

void ChiudiSocket(int sock) {
close (sock);
return;
}

int CreaSocket(char* Destinazione, int Porta) {
struct sockaddr_in temp;
struct hostent *h;
int il_socket;
int errore;

temp.sin_family=AF_INET;
temp.sin_port=htons(Porta);
h=gethostbyname(Destinazione);
if (h==0) {
printf("Connessione IP alfanumerico... fallito!\n");
exit(1);
}
bcopy(h->h_addr,&temp.sin_addr,h->h_length);
il_socket=socket(AF_INET,SOCK_STREAM,0);
errore=connect(il_socket,(struct sockaddr*) &temp, sizeof(temp));
return il_socket;
}

void SpedisciMessaggio (int sock, char* Messaggio) {
prinft("%s\n", Messaggio);
if (write(sock,Messaggio,strlen(Messaggio))<0) {
prinft("Impossibile comunicare col server\n");
ChiudiSocket(sock);
exit(1);
}
printf("Messaggio inviato!\n");
return;
}

int main(int argc,char* argv[]) {
int DescrittoreSocket;
DescrittoreSocket=CreaSocket("127.0.0.1",1745);

if ((argc==2)&&(strcmp(argv[1],"exit")==0))
SpedisciMessaggio(DescrittoreSocket,"exit");
else
SpedisciMessaggio(DescrittoreSocket,"Un messaggio... :)");

ChiudiSocket(DescrittoreSocket);
return 0;
}

------FINE-----------

Ciao a tutti :)

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT