getsockopt#
קריאת אופציית סוקט אחת מתוך הקרנל כמחרוזת ארוזה אטומה.
getsockopt מבקש מהקרנל את הערך הנוכחי של האופציה בשם OPTNAME ברמת פרוטוקול LEVEL ב־SOCKET. הקרנל אינו מחזיר ערך מטיפוסי - הוא מחזיר buffer בתים גולמי שצורתו נקבעת על ידי הצמד (LEVEL, OPTNAME). אתם מפענחים את ה־buffer הזה עם unpack, בדרך כלל עם התבנית "i" או "I", מכיוון שרוב האופציות הן מספרים שלמים פשוטים.
תקציר#
getsockopt SOCKET, LEVEL, OPTNAME
מה מקבלים בחזרה#
מחרוזת ארוזה המחזיקה את תשובת הקרנל בהצלחה, או undef בכישלון כאשר $! מוגדר לשגיאת המערכת. תוכן המחרוזת הוא בתים אטומים - אורכה וצורתה תלויים לחלוטין ב־(LEVEL, OPTNAME):
אופציות בוליאניות ושלמות (
SO_KEEPALIVE,SO_REUSEADDR,TCP_NODELAY, …) חוזרות כ־intארוז. פענחו עםunpackבאמצעות"i"או"I".פסקי זמן (
SO_RCVTIMEO,SO_SNDTIMEO) חוזרים כ־struct timevalארוז - שניlongים על רוב הפלטפורמות.אופציות הקשורות לכתובת (
SO_PEERCRED,IP_MULTICAST_IF, structים של linger, …) יש להן צורות משלהן; היוועצו ב־getsockopt(2)ובדפי ה־man של הפרוטוקול הרלוונטי לקבלת ה־struct המדויק.
תמיד בדקו את ערך ההחזרה לפני הפענוח - קריאה ל־unpack על undef מניבה תוצאה ריקה ומסתירה את השגיאה האמיתית שנרשמה ב־$!.
מצב גלובלי שהוא נוגע בו#
דוגמאות#
בדיקה האם האלגוריתם של Nagle מושבת כעת (TCP_NODELAY מוגדר) על סוקט TCP:
use Socket qw(IPPROTO_TCP TCP_NODELAY);
my $packed = getsockopt($sock, IPPROTO_TCP, TCP_NODELAY)
or die "getsockopt TCP_NODELAY: $!";
my $nodelay = unpack("I", $packed);
print "Nagle is ", $nodelay ? "off" : "on", "\n";
אותו רעיון, אך השגת מספר פרוטוקול ה־TCP בזמן ריצה ממסד נתוני הפרוטוקולים של המערכת ולא מקבוע של Socket:
use Socket qw(TCP_NODELAY);
defined(my $tcp = getprotobyname("tcp"))
or die "no protocol entry for tcp";
my $packed = getsockopt($sock, $tcp, TCP_NODELAY)
or die "getsockopt: $!";
קריאת SO_ERROR כדי לנקז ולנקות שגיאה אסינכרונית ממתינה על סוקט לא־חוסם לאחר connect או לאחר ש־select מסמן אותו ככתיב:
use Socket qw(SOL_SOCKET SO_ERROR);
my $packed = getsockopt($sock, SOL_SOCKET, SO_ERROR)
or die "getsockopt SO_ERROR: $!";
my $err = unpack("i", $packed);
if ($err) {
$! = $err;
die "asynchronous socket error: $!";
}
קריאת סוג הסוקט (SOCK_STREAM, SOCK_DGRAM, …) של מטפל שכבר נפתח:
use Socket qw(SOL_SOCKET SO_TYPE SOCK_STREAM);
my $packed = getsockopt($sock, SOL_SOCKET, SO_TYPE)
or die "getsockopt SO_TYPE: $!";
my $type = unpack("i", $packed);
print "stream socket\n" if $type == SOCK_STREAM;
קריאת אופציית struct timeval. SO_RCVTIMEO מחזיר שני longים מקומיים - שניות ומיקרו־שניות:
use Socket qw(SOL_SOCKET SO_RCVTIMEO);
my $packed = getsockopt($sock, SOL_SOCKET, SO_RCVTIMEO)
or die "getsockopt SO_RCVTIMEO: $!";
my ($sec, $usec) = unpack("l!l!", $packed);
printf "receive timeout: %d.%06d s\n", $sec, $usec;
מקרי קצה#
SOCKETאינו סוקט פתוח: הקריאה נכשלת, מחזירהundef, ומגדירה את$!ל־ENOTSOCK("Socket operation on non-socket") אוEBADF("Bad file descriptor") עבור מטפל סגור.LEVEL/OPTNAMEלא ידועים: הקרנל דוחה את הבקשה עםENOPROTOOPT("Protocol not available"). אין דרך להבחין בין זה למקרה ההצלחה אלא על ידי בדיקת ערך ההחזרה.ניב ההחזרה האמיתית: ה־
getsockopt(...) or dieהנפוץ עובד מכיוון שקריאה מוצלחת מחזירה מחרוזת ארוזה לא ריקה. תשובה באורך אפס תגרום לניב לפעול באופן שגוי, אך אף אופציית קרנל אינה מחזירה buffer באורך אפס בפועל - לכל אופציה מוגדרת יש לפחות בית אחד של payload.רוחב פענוח שלם: השתמשו ב־
"i"או"I"עבור אופציות בגודלint(SO_KEEPALIVE,TCP_NODELAY,SO_ERROR, …); השתמשו ב־"l!"או"L!"רק כאשר הצורה המתועדת של האופציה משתמשת באמת ב־longמקומי. טעות ברוחב מחזירה בשקט מספר שגוי על פלטפורמות LP64.אל תארזו את התשובה ביד:
getsockoptמחזיר את כל מה שהקרנל כתב, כולל כל ריפוד. ארזו מחדש רק בנסיעות חזור דרךsetsockopt, ואז העבירו את ה־buffer חזרה מילולית ולא בנו אותו מחדש.אין קבועים ללא
Socket:SOL_SOCKET,IPPROTO_TCP,TCP_NODELAY,SO_ERROR, וחבריהם אינם שמות מובנים. ייבאו אותם מ־Socketעםuse Socket qw(...)אוuse Socket qw(:all).
הבדלים מהמקור#
תאימות מלאה עם Perl 5.42 המקורי.
ראו גם#
setsockopt- מחצית הכתיבה; מגדיר את האופציה שאת ערכהgetsockoptקורא חזרהSocket- מקור הקבועיםSOL_*,IPPROTO_*,SO_*,TCP_*,IP_*שמועברים כ־LEVELו־OPTNAMEunpack- דרוש לפענוח מחרוזת הבתים הארוזה שהקרנל מחזירgetprotobyname- דרך בזמן ריצה להשגת מספר הפרוטוקולLEVELכשאינכם רוצים לייבא את קבועיIPPROTO_*מ־Socketgetsockname- קריאת אינטרוספקציה אחות; מחזיר את הכתובת המקומית הקשורה ל־SOCKETולא אופציה בודדתsocket- יוצר את הסוקט ש־getsockoptאז מתשאל