סוקטים

socketpair#

יוצר זוג סוקטים ללא שם, מחוברים, שמדברים זה עם זה.

socketpair פותח שני מטפלי קובץ רעננים ב־SOCKET1 וב־SOCKET2 כבר מחווטים יחד, כך שכל דבר שנכתב לאחד יוצא מהאחר. אין כתובות, אין bind, אין listen, אין accept. השימוש הנפוץ ביותר הוא לדבר עם תהליך־ילד שנוצר על־ידי fork - כל צד שומר מטפל אחד ועושה close לאחר.

תקציר#

use Socket;
socketpair(SOCKET1, SOCKET2, DOMAIN, TYPE, PROTOCOL) or die $!;
socketpair(my $s1, my $s2, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die $!;

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

אמת בהצלחה, שקר בכישלון (כש־$! מוגדר). בהצלחה שני ארגומנטי מטפלי הקובץ מאוכלסים במטפלים רעננים; בכישלון הם נשארים ללא שינוי. תמיד בדוק את ערך ההחזרה - כל שילוב של domain/type/protocol תלוי בפלטפורמה.

socketpair(my $a, my $b, AF_UNIX, SOCK_STREAM, PF_UNSPEC)
    or die "socketpair: $!";

הקבועים DOMAIN, TYPE, ו־PROTOCOL מגיעים ממודול Socket. AF_UNIX עם SOCK_STREAM ו־PF_UNSPEC הוא ברירת המחדל הניידת.

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

  • $! מוגדר בכישלון ל־errno של המערכת.

  • $^F - file descriptor המקסימלי של המערכת שמתחתיו close-on-exec אינו מוגדר. במערכות עם FD_CLOEXEC, descriptors מעל $^F מקבלים close-on-exec אוטומטית; מתחתיו, הם שורדים exec. ברירת המחדל היא 2 (stdin/stdout/stderr). הגבה אותו לפני socketpair אם אתה רוצה שהמטפלים החדשים יירשו דרך exec.

דוגמאות#

pipe רגיל של הורה/ילד תוך שימוש ב־stream socketpair. כל צד סוגר את הקצה שאינו זקוק לו, כך ש־EOF מתפשט נכון:

use Socket;

socketpair(my $child, my $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC)
    or die "socketpair: $!";

my $pid = fork // die "fork: $!";
if ($pid == 0) {
    close $child;
    print $parent "hello from child\n";
    close $parent;
    exit;
}
close $parent;
chomp(my $msg = <$child>);          # "hello from child"
waitpid $pid, 0;

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

use Socket;

socketpair(my $a, my $b, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die $!;
my $pid = fork // die $!;
if ($pid == 0) {
    close $a;
    while (my $line = <$b>) {
        chomp $line;
        print $b "echo: $line\n";
    }
    exit;
}
close $b;
print $a "ping\n";
chomp(my $reply = <$a>);            # "echo: ping"
close $a;
waitpid $pid, 0;

אמולציה של pipe עם socketpair. shutdown הופך סוקט דו־כיווני לערוץ חד־כיווני:

use Socket;
socketpair(my $rdr, my $wtr, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die $!;
shutdown($rdr, 1);                  # reader cannot write
shutdown($wtr, 0);                  # writer cannot read

זוג datagram - הודעות שומרות גבולות, כך ש־recv יחיד מחזיר בדיוק send אחד:

use Socket;
socketpair(my $a, my $b, AF_UNIX, SOCK_DGRAM, PF_UNSPEC) or die $!;
send($a, "one", 0)   or die $!;
send($a, "two", 0)   or die $!;
recv($b, my $m1, 64, 0);            # "one"
recv($b, my $m2, 64, 0);            # "two"

מקרי קצה#

  • AF_UNIX הוא ה־domain הנייד היחיד. ברוב המערכות socketpair עובד רק עם AF_UNIX; משפחות אחרות מחזירות EAFNOSUPPORT. השתמש בקבוע, לא ב־1 מקודד־קשיח.

  • פלט לא־מאוגר. מטפלי stream socketpair מאוגרים כמו כל מטפל קובץ אחר. הגדר את $| על הכותב (לאחר select שלו) או שהקורא יחסום בהמתנה לפלט שעדיין נמצא ב־buffer של ההורה. autoflush מ־IO::Handle הוא הצורה הרגילה:

    $parent->autoflush(1);
    
  • סגור את שני הקצוות אחרת EOF לעולם לא מגיע. stream socketpair מספק EOF על קורא רק לאחר שכל עותק כתיב של מטפל העמית נסגר. ילד ששוכח לעשות close לקצה ההורה יתלה את ההורה על <$handle> לנצח.

  • Close-on-exec דרך $^F. מטפלי קובץ שה־descriptor המספרי שלהם גדול מ־$^F מקבלים FD_CLOEXEC בפלטפורמות שתומכות בכך, כך שהם נעלמים דרך exec. הגבה את $^F לפני הקריאה אם המטפל חייב לשרוד exec.

  • לא ממומש בפלטפורמה. אם קריאת המערכת socketpair(2) הבסיסית אינה קיימת, perl מעלה חריגה במקום להחזיר שקר. לכוד עם eval אם אתה צריך ליפול חזרה.

  • Datagram מול stream. SOCK_STREAM הוא מוכוון־בתים (אין גבולות הודעה, השתמש במסגור שורות חדשות או קידומות אורך). SOCK_DGRAM שומר גבולות הודעה אך מאבד אותם בשקט אם buffer הקבלה קטן מההודעה.

  • PROTOCOL. עבור AF_UNIX הפרוטוקול נשכח מהלב; PF_UNSPEC (או 0) הוא מקובל. ציון פרוטוקול שאינו תואם לשילוב domain/type מחזיר EPROTONOSUPPORT.

הבדלים מהמקור#

תאימות מלאה עם Perl 5.42 המקורי.

ראו גם#

  • socket - יוצר סוקט יחיד כאשר רק קצה אחד שייך לך (השני מגיע מהרשת דרך accept או connect)

  • pipe - ערוץ בתים חד־כיווני; קל יותר מ־socketpair אך אינו יכול לבצע תעבורה דו־כיוונית בקריאה אחת

  • send / recv - I/O מוכוון־הודעות, נדרש עבור זוגות סוקטים SOCK_DGRAM

  • fork - השותף האופייני: socketpair אחד לפני ה־fork נותן להורה ולילד צינור מן המוכן

  • Socket - מקור של AF_UNIX, SOCK_STREAM, SOCK_DGRAM, PF_UNSPEC והקבועים האחרים ש־socketpair מצפה להם

  • $^F - סף שמעליו descriptors חדשים מקבלים close-on-exec; חשוב כאשר המטפלים חייבים לשרוד exec