Υποδοχές (sockets)

shutdown#

Τερματίζει τη μία κατεύθυνση μιας σύνδεσης υποδοχής, ή και τις δύο.

Η shutdown λέει στον πυρήνα να σταματήσει μια υποδοχή να κάνει περαιτέρω αναγνώσεις, περαιτέρω εγγραφές, ή και τα δύο - χωρίς να κλείσει τον περιγραφέα αρχείου. Σε αντίθεση με την close, η επίδραση είναι άμεση και διαδίδεται σε κάθε forked αντίγραφο του περιγραφέα, όχι μόνο σε αυτό της τρέχουσας διεργασίας. Το HOW έχει την ίδια σημασία με το όρισμα της κλήσης συστήματος shutdown(2).

Σύνοψη#

shutdown SOCKET, HOW

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

1 σε επιτυχία. Σε αποτυχία, undef αν το SOCKET δεν είναι έγκυρο filehandle, ή 0 με την $! ορισμένη για κάθε άλλο σφάλμα (η υποδοχή δεν είναι συνδεδεμένη, έχει ήδη τερματιστεί σε εκείνη την κατεύθυνση, κ.ο.κ.).

Δύο διακριτές επιστροφές αποτυχίας, οπότε μερικές φορές χρειάζεται τριμερής έλεγχος:

my $r = shutdown($sock, 1);
if    (!defined $r) { die "bad filehandle" }
elsif (!$r)         { die "shutdown failed: $!" }

Το όρισμα HOW#

Το HOW είναι μικρός ακέραιος που ταιριάζει με τις σταθερές POSIX SHUT_*:

  • 0 - σταματά την ανάγνωση. Ο ομότιμος μπορεί ακόμη να στέλνει, αλλά τα δεδομένα θα απορρίπτονται από τον πυρήνα και η read / sysread σε αυτό το άκρο θα επιστρέφει τέλος αρχείου.

  • 1 - σταματά την εγγραφή. Η έξοδος που είναι σε ουρά εκκενώνεται, και κατόπιν ο ομότιμος βλέπει τέλος αρχείου στο άκρο ανάγνωσής του. Η print / syswrite σε αυτό το άκρο θα αποτύχει και θα εγείρει SIGPIPE.

  • 2 - σταματά και τις δύο κατευθύνσεις. Ισοδύναμο με κλήση της shutdown με 0 και κατόπιν με 1.

Τα συμβολικά ονόματα SHUT_RD, SHUT_WR, SHUT_RDWR βρίσκονται στο Socket αν προτιμάτε ευανάγνωστο κώδικα:

use Socket qw(SHUT_WR);
shutdown $sock, SHUT_WR;

shutdown έναντι close#

Η close απορρίπτει τον περιγραφέα αρχείου μόνο στην τρέχουσα διεργασία. Άλλες διεργασίες που κληρονόμησαν τον περιγραφέα μέσω fork κρατούν τα αντίγραφά τους ανοιχτά, και ο ομότιμος δεν βλέπει τέλος αρχείου μέχρι να κλείσει το τελευταίο αντίγραφο.

Η shutdown ενεργεί στην υποκείμενη υποδοχή, όχι στον περιγραφέα. Μετά από shutdown($sock, 1), κάθε forked αντίγραφο βλέπει το μισό εγγραφής κλειστό και ο ομότιμος διαβάζει τέλος αρχείου αμέσως - αν και ο ίδιος ο περιγραφέας παραμένει ανοιχτός και μπορεί ακόμη να διαβαστεί.

Αυτός είναι ο συνήθης λόγος να καταφύγει κανείς στη shutdown: γονέας και παιδί μοιράζονται μια υποδοχή, το παιδί τελείωσε την αποστολή και ο γονέας πρέπει να συνεχίσει να διαβάζει απαντήσεις. Η close στο παιδί θα ήταν αόρατη για τον ομότιμο· η shutdown($sock, 1) όχι.

Μισό κλείσιμο για πρωτόκολλα αίτησης/απάντησης#

Ένας πελάτης που στέλνει πλήρη αίτηση και κατόπιν χρειάζεται ο διακομιστής να γνωρίζει ότι δεν υπάρχει άλλη είσοδος χρησιμοποιεί shutdown με HOW = 1:

use IO::Socket::INET;
my $sock = IO::Socket::INET->new(PeerAddr => "example.com:80")
    or die "connect: $!";

print $sock "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n";
shutdown $sock, 1;          # signal end-of-request to the server

while (my $line = <$sock>) {
    print $line;
}
close $sock;

Χωρίς τη shutdown, οι διακομιστές που διαβάζουν μέχρι το τέλος εισόδου θα μπλόκαραν περιμένοντας περισσότερα δεδομένα αίτησης.

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

Διακοπή ανάγνωσης αλλά συνέχιση εγγραφής - ο ομότιμος μπορεί να συνεχίζει να στέλνει, απλώς δεν θα τα κοιτάμε:

shutdown $sock, 0;

Διακοπή εγγραφής, συνέχιση ανάγνωσης - τυπικό μισό κλείσιμο μετά την ολοκλήρωση αίτησης:

shutdown $sock, 1;

Πλήρης τερματισμός, και κατόπιν κλείσιμο του περιγραφέα:

shutdown $sock, 2;
close $sock;

Χρήση των σταθερών Socket για ευαναγνωσιμότητα:

use Socket qw(SHUT_RD SHUT_WR SHUT_RDWR);
shutdown $sock, SHUT_WR;

Έλεγχος του συγκεκριμένου σφάλματος όταν ένα shutdown αποτυγχάνει σε ήδη κλειστή κατεύθυνση:

unless (shutdown $sock, 1) {
    warn "shutdown write failed: $!";
}

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

  • Η εγγραφή μετά από shutdown($sock, 1) αποτυγχάνει και εγείρει SIGPIPE. Η προεπιλεγμένη ενέργεια τερματίζει τη διεργασία· εγκαταστήστε $SIG{PIPE} = 'IGNORE' (ή χειριστή) για να τη μετατρέψετε σε κανονική αποτυχία εγγραφής που μπορείτε να ελέγξετε.

  • Η ανάγνωση μετά από shutdown($sock, 0) επιστρέφει τέλος αρχείου αμέσως, όχι σφάλμα. Όσα δεδομένα είναι ήδη σε ουρά στον ενταμιευτή λήψης του πυρήνα απορρίπτονται.

  • Δεν είναι υποδοχή: η κλήση shutdown σε κανονικό filehandle θέτει την $! σε ENOTSOCK και επιστρέφει 0.

  • Μη συνδεδεμένη: η shutdown σε υποδοχή που δεν συνδέθηκε ποτέ (φρέσκο αποτέλεσμα της socket, ή υποδοχή που ακούει) θέτει την $! σε ENOTCONN και επιστρέφει 0.

  • Διπλό shutdown: ο τερματισμός του ίδιου μισού δύο φορές τυπικά επιστρέφει 0 με ENOTCONN. Ο φορητός κώδικας αγνοεί τη δεύτερη αποτυχία αντί να την αντιμετωπίζει ως μοιραία.

  • Κλειστό filehandle: επιστρέφει undef· υπό use warnings εκπέμπεται προειδοποίηση shutdown() on closed socket.

  • Ο περιγραφέας δεν κλείνει: η shutdown απενεργοποιεί τις κατευθύνσεις που ζητήσατε αλλά αφήνει τον περιγραφέα αρχείου ανοιχτό. Καλέστε close (ή αφήστε το handle να βγει εκτός εμβέλειας) όταν πραγματικά τελειώσετε με αυτό.

  • Κοινόχρηστοι περιγραφείς μέσω fork: αυτή είναι η περίπτωση για την οποία σχεδιάστηκε η shutdown. Όλες οι διεργασίες που κρατούν τον περιγραφέα βλέπουν το shutdown να εφαρμόζεται άμεσα· δεν έχουν ξεχωριστές όψεις.

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

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

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

  • close - απελευθερώνει τον περιγραφέα σε αυτή τη διεργασία· δεν επηρεάζει forked αντίγραφα και δεν σηματοδοτεί τέλος αρχείου στον ομότιμο μέχρι να κλείσει το τελευταίο αντίγραφο

  • socket - δημιουργεί την υποδοχή στην οποία λειτουργεί η shutdown

  • connect - εγκαθιδρύει τη σύνδεση· η shutdown σε μη συνδεδεμένη υποδοχή αποτυγχάνει με ENOTCONN

  • accept - παράγει τη συνδεδεμένη υποδοχή στην πλευρά του διακομιστή που τυπικά μισοκλείνει με shutdown

  • Socket - εξάγει τις SHUT_RD, SHUT_WR, SHUT_RDWR και τις υπόλοιπες σταθερές επιπέδου socket

  • $! - συμβολοσειρά σφάλματος συστήματος που ελέγχεται μετά από επιστροφή 0