Programmazione socket in C++

Programmazione Socket In C



La programmazione dei socket è diventata un argomento importante nel campo delle reti di computer. Si tratta di stabilire una connessione tra due nodi, server e client per comunicare tra loro senza alcuna interruzione. Il server funge da ascoltatore nel canale di comunicazione e ascolta il client su una porta specifica in un indirizzo IP. D'altra parte, il cliente agisce come comunicatore nel canale di comunicazione. Il client contatta il server per creare una connessione e stabilire un contatto con il server. Questo articolo mira a fornire una guida completa e dettagliata alla programmazione dei socket in C++, coprendo le nozioni di base, presentando esempi pratici e fornendo una spiegazione dettagliata del codice.

Stabilire il modello client-server

La programmazione socket è il processo che crea un canale di comunicazione tra il server e il client utilizzando i socket. Nel seguente codice di esempio, il client avvia un contatto con il server e il server è configurato per accettare le connessioni client. Cerchiamo di comprendere i segmenti di codice server e client dimostrando il loro funzionamento principale all'interno della comunicazione di rete. Quello che segue è il codice lato server. Vediamo prima il codice e poi spieghiamolo nel dettaglio, punto per punto.

1. Lato server







Il codice per il lato server del modello è riportato di seguito. Vediamo cosa succede nel codice:



#include
#include
#include
#include

utilizzando spazio dei nomi standard ;

#definire PORTA 8080
#definisce MAX_BUF_SIZE 1024

int principale ( ) {
int ser_socket, cli_socket ;
struttura sockaddr_in indirizzo_ser, indirizzo_cli ;
car buf [ MAX_BUF_SIZE ] = { 0 } ;

Se ( ( ser_socket = PRESA ( AF_INET, SOCK_STREAM, 0 ) ) == - 1 ) {
errore ( 'Errore nella creazione del Socket' ) ;
Uscita ( EXIT_FAILURE ) ;
}

ser_indirizzo. peccato_famiglia = OF_INET ;
ser_indirizzo. sin_addr . s_ind = INADDR_ANY ;
ser_indirizzo. sin_porto = tonnellate ( PORTA ) ;

Se ( legamento ( essere_presa, ( struttura calzino * ) & indirizzo_ser, taglia di ( ser_indirizzo ) ) == - 1 ) {
errore ( 'Fallimento del legame' ) ;
Uscita ( EXIT_FAILURE ) ;
}

Se ( Ascoltare ( essere_presa, 3 ) == - 1 ) {
errore ( 'Impossibile ascoltare' ) ;
Uscita ( EXIT_FAILURE ) ;
}

cout << 'Server in ascolto sulla porta' << PORTA << '... \N ' ;

socklen_t cli_address_len = taglia di ( indirizzo_cli ) ;
Se ( ( cli_socket = accettare ( essere_presa, ( struttura calzino * ) & indirizzo_cli, & indirizzo_cli_len ) ) == - 1 ) {
errore ( 'Impossibile accettare' ) ;
Uscita ( EXIT_FAILURE ) ;
}

Leggere ( cli_socket, buf, MAX_BUF_SIZE ) ;
cout << 'Il messaggio del cliente è: ' << buf << fine ;

Inviare ( presa_cli, 'Messaggio del server' , strlen ( 'Messaggio del server' ) , 0 ) ;

vicino ( cli_socket ) ;
vicino ( ser_socket ) ;

ritorno 0 ;
}

L'esempio fornito è il codice lato server del programma C++. Questo codice funziona affinché un semplice server TCP ascolti le connessioni su una singola porta specifica. Quando una connessione viene creata correttamente, il server riceverà un messaggio inviato dal client. Successivamente, lo stampa sulla console e invia un messaggio di risposta al client. Cerchiamo di comprendere ogni riga di codice.



Il programma inizia includendo le librerie: 'iostream' per le definizioni di input/output standard, 'cstring' per le funzioni di gestione delle stringhe, 'unistd.h' per fornire l'accesso all'API del sistema operativo POSIX e 'arpa/inet.h' per eseguire le operazioni su Internet. L'istruzione '#define PORT 8080' significa che definisce il numero di porta 8080 su cui il server sarà in ascolto. '#define MAX_BUF_SIZE 1024' indica la dimensione massima del buffer per i dati in entrata che è 1024.





Nella funzione principale, vengono inizializzate due variabili, 'ser_socket' e 'cli_socket', per rappresentare rispettivamente sia il server che il client. Le altre tre variabili che sono 'sockaddr_in', 'ser_address' e 'cli_address' di tipo 'struct' vengono inizializzate come strutture di indirizzi per il server e il client. Successivamente, viene inizializzato un buffer denominato “buf” che memorizza i dati provenienti dal client.

La funzione socket() nella condizione 'if' crea un nuovo socket TCP. AF_INET denota IPv4, SOCK_STREAM rappresenta il socket TCP affidabile e orientato alla connessione, l'ultimo argomento che è 0 viene fornito per selezionare il protocollo TCP predefinito, INADDR_ANY accetta le connessioni su qualsiasi indirizzo IP e htons (PORT) converte il numero di porta dal ordine dei byte dell'host all'ordine dei byte della rete.



Poiché tutto è definito correttamente, il passo successivo è impostare il server come lister sulla porta specificata e accettare le connessioni su qualsiasi interfaccia di rete. Il socket viene fornito con le informazioni in “ser_address” dal metodo bind(). Stampiamo un errore e terminiamo il processo se l'associazione fallisce. La funzione accetta() apre un nuovo socket per la connessione con il client, mentre la funzione ascolta() indica al server di attendere le connessioni in entrata. Se la funzione accetta() fallisce, viene stampato il messaggio di errore e la funzione termina.

Successivamente, il server legge il messaggio del client con la funzione read() nel buffer “buf” e quindi lo stampa sulla console. La funzione send() viene utilizzata dal server per inviare un messaggio in risposta al client. Infine, utilizzando close(), il server chiude il socket del client, terminando il programma in modo che tutte le connessioni vengano chiuse correttamente e non vi sia alcuna probabilità di violazione dei dati.

2. Lato cliente

Ora vediamo cosa succede nel modello client:

#include
#include
#include
#include

#definire PORTA 8080
#define SERVER_IP '127.0.0.1'

int principale ( ) {
int cli_socket ;
struttura sockaddr_in ser_address ;
cost car * Messaggio = 'Il cliente sta inviando i saluti!' ;

Se ( ( cli_socket = PRESA ( AF_INET, SOCK_STREAM, 0 ) ) == - 1 ) {
errore ( 'Errore nella creazione del socket' ) ;
Uscita ( EXIT_FAILURE ) ;
}

ser_indirizzo. peccato_famiglia = OF_INET ;
ser_indirizzo. sin_porto = tonnellate ( PORTA ) ;

Se ( inet_pton ( AF_INET, IP_SERVER, & ser_indirizzo. sin_addr ) <= 0 ) {
errore ( 'Indirizzo sbagliato' ) ;
Uscita ( EXIT_FAILURE ) ;
}

Se ( Collegare ( presa_cli, ( struttura calzino * ) & indirizzo_ser, taglia di ( ser_indirizzo ) ) == - 1 ) {
errore ( 'Connessione fallita' ) ;
Uscita ( EXIT_FAILURE ) ;
}
Inviare ( cli_socket, messaggio, strlen ( Messaggio ) , 0 ) ;

car buf [ 1024 ] = { 0 } ;
Leggere ( cli_socket, buf, taglia di ( buf ) ) ;
standard :: cout << 'Risposta del server: ' << buf << standard :: fine ;

vicino ( cli_socket ) ;
ritorno 0 ;
}

Vediamo ogni riga di codice per capire come funziona il programma.

Le stesse quattro librerie – iostream, cstring, unistd.h e arpa/inet.h – sono incluse anche sul lato client. Viene definito anche un numero di porta insieme all'indirizzo IP dell'host locale 127.0.0.1. Viene fornito il messaggio che deve essere consegnato al server. Il client e il server devono stabilire una connessione come segue:

Il 'if ((client_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1);' crea un socket per IPv4 con un tipo di flusso e il protocollo predefinito TCP. Perror() stampa i dettagli dell'errore se la funzione socket() non riesce a stabilire una connessione ed esce dal programma.

Il 'server_address.sin_port = htons(PORT);' imposta il numero di porta dopo la conversione nell'ordine dei byte di rete. Successivamente, qui viene fornito un altro messaggio di errore che è 'Indirizzo sbagliato', che viene stampato se c'è qualcosa di sbagliato nell'indirizzo. Individuando l'indirizzo in “ser_address”, il client si connetterà al server. Se la connessione fallisce, vengono stampati i dettagli dell'errore. La funzione send() trasferirà il messaggio al server, assicurandosi che non contenga alcun flag.

Per ricevere e memorizzare una risposta dal server, viene inizializzato un buffer denominato 'buf' di tipo 'char'. La funzione read() legge la risposta del server nel buffer. Infine, la risposta del server viene stampata sulla console. Infine, la connessione viene chiusa utilizzando l'istruzione close() per terminare il socket. Quello che segue è l'output del programma:

Conclusione

La programmazione dei socket è una parte importante della comunicazione di rete in informatica. Consente lo sviluppo di applicazioni in grado di comunicare in rete, consentendo un'ampia gamma di possibilità, da semplici architetture client-server a sistemi distribuiti strutturati. Quando un socket viene creato in un contesto di programmazione, il programma deve configurare le sue caratteristiche endpoint come i protocolli, TCP o UDP, e l'indirizzo di rete come l'indirizzo IP e il numero di porta. Questi socket consentono ai server di inviare e ricevere i dati. Questo articolo mostra un esempio pratico di come funziona il modello client-server nella programmazione socket.