Υποδοχές (sockets)

accept#

Αποδοχή εισερχόμενης σύνδεσης σε μια υποδοχή που ακούει.

Η accept αποτελεί τη διακομιστική πλευρά της χειραψίας σύνδεσης. Περιμένει στο GENERICSOCKET — μια υποδοχή που έχει ήδη δημιουργηθεί με socket, συνδεθεί με bind και τεθεί σε κατάσταση ακρόασης με listen — μέχρι να συνδεθεί ένας πελάτης, οπότε εγκαθιστά μια ολοκαίνουρια συνδεδεμένη υποδοχή στο NEWSOCKET και επιστρέφει την πακεταρισμένη διεύθυνση του πελάτη. Το GENERICSOCKET παραμένει σε κατάσταση ακρόασης, έτοιμο για την επόμενη κλήση. Η συμπεριφορά αντικατοπτρίζει την κλήση συστήματος POSIX accept(2).

Σύνοψη#

accept NEWSOCKET, GENERICSOCKET

Τι επιστρέφεται#

Η διεύθυνση του πελάτη στην πακεταρισμένη μορφή που χρησιμοποιείται από την οικογένεια διευθύνσεων του GENERICSOCKET — η ίδια μορφή που θα περνούσατε στη connect ή θα αποπακετάρατε με Socket::unpack_sockaddr_in για IPv4, ή με Socket::unpack_sockaddr_in6 / unpack_sockaddr_un για IPv6 και υποδοχές πεδίου Unix αντίστοιχα. Σε αποτυχία επιστρέφει ψευδή τιμή (την κενή συμβολοσειρά) και θέτει την $! στο υποκείμενο errno.

Το NEWSOCKET συμπληρώνεται ως παρενέργεια — είναι ένα απλό όρισμα filehandle, όχι τιμή επιστροφής. Μια bareword όπως το CLIENT αυτο-δημιουργεί ένα typeglob· ένα λεκτικό filehandle όπως το my $client συμπληρώνεται επιτόπου:

my $client;
accept($client, $server) or die "accept: $!";
# $client is now a readable/writable handle to the connected peer

Καθολική κατάσταση που επηρεάζει#

  • $! — τίθεται σε αποτυχία στο υποκείμενο errno (EINTR, EAGAIN/EWOULDBLOCK σε μη μπλοκαριστικές υποδοχές ακρόασης, ECONNABORTED, EMFILE, ENFILE).

  • $^F — ο μέγιστος περιγραφέας αρχείου του συστήματος. Σε συστήματα που υποστηρίζουν τη σημαία close-on-exec, η accept θέτει FD_CLOEXEC στον νέο περιγραφέα όταν ο αριθμός του είναι μεγαλύτερος από το $^F (προεπιλογή 2, καλύπτοντας τα τυπικά ρεύματα). Αυξήστε το $^F πριν την accept αν θέλετε η υποδοχή που έγινε αποδεκτή να επιβιώσει της exec.

Παραδείγματα#

Ελάχιστος βρόχος αποδοχής τύπου TCP echo:

use Socket;

socket(my $server, PF_INET, SOCK_STREAM, getprotobyname("tcp"))
    or die "socket: $!";
bind($server, sockaddr_in(8080, INADDR_ANY)) or die "bind: $!";
listen($server, SOMAXCONN)                   or die "listen: $!";

while (my $peer = accept(my $client, $server)) {
    my ($port, $iaddr) = sockaddr_in($peer);
    print $client "hello ", inet_ntoa($iaddr), ":$port\n";
    close $client;
}
die "accept: $!";   # loop exits only on error

Αποπακετάρισμα της επιστρεφόμενης διεύθυνσης για καταγραφή:

use Socket;

my $peer = accept(my $client, $server)
    or die "accept: $!";
my ($port, $iaddr) = sockaddr_in($peer);
printf "connection from %s port %d\n", inet_ntoa($iaddr), $port;

Μη μπλοκαριστική accept. Θέστε πρώτα το $server σε μη μπλοκαριστική κατάσταση· μια κλήση χωρίς εκκρεμή σύνδεση επιστρέφει τότε ψευδή τιμή με την $! σε EAGAIN ή EWOULDBLOCK:

use Errno qw(EAGAIN EWOULDBLOCK);
use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);

my $flags = fcntl($server, F_GETFL, 0);
fcntl($server, F_SETFL, $flags | O_NONBLOCK);

my $peer = accept(my $client, $server);
if (!$peer) {
    if ($! == EAGAIN || $! == EWOULDBLOCK) {
        # no pending connection — try again later
    } else {
        die "accept: $!";
    }
}

Διατηρήστε την αποδεκτή υποδοχή κατά τη διάρκεια της exec αυξάνοντας το $^F ώστε ο χρόνος εκτέλεσης να μη θέσει FD_CLOEXEC:

local $^F = 10_000;
accept(my $client, $server) or die "accept: $!";
exec "/usr/libexec/handler", fileno($client);

Οριακές περιπτώσεις#

  • Το GENERICSOCKET δεν ακούει — η accept αποτυγχάνει με την $! σε EINVAL. Καλέστε πρώτα την listen στην υποδοχή του διακομιστή.

  • Το NEWSOCKET είναι ήδη ανοιχτό — η υπάρχουσα λαβή κλείνει σιωπηλά πριν εγκατασταθεί ο νέος περιγραφέας, ακριβώς όπως στις open και socket. Αν η παλιά λαβή ήταν η μόνη αναφορά σε ένα ρεύμα με ενταμιευτή, τυχόν μη εκκενωμένα δεδομένα χάνονται. Κλείστε τη ρητά εκ των προτέρων όταν αυτό έχει σημασία.

  • Σήμα κατά την κλήση — ένα σήμα που παραδίδεται ενώ η accept είναι μπλοκαρισμένη κάνει την υποκείμενη κλήση συστήματος να επιστρέψει EINTR. Το pperl δεν επανεκκινεί αυτόματα: η accept επιστρέφει ψευδή τιμή και αφήνει την $! σε EINTR. Τυλίξτε σε βρόχο επανάληψης αν οι χειριστές σημάτων σας δεν τερματίζουν τη διεργασία.

  • Εξάντληση περιγραφέων — το EMFILE (όριο διεργασίας) ή το ENFILE (όριο συστήματος) σε έναν φορτωμένο διακομιστή είναι ο συνηθισμένος λόγος που η accept αρχίζει να αποτυγχάνει υπό φορτίο. Αντιμετωπίστε τα ως παροδικά· κλείστε αδρανείς πελάτες και ξαναδοκιμάστε.

  • Αναντιστοιχία οικογένειας διευθύνσεων στην πακεταρισμένη επιστροφή — ο πακεταρισμένος ενταμιευτής ακολουθεί την οικογένεια του GENERICSOCKET. Το αποπακετάρισμα μιας διεύθυνσης πεδίου Unix με Socket::unpack_sockaddr_in παράγει σκουπίδια. Παρακολουθήστε εσείς την οικογένεια ή χρησιμοποιήστε την Socket::sockaddr_family για να γίνει η αποστολή.

  • Οι παρενθέσεις είναι προαιρετικές, τα κόμματα όχι — τα accept $c, $s και accept($c, $s) λειτουργούν και τα δύο· το accept $c $s είναι συντακτικό σφάλμα.

  • Περιβάλλον λίστας έναντι βαθμωτού — η πακεταρισμένη διεύθυνση επιστροφής είναι συμβολοσειρά bytes· το περιβάλλον λίστας δεν την επεκτείνει σε (port, addr). Καλέστε την sockaddr_in / αντίστοιχη για αποπακετάρισμα.

Διαφορές από το upstream#

Πλήρως συμβατό με το upstream Perl 5.42.

Δείτε επίσης#

  • socket — δημιουργεί την υποδοχή διακομιστή την οποία περιμένει η accept

  • bind — προσαρτά την υποδοχή διακομιστή σε τοπική διεύθυνση πριν τις listen και accept

  • listen — υποχρεωτικό βήμα μεταξύ της bind και της accept· καθορίζει το βάθος της ουράς εκκρεμοτήτων

  • connect — το αντίστοιχο της πλευράς πελάτη· η accept επιστρέφει τη διεύθυνση που έδωσε μια κλήση connect

  • getpeername — ανακτά την ίδια πακεταρισμένη διεύθυνση αργότερα από το ίδιο το NEWSOCKET

  • Socket — οι σταθερές (PF_INET, SOCK_STREAM, INADDR_ANY) και οι packers (sockaddr_in, unpack_sockaddr_in) που μετατρέπουν την πακεταρισμένη τιμή επιστροφής σε κάτι αξιοποιήσιμο

  • $^F — κατώφλι που ελέγχει το αν η accept θέτει FD_CLOEXEC στον νέο περιγραφέα