accept#
מקבל חיבור נכנס על socket מאזין.
accept הוא צד השרת בלחיצת היד של החיבור. הוא ממתין על GENERICSOCKET — socket שכבר נוצר באמצעות socket, נקשר באמצעות bind, והועבר למצב האזנה באמצעות listen — עד שלקוח מתחבר, ואז מתקין socket מחובר חדש לחלוטין ב־NEWSOCKET ומחזיר את הכתובת הארוזה של הלקוח. GENERICSOCKET נשאר במצב האזנה, מוכן לקריאה הבאה. ההתנהגות משקפת את קריאת המערכת POSIX accept(2).
תקציר#
accept NEWSOCKET, GENERICSOCKET
מה מוחזר#
כתובת הלקוח בצורה הארוזה המשמשת את משפחת הכתובות של GENERICSOCKET — אותה צורה שהיו מעבירים ל־connect או מפרקים באמצעות Socket::unpack_sockaddr_in עבור IPv4, או באמצעות Socket::unpack_sockaddr_in6 / unpack_sockaddr_un עבור IPv6 ו־sockets במרחב Unix בהתאמה. בכישלון מחזיר ערך שקרי (המחרוזת הריקה) וקובע את $! ל־errno הבסיסי.
NEWSOCKET ממולא כתופעת לוואי — הוא ארגומנט filehandle רגיל, לא ערך מוחזר. מילה חשופה כמו CLIENT יוצרת אוטומטית typeglob; filehandle לקסיקלי כמו my $client ממולא במקום:
my $client;
accept($client, $server) or die "accept: $!";
# $client is now a readable/writable handle to the connected peer
מצב גלובלי שהפונקציה נוגעת בו#
$!— נקבע בכישלון ל־errnoהבסיסי (EINTR,EAGAIN/EWOULDBLOCKעל מאזינים לא־חוסמים,ECONNABORTED,EMFILE,ENFILE).$^F— מתאר הקובץ המרבי של המערכת. במערכות התומכות בדגל close-on-exec,acceptקובעFD_CLOEXECעל המתאר החדש כאשר מספרו גדול מ־$^F(ברירת מחדל2, המכסה את הזרמים הסטנדרטיים). יש להעלות את$^Fלפניacceptאם רוצים שה־socket המקובל ישרוד אתexec.
דוגמאות#
לולאת accept מינימלית בסגנון TCP echo:
use Socket;
socket(my $server, PF_INET, SOCK_STREAM, getprotobyname("tcp"))
or die "socket: $!";
bind($server, sockaddr_in(8080, INADDR_ANY)) or die "bind: $!";
listen($server, SOMAXCONN) or die "listen: $!";
while (my $peer = accept(my $client, $server)) {
my ($port, $iaddr) = sockaddr_in($peer);
print $client "hello ", inet_ntoa($iaddr), ":$port\n";
close $client;
}
die "accept: $!"; # loop exits only on error
פירוק הכתובת המוחזרת לצורך רישום ביומן:
use Socket;
my $peer = accept(my $client, $server)
or die "accept: $!";
my ($port, $iaddr) = sockaddr_in($peer);
printf "connection from %s port %d\n", inet_ntoa($iaddr), $port;
accept לא־חוסם. תחילה יש להעביר את $server למצב לא־חוסם; קריאה ללא חיבור ממתין מחזירה אז ערך שקרי כאשר $! מוגדר כ־EAGAIN או EWOULDBLOCK:
use Errno qw(EAGAIN EWOULDBLOCK);
use Fcntl qw(F_GETFL F_SETFL O_NONBLOCK);
my $flags = fcntl($server, F_GETFL, 0);
fcntl($server, F_SETFL, $flags | O_NONBLOCK);
my $peer = accept(my $client, $server);
if (!$peer) {
if ($! == EAGAIN || $! == EWOULDBLOCK) {
# no pending connection — try again later
} else {
die "accept: $!";
}
}
שימור ה־socket המקובל לאורך exec על ידי העלאת $^F כך שזמן הריצה לא יקבע FD_CLOEXEC:
local $^F = 10_000;
accept(my $client, $server) or die "accept: $!";
exec "/usr/libexec/handler", fileno($client);
מקרי קצה#
GENERICSOCKETאינו מאזין —acceptנכשל כאשר$!מוגדר כ־EINVAL. יש לקרוא ל־listenעל socket השרת תחילה.NEWSOCKETכבר פתוח — ה־handle הקיים נסגר בשקט לפני שהמתאר החדש מותקן, בדיוק כמו עבורopenו־socket. אם ה־handle הישן היה ההפניה היחידה לזרם עם buffer, כל נתון שלא הוזרם החוצה אובד. יש לסגור במפורש מראש כאשר זה חשוב.אות במהלך הקריאה — אות הנשלח בזמן ש־
acceptחסום גורם לקריאת המערכת הבסיסית להחזירEINTR. pperl אינו מבצע auto-restart:acceptמחזיר ערך שקרי ומשאיר את$!מוגדר כ־EINTR. יש לעטוף בלולאת ניסיון חוזר אם מטפלי האותות אינם מסיימים את התהליך.מיצוי מתארים —
EMFILE(מגבלת תהליך) אוENFILE(מגבלת מערכת) על שרת עמוס היא הסיבה הנפוצה לכך ש־acceptמתחיל להיכשל תחת עומס. יש להתייחס אליהם כחולפים; יש לסגור לקוחות בטלים ולנסות שוב.אי־התאמה של משפחת כתובות בערך המוחזר הארוז — ה־buffer הארוז עוקב אחר המשפחה של
GENERICSOCKET. פירוק כתובת במרחב Unix באמצעותSocket::unpack_sockaddr_inמפיק זבל. יש לעקוב אחר המשפחה בעצמכם או להשתמש ב־Socket::sockaddr_familyלצורך ניתוב.סוגריים הם אופציונליים, פסיקים אינם —
accept $c, $sו־accept($c, $s)שניהם עובדים;accept $c $sהוא שגיאת תחביר.הקשר רשימה לעומת הקשר סקלרי — ערך הכתובת הארוזה המוחזר הוא מחרוזת בתים; הקשר רשימה אינו מרחיב אותו ל־
(port, addr). יש לקרוא ל־sockaddr_in/ שווה־ערך לפירוק.
הבדלים מן ה-upstream#
תואם מלא ל־Perl 5.42 upstream.
ראו גם#
socket— יצירת socket השרת ש־acceptממתין עליוconnect— המקבילה בצד הלקוח;acceptמחזיר את הכתובת שקריאתconnectסיפקהgetpeername— שחזור אותה כתובת ארוזה מאוחר יותר מ־NEWSOCKETעצמוSocket— הקבועים (PF_INET,SOCK_STREAM,INADDR_ANY) ופונקציות האריזה (sockaddr_in,unpack_sockaddr_in) שהופכים את הערך המוחזר הארוז למשהו שמיש$^F— סף השולט אםacceptקובעFD_CLOEXECעל המתאר החדש