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— δημιουργεί την υποδοχή διακομιστή την οποία περιμένει ηacceptbind— προσαρτά την υποδοχή διακομιστή σε τοπική διεύθυνση πριν τιςlistenκαιacceptlisten— υποχρεωτικό βήμα μεταξύ τηςbindκαι τηςaccept· καθορίζει το βάθος της ουράς εκκρεμοτήτωνconnect— το αντίστοιχο της πλευράς πελάτη· ηacceptεπιστρέφει τη διεύθυνση που έδωσε μια κλήσηconnectgetpeername— ανακτά την ίδια πακεταρισμένη διεύθυνση αργότερα από το ίδιο τοNEWSOCKETSocket— οι σταθερές (PF_INET,SOCK_STREAM,INADDR_ANY) και οι packers (sockaddr_in,unpack_sockaddr_in) που μετατρέπουν την πακεταρισμένη τιμή επιστροφής σε κάτι αξιοποιήσιμο$^F— κατώφλι που ελέγχει το αν ηacceptθέτειFD_CLOEXECστον νέο περιγραφέα