סוקטים

getpeername#

מחזיר את הכתובת של הקצה המרוחק של סוקט מחובר.

getpeername שואל את הקרנל מי בצד השני של SOCKET ומחזיר את התשובה כמחרוזת בייטים ארוזה sockaddr - אותה צורה ש־connect קיבל ואותה צורה ש־accept החזיר. אחר־כך מעבירים את מחרוזת הבייטים הזו לפורק מ־Socket (או Socket::unpack_sockaddr_in, Socket::unpack_sockaddr_in6, Socket::unpack_sockaddr_un) כדי לחלץ את הפורט, הכתובת, או הנתיב.

זוהי המקבילה ל־getsockname, שמחזיר את הקצה המקומי של אותו סוקט.

תקציר#

use Socket;
my $peer = getpeername SOCKET;
my ($port, $iaddr) = unpack_sockaddr_in($peer);

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

מחרוזת בייטים ארוזה של sockaddr בהצלחה, undef בכישלון (עם $! מוגדר - בדרך־כלל ENOTCONN כאשר לסוקט אין עמית, או EBADF כאשר SOCKET אינו file descriptor פתוח).

מחרוזת הבייטים אטומה. הפריסה שלה תלויה במשפחת הכתובות של הסוקט; תמיד יש לפענח אותה עם פורק התואם למשפחה שאתם מצפים לה:

אם אינכם יודעים את המשפחה באתר הקריאה, יש לשאול את Socket::sockaddr_family תחילה:

my $peer   = getpeername $sock;
my $family = sockaddr_family($peer);

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

getpeername קורא את מצב ה־file-descriptor של SOCKET. הוא קובע את $! בכישלון ואינו נוגע אחרת בגלובלים של המפרש. הוא אינו מקיים אינטראקציה עם $_, עם מטפל הקובץ הנבחר, או עם מפרידי פלט.

דוגמאות#

חיפוש העמית המרוחק IPv4 של סוקט TCP בצד הלקוח והצגתו בצורת dotted-quad:

use Socket;
my $peer = getpeername($sock)
    or die "getpeername: $!";
my ($port, $iaddr) = unpack_sockaddr_in($peer);
printf "peer is %s:%d\n", inet_ntoa($iaddr), $port;

צד השרת: כל סוקט המוחזר על־ידי accept כבר מחובר, ו־accept עצמו מחזיר את כתובת העמית. getpeername שימושי מאוחר יותר, כאשר אותה כתובת ארוזה לא נשמרה:

while (my $client = accept(my $conn, $listen)) {
    handle($conn);
}

sub handle {
    my ($conn) = @_;
    my $peer = getpeername($conn) or return;
    my ($port, $iaddr) = unpack_sockaddr_in($peer);
    warn "request from ", inet_ntoa($iaddr), ":$port\n";
    # ...
}

פתרון העמית בחזרה לשם מארח - שימו לב שזה מנפיק חיפוש DNS חוסם:

use Socket;
my $peer = getpeername($sock);
my ($port, $iaddr) = unpack_sockaddr_in($peer);
my $name = gethostbyaddr($iaddr, AF_INET);
print "connected to ", $name // inet_ntoa($iaddr), "\n";

קוד מעורב IPv4 / IPv6 - פענוח גנרי דרך משפחת הכתובות:

use Socket qw(sockaddr_family unpack_sockaddr_in unpack_sockaddr_in6
              AF_INET AF_INET6 inet_ntop);

my $peer = getpeername($sock) or die "getpeername: $!";
my $fam  = sockaddr_family($peer);

if ($fam == AF_INET) {
    my ($port, $iaddr) = unpack_sockaddr_in($peer);
    printf "v4 %s:%d\n", inet_ntop(AF_INET, $iaddr), $port;
}
elsif ($fam == AF_INET6) {
    my ($port, $iaddr) = unpack_sockaddr_in6($peer);
    printf "v6 [%s]:%d\n", inet_ntop(AF_INET6, $iaddr), $port;
}

בדיקה האם סוקט מחובר בכלל - מסלול ה־ENOTCONN הוא הדרך המוגדרת היטב לשאול:

if (defined getpeername($sock)) {
    # connected
} else {
    # $! == ENOTCONN for an unconnected socket
}

מקרי קצה#

  • סוקט לא־מחובר: סוקט שעבר socket אבל עדיין לא connect או accept אין לו עמית. getpeername מחזיר undef וקובע את $! ל־ENOTCONN.

  • מטפל קובץ שאינו סוקט: העברת קובץ פשוט או pipe מניבה undef עם $! מוגדר ל־ENOTSOCK.

  • מטפל קובץ סגור או לא־תקף: מחזיר undef עם $! מוגדר ל־EBADF. תחת use warnings נפלטת אזהרת getpeername() on closed socket.

  • סוקטי datagram: getpeername מחזיר כתובת רק כאשר סוקט SOCK_DGRAM עבר connect לעמית ברירת מחדל; אחרת הוא נכשל עם ENOTCONN למרות שהסוקט שמיש דרך recv / send.

  • AF_UNIX עם עמית לא־מאוגד: סוקט תחום־Unix שהעמית שלו מעולם לא קרא bind מחזיר sockaddr_un ארוז עם נתיב ריק. Socket::unpack_sockaddr_un מחזיר את המחרוזת הריקה.

  • לאחר shutdown: getpeername עדיין מצליח כל עוד ה־descriptor פתוח; shutdown מפרק את זרימת הנתונים, לא את הרשומה של הקרנל על העמית.

  • SOCKET הוא טוקן מטפל קובץ שמנותח: כמו מובנים אחרים של סוקט, הארגומנט מנותח כמטפל קובץ, לא מוערך כביטוי כללי. getpeername $handles[0] היא שגיאת תחביר; יש להשתמש בסקלר המחזיק את המטפל, או ב־dereferencing של *{...}.

הבדלים מ־upstream#

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

ראו גם#

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

  • accept - כבר מוסר את כתובת העמית עבור סוקט בצד השרת שזה עתה התקבל; יש להשתמש ב־getpeername מאוחר יותר כאשר אותה כתובת לא נשמרה

  • connect - הקריאה שמקימה את העמית מלכתחילה; הכתובת שמועברת ל־connect היא מה ש־getpeername יחזיר לאחר מכן

  • Socket - מספק את הפורקים (unpack_sockaddr_in, unpack_sockaddr_in6, unpack_sockaddr_un) ואת sockaddr_family הנדרשים לפרשנות של מחרוזת הבייטים המוחזרת

  • gethostbyaddr - הופך את כתובת ה־IPv4 הארוזה מהפורק לשם מארח

  • $! - נושא את הסיבה בכישלון (ENOTCONN, ENOTSOCK, EBADF)