Προγραμματισμός Socket σε C++

Programmatismos Socket Se C



Ο προγραμματισμός υποδοχών έχει γίνει ένα σημαντικό θέμα στον τομέα της δικτύωσης υπολογιστών. Περιλαμβάνει τη δημιουργία μιας σύνδεσης μεταξύ δύο κόμβων, διακομιστή και πελάτη για να επικοινωνούν μεταξύ τους χωρίς καμία διακοπή. Ο διακομιστής λειτουργεί ως ακροατής στο κανάλι επικοινωνίας και ακούει τον πελάτη σε μια συγκεκριμένη θύρα σε μια διεύθυνση IP. Από την άλλη πλευρά, ο πελάτης ενεργεί ως επικοινωνιακός στο κανάλι επικοινωνίας. Ο πελάτης επικοινωνεί με τον διακομιστή για να δημιουργήσει μια σύνδεση και να κάνει μια επαφή με τον διακομιστή. Αυτό το άρθρο στοχεύει να παρέχει έναν περιεκτικό και λεπτομερή οδηγό για τον προγραμματισμό υποδοχών σε C++, που καλύπτει τα βασικά, παρουσιάζοντας πρακτικά παραδείγματα και παρέχει λεπτομερή επεξήγηση του κώδικα.

Καθιέρωση του μοντέλου πελάτη-διακομιστή

Ο προγραμματισμός υποδοχών είναι η διαδικασία που δημιουργεί ένα κανάλι επικοινωνίας μεταξύ του διακομιστή και του πελάτη χρησιμοποιώντας υποδοχές. Στο ακόλουθο παράδειγμα κώδικα, ο υπολογιστής-πελάτης ξεκινά μια επαφή με το διακομιστή και ο διακομιστής έχει ρυθμιστεί ώστε να δέχεται τις συνδέσεις πελάτη. Ας κατανοήσουμε τα τμήματα κώδικα διακομιστή και πελάτη, επιδεικνύοντας τον πυρήνα τους που λειτουργεί εντός της επικοινωνίας δικτύου. Ακολουθεί ο κώδικας από την πλευρά του διακομιστή. Ας δούμε πρώτα τον κώδικα και μετά ας εξηγήσουμε τον κώδικα αναλυτικά, σημείο προς σημείο.

1. Πλευρά διακομιστή







Ο κώδικας για την πλευρά του διακομιστή του μοντέλου δίνεται παρακάτω. Ας δούμε τι συμβαίνει στον κώδικα:



#include
#include
#include
#include

χρησιμοποιώντας χώρο ονομάτων std ;

#define PORT 8080
#define MAX_BUF_SIZE 1024

ενθ κύριος ( ) {
ενθ ser_socket, cli_socket ;
struct sockaddr_in ser_address, cli_address ;
απανθρακώνω buf [ MAX_BUF_SIZE ] = { 0 } ;

αν ( ( ser_socket = πρίζα ( AF_INET, SOCK_STREAM, 0 ) ) == - 1 ) {
λάθη ( 'Σφάλμα στη δημιουργία του Socket' ) ;
έξοδος ( EXIT_FAILURE ) ;
}

ser_address. αμαρτία_οικογένεια = OF_INET ;
ser_address. sin_addr . s_addr = INADDR_ANY ;
ser_address. sin_port = htons ( ΛΙΜΑΝΙ ) ;

αν ( δένω ( be_socket, ( struct sockaddr * ) & ser_address, μέγεθος του ( ser_address ) ) == - 1 ) {
λάθη ( 'Αποτυχία στη δέσμευση' ) ;
έξοδος ( EXIT_FAILURE ) ;
}

αν ( ακούω ( be_socket, 3 ) == - 1 ) {
λάθη ( 'Απέτυχε να ακούσω' ) ;
έξοδος ( EXIT_FAILURE ) ;
}

cout << 'Ακρόαση διακομιστή στη θύρα' << ΛΙΜΑΝΙ << '... \n ' ;

socklen_t cli_address_len = μέγεθος του ( cli_address ) ;
αν ( ( cli_socket = αποδέχομαι ( be_socket, ( struct sockaddr * ) & cli_address, & cli_address_len ) ) == - 1 ) {
λάθη ( 'Απέτυχε η αποδοχή' ) ;
έξοδος ( EXIT_FAILURE ) ;
}

ανάγνωση ( cli_socket, buf, MAX_BUF_SIZE ) ;
cout << 'Το μήνυμα του πελάτη είναι: ' << buf << endl ;

στείλετε ( cli_socket, 'Μήνυμα διακομιστή' , strlen ( 'Μήνυμα διακομιστή' ) , 0 ) ;

Κλείσε ( cli_socket ) ;
Κλείσε ( ser_socket ) ;

ΕΠΙΣΤΡΟΦΗ 0 ;
}

Το παράδειγμα που δίνεται είναι ο κώδικας διακομιστή του προγράμματος C++. Αυτός ο κώδικας λειτουργεί για έναν απλό διακομιστή TCP που ακούει για συνδέσεις σε μια συγκεκριμένη θύρα. Όταν μια σύνδεση δημιουργηθεί με επιτυχία, ο διακομιστής θα λάβει ένα μήνυμα που αποστέλλεται από τον πελάτη. Μετά από αυτό, το εκτυπώνει στην κονσόλα και στέλνει ένα μήνυμα απάντησης στον πελάτη. Ας κατανοήσουμε κάθε γραμμή κώδικα.



Το πρόγραμμα ξεκινά με τη συμπερίληψη των βιβλιοθηκών: «iostream» για τυπικούς ορισμούς εισόδου/εξόδου, «cstring» για συναρτήσεις χειρισμού συμβολοσειρών, «unistd.h» για παροχή πρόσβασης στο API του λειτουργικού συστήματος POSIX και «arpa/inet.h» σε εκτελέστε τις λειτουργίες του Διαδικτύου. Η δήλωση '#define PORT 8080' σημαίνει ότι ορίζει τον αριθμό θύρας 8080 στην οποία θα ακούσει ο διακομιστής. Το '#define MAX_BUF_SIZE 1024' σημαίνει το μέγιστο μέγεθος buffer για τα εισερχόμενα δεδομένα που είναι 1024.





Στην κύρια συνάρτηση, αρχικοποιούνται δύο μεταβλητές, 'ser_socket' και 'cli_socket', για να αντιπροσωπεύουν και τον διακομιστή και τον πελάτη, αντίστοιχα. Οι άλλες τρεις μεταβλητές που είναι 'sockaddr_in', 'ser_address' και 'cli_address' τύπου 'struct' αρχικοποιούνται ως δομές διεύθυνσης για τον διακομιστή και τον πελάτη. Μετά από αυτό, αρχικοποιείται ένα buffer με το όνομα 'buf' το οποίο αποθηκεύει τα δεδομένα που προέρχονται από τον πελάτη.

Η συνάρτηση socket() στη συνθήκη 'if' δημιουργεί μια νέα υποδοχή TCP. Το AF_INET υποδηλώνει IPv4, το SOCK_STREAM αντιπροσωπεύει την προσανατολισμένη στη σύνδεση και αξιόπιστη υποδοχή TCP, το τελευταίο όρισμα που είναι 0 δίνεται για την επιλογή του προεπιλεγμένου πρωτοκόλλου TCP, το INADDR_ANY δέχεται τις συνδέσεις σε οποιαδήποτε διεύθυνση IP και τα htons (PORT) μετατρέπουν τον αριθμό θύρας από το σειρά byte κεντρικού υπολογιστή στη σειρά byte δικτύου.



Εφόσον όλα έχουν οριστεί σωστά, το επόμενο βήμα είναι να ρυθμίσετε τον διακομιστή ως λίστα στη δεδομένη θύρα και να αποδεχτείτε τις συνδέσεις σε οποιαδήποτε διεπαφή δικτύου. Το socket δίνεται με τις πληροφορίες στο 'ser_address' με τη μέθοδο bind(). Εκτυπώνουμε ένα σφάλμα και τερματίζουμε τη διαδικασία εάν αποτύχει η σύνδεση. Η συνάρτηση accept() ανοίγει μια νέα υποδοχή για τη σύνδεση με τον πελάτη, ενώ η συνάρτηση listen() δίνει εντολή στον διακομιστή να περιμένει τις εισερχόμενες συνδέσεις. Εάν η συνάρτηση accept() αποτύχει, εκτυπώνεται το μήνυμα σφάλματος και η συνάρτηση θα βγει.

Στη συνέχεια, ο διακομιστής διαβάζει το μήνυμα πελάτη με τη συνάρτηση read() στην προσωρινή μνήμη 'buf' και στη συνέχεια το εκτυπώνει στην κονσόλα. Η συνάρτηση send() χρησιμοποιείται από τον διακομιστή για να στείλει ένα μήνυμα ως απάντηση στον πελάτη. Τέλος, χρησιμοποιώντας την close(), ο διακομιστής κλείνει την υποδοχή του πελάτη, τερματίζοντας το πρόγραμμα έτσι ώστε όλες οι συνδέσεις να κλείσουν σωστά και να μην υπάρχει πιθανότητα παραβίασης δεδομένων.

2. Πλευρά πελάτη

Τώρα, ας δούμε τι συμβαίνει στο μοντέλο πελάτη:

#include
#include
#include
#include

#define PORT 8080
#define SERVER_IP '127.0.0.1'

ενθ κύριος ( ) {
ενθ cli_socket ;
struct sockaddr_in ser_address ;
συνθ απανθρακώνω * μήνυμα = 'Ο πελάτης στέλνει χαιρετισμούς!' ;

αν ( ( cli_socket = πρίζα ( AF_INET, SOCK_STREAM, 0 ) ) == - 1 ) {
λάθη ( 'Σφάλμα στη δημιουργία πρίζας' ) ;
έξοδος ( EXIT_FAILURE ) ;
}

ser_address. αμαρτία_οικογένεια = OF_INET ;
ser_address. sin_port = htons ( ΛΙΜΑΝΙ ) ;

αν ( inet_pton ( AF_INET, SERVER_IP, & ser_address. sin_addr ) <= 0 ) {
λάθη ( 'Λάθος διεύθυνση' ) ;
έξοδος ( EXIT_FAILURE ) ;
}

αν ( συνδέω-συωδεομαι ( cli_socket, ( struct sockaddr * ) & ser_address, μέγεθος του ( ser_address ) ) == - 1 ) {
λάθη ( 'Αποτυχία σύνδεσης' ) ;
έξοδος ( EXIT_FAILURE ) ;
}
στείλετε ( cli_socket, mesg, strlen ( μήνυμα ) , 0 ) ;

απανθρακώνω buf [ 1024 ] = { 0 } ;
ανάγνωση ( cli_socket, buf, μέγεθος του ( buf ) ) ;
std :: cout << 'Απάντηση διακομιστή:' << buf << std :: endl ;

Κλείσε ( cli_socket ) ;
ΕΠΙΣΤΡΟΦΗ 0 ;
}

Ας δούμε κάθε γραμμή κώδικα για να κατανοήσουμε πώς λειτουργεί το πρόγραμμα.

Οι ίδιες τέσσερις βιβλιοθήκες – iostream, cstring, unistd.h και arpa/inet.h – περιλαμβάνονται επίσης στην πλευρά του πελάτη. Καθορίζεται επίσης ένας αριθμός θύρας μαζί με τη διεύθυνση IP του τοπικού κεντρικού υπολογιστή 127.0.0.1. Δίνεται το μήνυμα που πρέπει να παραδοθεί στον διακομιστή. Ο πελάτης και ο διακομιστής πρέπει να δημιουργήσουν μια σύνδεση ως το ακόλουθο βήμα:

Το 'if ((client_socket = socket(AF_INET, SOCK_STREAM, 0)) == -1);' δημιουργεί μια υποδοχή για IPv4 με τύπο ροής και το προεπιλεγμένο πρωτόκολλο TCP. Το perrror() εκτυπώνει τις λεπτομέρειες του σφάλματος εάν η συνάρτηση socket() αποτύχει να δημιουργήσει μια σύνδεση και βγαίνει από το πρόγραμμα.

Το 'server_address.sin_port = htons(PORT);' ορίζει τον αριθμό θύρας μετά τη μετατροπή στη σειρά byte δικτύου. Στη συνέχεια, ένα άλλο μήνυμα αποτυχίας που είναι η 'Λάθος διεύθυνση' δίνεται εδώ, το οποίο εκτυπώνεται εάν υπάρχει κάτι λάθος με τη διεύθυνση. Εντοπίζοντας τη διεύθυνση στο 'ser_address', ο πελάτης θα συνδεθεί στον διακομιστή. Εάν η σύνδεση αποτύχει, εκτυπώνονται οι λεπτομέρειες του σφάλματος. Η συνάρτηση send() θα μεταφέρει το μήνυμα στον διακομιστή, διασφαλίζοντας ότι δεν περιέχει καμία σημαία.

Για να λάβετε και να αποθηκεύσετε μια απάντηση από τον διακομιστή, αρχικοποιείται ένα buffer με το όνομα 'buf' τύπου 'char'. Η συνάρτηση read() διαβάζει την απόκριση του διακομιστή στο buffer. Τέλος, η απάντηση του διακομιστή εκτυπώνεται στην κονσόλα. Τέλος, η σύνδεση κλείνει χρησιμοποιώντας τη δήλωση close() για τον τερματισμό της πρίζας. Ακολουθεί η έξοδος του προγράμματος:

συμπέρασμα

Ο προγραμματισμός υποδοχών είναι ένα σημαντικό μέρος της δικτυακής επικοινωνίας στην επιστήμη των υπολογιστών. Επιτρέπει την ανάπτυξη εφαρμογών που μπορούν να επικοινωνούν μέσω του δικτύου, επιτρέποντας ένα ευρύ φάσμα δυνατοτήτων από απλές αρχιτεκτονικές πελάτη-διακομιστή έως δομημένα κατανεμημένα συστήματα. Όταν δημιουργείται μια υποδοχή σε ένα πλαίσιο προγραμματισμού, το πρόγραμμα πρέπει να διαμορφώσει τα χαρακτηριστικά του τελικού σημείου του, όπως τα πρωτόκολλα, το TCP ή το UDP και τη διεύθυνση δικτύου όπως τη διεύθυνση IP και τον αριθμό θύρας. Αυτές οι υποδοχές επιτρέπουν στους διακομιστές να στέλνουν και να λαμβάνουν τα δεδομένα. Αυτό το άρθρο παρουσιάζει ένα πρακτικό παράδειγμα σχετικά με τον τρόπο λειτουργίας του μοντέλου πελάτη-διακομιστή στον προγραμματισμό υποδοχών.