סוקטים

shutdown#

סגירת כיוון אחד של חיבור סוקט, או של שניהם.

shutdown אומר לקרנל להפסיק סוקט לבצע קריאות נוספות, כתיבות נוספות, או שניהם - בלי לסגור את ה־file descriptor. בניגוד ל־close, האפקט מיידי ומתפשט דרך כל עותק שנעשה לו fork של ה־descriptor, לא רק זה של תהליך זה. ל־HOW יש אותה משמעות כמו לארגומנט של קריאת המערכת shutdown(2).

תקציר#

shutdown SOCKET, HOW

מה מקבלים בחזרה#

1 בהצלחה. בכישלון, undef אם SOCKET אינו מטפל קובץ תקף, או 0 עם $! מוגדר לכל שגיאה אחרת (הסוקט אינו מחובר, כבר נסגר באותו כיוון, וכו«).

שתי החזרות כישלון נבדלות, לכן בדיקה תלת־כיוונית נחוצה לפעמים:

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

הארגומנט HOW#

HOW הוא מספר שלם קטן התואם את הקבועים SHUT_* של POSIX:

  • 0 - להפסיק לקרוא. העמית עדיין יכול לשלוח, אך הנתונים יושלכו על־ידי הקרנל ו־read / sysread בקצה זה יחזיר end-of-file.

  • 1 - להפסיק לכתוב. פלט בתור נשטף, ואז העמית רואה end-of-file בצד הקריאה שלו. 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 משחרר את ה־file descriptor בתהליך הנוכחי בלבד. תהליכים אחרים שירשו את ה־descriptor דרך fork שומרים על העותקים שלהם פתוחים, והעמית אינו רואה end-of-file עד שהעותק האחרון נסגר.

shutdown פועל על הסוקט שבבסיס, לא על ה־descriptor. לאחר shutdown($sock, 1) כל עותק שנעשה לו fork רואה את חצי הכתיבה סגור והעמית קורא end-of-file מיד - למרות שה־descriptor עצמו עדיין פתוח ועדיין ניתן לקרוא ממנו.

זוהי הסיבה הרגילה לפנות ל־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, שרתים הקוראים עד end-of-input ייחסמו בהמתנה לנתוני בקשה נוספים.

דוגמאות#

להפסיק לקרוא אך להמשיך לכתוב - העמית יכול להמשיך לשלוח, פשוט לא נסתכל בזה:

shutdown $sock, 0;

להפסיק לכתוב, להמשיך לקרוא - חצי־סגירה טיפוסית לאחר סיום בקשה:

shutdown $sock, 1;

shutdown מלא, ואז סגירת ה־descriptor:

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) מחזירה end-of-file מיד, לא שגיאה. כל נתון שכבר היה בתור ב־buffer הקבלה של הקרנל מושלך.

  • לא סוקט: קריאה ל־shutdown על מטפל קובץ רגיל קובעת את $! ל־ENOTSOCK ומחזירה 0.

  • לא מחובר: shutdown על סוקט שמעולם לא חובר (תוצאה רעננה של socket, או סוקט מאזין) קובע את $! ל־ENOTCONN ומחזיר 0.

  • shutdown כפול: סגירת אותו חצי פעמיים בדרך כלל מחזירה 0 עם ENOTCONN. קוד נייד מתעלם מהכישלון השני במקום להתייחס אליו כקריטי.

  • מטפל קובץ סגור: מחזיר undef; תחת use warnings נפלטת אזהרת shutdown() on closed socket.

  • ה־descriptor אינו סגור: shutdown מנטרל את הכיוונים שביקשת אך משאיר את ה־file descriptor פתוח. יש לקרוא ל־close (או לתת למטפל לצאת מתחום) כשמסיימים איתו ממש.

  • descriptors משותפים בין fork: זה המקרה ש־shutdown תוכנן עבורו. כל התהליכים המחזיקים את ה־descriptor רואים את ה־shutdown נכנס לתוקף מיד; אין להם תצוגות נפרדות.

הבדלים מ־upstream#

תואם מלא ל־upstream Perl 5.42.

ראו גם#

  • close - לשחרר את ה־descriptor בתהליך זה; אינו משפיע על עותקים שנעשה להם fork ואינו מסמן end-of-file לעמית עד שהעותק האחרון נסגר

  • socket - ליצור את הסוקט ש־shutdown פועל עליו

  • connect - לבסס את החיבור; shutdown על סוקט לא־מחובר נכשל עם ENOTCONN

  • accept - מייצר את הסוקט המחובר בצד השרת שעובר בדרך כלל חצי־סגירה עם shutdown

  • Socket - מייצא את SHUT_RD, SHUT_WR, SHUT_RDWR ואת שאר הקבועים ברמת הסוקט

  • $! - מחרוזת שגיאת מערכת לבדיקה לאחר החזרת 0