סוקטים

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 מניבה תוצאה ריקה ומסתירה את השגיאה האמיתית שנרשמה ב־$!.

מצב גלובלי שהוא נוגע בו#

  • $! מוגדר בכישלון לערך ה־errno מקריאת getsockopt(2) הבסיסית. הוא אינו מנוקה בהצלחה, אז בדקו את ערך ההחזרה, לא את $! ישירות.

דוגמאות#

בדיקה האם האלגוריתם של 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 ו־OPTNAME

  • unpack - דרוש לפענוח מחרוזת הבתים הארוזה שהקרנל מחזיר

  • getprotobyname - דרך בזמן ריצה להשגת מספר הפרוטוקול LEVEL כשאינכם רוצים לייבא את קבועי IPPROTO_* מ־Socket

  • getsockname - קריאת אינטרוספקציה אחות; מחזיר את הכתובת המקומית הקשורה ל־SOCKET ולא אופציה בודדת

  • socket - יוצר את הסוקט ש־getsockopt אז מתשאל