סוקטים

setsockopt#

מגדיר אפשרות ברמת הקרנל על סוקט פתוח.

setsockopt הוא העטיפה של Perl סביב קריאת המערכת setsockopt(2) של POSIX. הוא כותב את האפשרות הקרויה ב־OPTNAME ברמת הפרוטוקול LEVEL על מטפל הקובץ של ה־SOCKET שכבר נפתח, באמצעות הערך הארוז ב־OPTVAL. זהו הכפתור לכל מה שהקרנל מאפשר לכם לכוון על סוקט - שימוש חוזר בכתובות, keepalives, timeouts, האלגוריתם של Nagle, חברות multicast, גודלי buffer לשליחה וקבלה, התנהגות SO_LINGER ב־close, וכן הלאה. שמות סימבוליים ל־LEVEL ול־OPTNAME באים מהמודול Socket.

תקציר#

setsockopt SOCKET, LEVEL, OPTNAME, OPTVAL

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

ערך אמת בהצלחה, undef בכשל (עם $! קבוע ל־errno הבסיסי: EBADF, ENOPROTOOPT, EINVAL, ENOTSOCK, וכו«). תמיד בדקו את ערך ההחזרה - אפשרות שנתעלמה ממנה בשקט אומרת שהתוכנית רצה עם ברירות מחדל שונות ממה שהקוד קורא:

setsockopt($sock, SOL_SOCKET, SO_REUSEADDR, pack("l", 1))
    or die "setsockopt SO_REUSEADDR: $!";

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

אין ישירות. setsockopt קובע את $! בכשל; זה הגלובל היחיד של המפענח המעורב. ההשפעה היא על מצב הסוקט בצד הקרנל, לא על משתנה כלשהו ברמת Perl.

OPTVAL - בתים ארוזים או מספר שלם חשוף#

רוב אפשרויות הסוקט לוקחות דגל int של C. setsockopt מקבל כל אחת מהצורות לנוחות:

  • מחרוזת בתים ארוזה שתבניתה תואמת לסוג ה־C של האפשרות, בנויה בדרך כלל עם pack: pack("l", 1) לבוליאני, pack("ll", $on_secs, $on_usecs) ל־struct timeval, pack("ii", $on, $linger) ל־struct linger.

  • מספר שלם פשוט, שהוא קיצור ל־pack("i", OPTVAL). שימושי עבור SO_REUSEADDR, TCP_NODELAY, ודגלים בוליאניים אחרים:

    setsockopt($sock, IPPROTO_TCP, TCP_NODELAY, 1);
    

אפשרויות שערכן הוא struct (SO_LINGER, SO_RCVTIMEO, IP_ADD_MEMBERSHIP) חייבות להיות מועברות כמחרוזת pack מפורשת. מספר שלם חשוף שם מפיק EINVAL או, גרוע יותר, כותב בשקט זיכרון בגודל שגוי שהקרנל מפרש לא נכון.

דוגמאות#

השבתת האלגוריתם של Nagle על סוקט TCP כך שכתיבות קטנות יוצאות מיד - שימושי לפרוטוקולים רגישי השהיה (shells אינטראקטיביים, תעבורת משחקים, RPC):

use Socket qw(IPPROTO_TCP TCP_NODELAY);

setsockopt($sock, IPPROTO_TCP, TCP_NODELAY, 1)
    or die "TCP_NODELAY: $!";

מתן רשות לשרת לקשור מחדש פורט שעדיין ב־TIME_WAIT מתהליך קודם - קריאת setsockopt הנפוצה ביותר בפועל, והיא חייבת לבוא לפני bind:

use Socket qw(SOL_SOCKET SO_REUSEADDR);

setsockopt($srv, SOL_SOCKET, SO_REUSEADDR, pack("l", 1))
    or die "SO_REUSEADDR: $!";
bind($srv, sockaddr_in(9000, INADDR_ANY))
    or die "bind: $!";

הגדרת timeout לקבלה כך ש־recv חוסם או קריאה מהסוקט מחזירים עם EAGAIN / EWOULDBLOCK לאחר 5 שניות במקום להיתקע לנצח. ערך האפשרות הוא struct timeval ארוז:

use Socket qw(SOL_SOCKET SO_RCVTIMEO);

my $tv = pack("l!l!", 5, 0);    # 5 seconds, 0 microseconds
setsockopt($sock, SOL_SOCKET, SO_RCVTIMEO, $tv)
    or die "SO_RCVTIMEO: $!";

הפעלת בדיקות keepalive של TCP כך שחיבורים חצי־פתוחים (העמית קרס, הכבל נמשך) בסופו של דבר מבחינים בהם והסוקט נכשל במקום להיחסם ללא הגבלה:

use Socket qw(SOL_SOCKET SO_KEEPALIVE);

setsockopt($sock, SOL_SOCKET, SO_KEEPALIVE, 1)
    or die "SO_KEEPALIVE: $!";

לגרום ל־close להיחסם עד 10 שניות בעוד הקרנל מבצע flush של נתונים שלא נשלחו, ואז להשליך ולאפס. SO_LINGER לוקח struct של שני מספרים שלמים: דגל ה־on וה־timeout בשניות:

use Socket qw(SOL_SOCKET SO_LINGER);

setsockopt($sock, SOL_SOCKET, SO_LINGER, pack("ii", 1, 10))
    or die "SO_LINGER: $!";

הגדלת buffer הקבלה של הקרנל לסוקט להעברת bulk. הקרנל בדרך כלל מכפיל את הערך שאתם מבקשים ומגביל אותו ב־net.core.rmem_max:

use Socket qw(SOL_SOCKET SO_RCVBUF);

setsockopt($sock, SOL_SOCKET, SO_RCVBUF, 1 << 20)   # 1 MiB
    or die "SO_RCVBUF: $!";

מקרי קצה#

  • SOCKET חייב להיות כבר פתוח. setsockopt אינו יוצר את הסוקט - קראו תחילה ל־socket. מטפל קובץ סגור או שמעולם לא נפתח נכשל עם EBADF.

  • הסדר משנה. SO_REUSEADDR חייב להיות מוגדר לפני bind. SO_KEEPALIVE יכול ללכת לפני או אחרי connect, אך עבור סוקטי שרת הגדירו אותו על מטפל הקובץ של הילד המקובל המוחזר על־ידי accept, לא על המאזין - אפשרויות לא תמיד מתפשטות.

  • קיצור מספר שלם הוא pack("i", N), לא pack("l", N). בפלטפורמות שבהן int ו־long שונים בגודלם, העברת מספר שלם פשוט והעברת pack("l", N) כותבים ספירות בתים שונות. שניהם במקרה עובדים עבור אפשרויות בוליאניות בגודל־int הנפוצות ב־LP64 Linux, אך צורת הקיצור היא ברירת המחדל הניידת.

  • אפשרויות עם ערך struct זקוקות ל־pack מפורש. SO_LINGER, SO_RCVTIMEO, SO_SNDTIMEO, IP_ADD_MEMBERSHIP, IP_MREQ, ודומיהם לוקחים structs בתבנית קבועה. העבירו מחרוזת ארוזה באורך הנכון או שהקרנל ידחה את הקריאה עם EINVAL.

  • LEVEL בוחר את מרחב השמות של האפשרות. SOL_SOCKET לאפשרויות גנריות, IPPROTO_TCP לאפשרויות ייחודיות־TCP (TCP_NODELAY, TCP_KEEPIDLE), IPPROTO_IP ל־IPv4 (IP_TTL, IP_ADD_MEMBERSHIP), IPPROTO_IPV6 ל־IPv6. אי־התאמה בין LEVEL ל־OPTNAME נותנת ENOPROTOOPT.

  • אפשרויות לקריאה־בלבד או שאינן נתמכות נכשלות עם ENOPROTOOPT. זה גם יורה כאשר הקרנל פשוט אינו מכיר את האפשרות - שכיח בהעתקת קוד בין מערכות הפעלה.

  • אפשרויות מורשות. כמה אפשרויות (SO_PRIORITY מעל ערכים מסוימים, IP_TRANSPARENT, SO_BINDTODEVICE) דורשות CAP_NET_ADMIN ב־Linux; קריאה לא־מורשית נכשלת עם EPERM.

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

הבדלים מ־upstream#

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

ראו גם#

  • getsockopt - המקבילה לשאילתה; קראו חזרה אפשרות כדי לראות מה הקרנל אכן יישם

  • socket - יוצר את מטפל הקובץ של הסוקט ש־setsockopt מגדיר; חייב להיקרא תחילה

  • bind - הגדירו SO_REUSEADDR עם setsockopt לפני קריאה ל־bind על סוקט שרת

  • accept - אפשרויות שמוגדרות על המאזין לא כולן מתפשטות; יישמו מחדש אפשרויות לכל־חיבור על המטפל המקובל

  • pack - בונה את מחרוזת הבתים של OPTVAL עבור אפשרויות עם ערך struct כמו SO_LINGER ו־SO_RCVTIMEO

  • Socket - מקור SOL_SOCKET, IPPROTO_TCP, SO_REUSEADDR, TCP_NODELAY ושאר הקבועים הסימבוליים