socket — מודול socket

מודול זה מספק גישה לממשק socket של BSD.

הבדל מ-CPython

לשם יעילות ועקביות, אובייקטי socket ב-MicroPython מממשים ממשק stream (דמוי קובץ) באופן ישיר. ב-CPython, יש להמיר socket לאובייקט דמוי קובץ באמצעות המתודה makefile(). מתודה זו עדיין נתמכת על ידי MicroPython (אך אינה מבצעת דבר), כך שכאשר תאימות ל-CPython חשובה, הקפידו להשתמש בה.

תבנית/תבניות כתובת socket

תבנית כתובת ה-socket המקורית של מודול socket היא סוג נתונים אטום המוחזר על ידי הפונקציה getaddrinfo(), שיש להשתמש בה לפענוח כתובת טקסטואלית (כולל כתובות מספריות):

sockaddr = socket.getaddrinfo('www.micropython.org', 80)[0][-1]
# You must use getaddrinfo() even for numeric addresses
sockaddr = socket.getaddrinfo('127.0.0.1', 80)[0][-1]
# Now you can use that address
sock.connect(sockaddr)

שימוש ב-getaddrinfo() הוא הדרך היעילה ביותר (הן מבחינת זיכרון והן מבחינת כוח עיבוד) והניידת ביותר לעבודה עם כתובות.

מודול socket מספק גם דרך תואמת-CPython לציון כתובות באמצעות tuples, כמתואר להלן. ב-OpenMV Cam מודול socket מובנה; ניתן לתת כתובות מספריות ישירות בתבנית ה-tuple, אך שמות מתחם יש לפענח תחילה באמצעות getaddrinfo().

לסיכום:

  • השתמשו תמיד ב-getaddrinfo() כדי לפענח שמות מארחים.

  • כתובות tuple המתוארות להלן יכולות לשמש כקיצור לכתובות מספריות, להאקים מהירים ולשימוש אינטראקטיבי.

תבנית כתובת tuple עבור מודול socket:

  • IPv4: (ipv4_address, port), כאשר ipv4_address היא מחרוזת עם כתובת IPv4 מספרית בכתיב נקודות, למשל "8.8.8.8", ו-port הוא מספר פורט שלם בטווח 1-65535. שמות מתחם אינם מתקבלים כ-ipv4_address; פענחו אותם תחילה באמצעות getaddrinfo().

  • IPv6: (ipv6_address, port, flowinfo, scopeid), כאשר ipv6_address היא מחרוזת עם כתובת IPv6 מספרית בכתיב נקודתיים, למשל "2001:db8::1", ו-port הוא מספר פורט שלם בטווח 1-65535. flowinfo חייב להיות 0. scopeid הוא מזהה ההיקף של הממשק עבור כתובות link-local. שמות מתחם אינם מתקבלים כ-ipv6_address; פענחו אותם תחילה באמצעות getaddrinfo().

פונקציות

socket.getaddrinfo(host: str, port: int, af: int = 0, type: int = 0, proto: int = 0, flags: int = 0, /) List[Tuple]

מתרגם את ארגומנט המארח/הפורט לרצף של 5-tuples המכילים את כל הארגומנטים הדרושים ליצירת socket המחובר לאותו שירות. הארגומנטים af, type ו-proto (בעלי אותה משמעות כמו עבור הפונקציה socket) יכולים לשמש לסינון סוגי הכתובות המוחזרות. אם פרמטר אינו מצוין או הוא אפס, ניתן להחזיר את כל צירופי הכתובות (מה שמחייב סינון בצד המשתמש).

רשימת ה-5-tuples המתקבלת היא בעלת המבנה הבא:

(family, type, proto, canonname, sockaddr)

הדוגמה הבאה מראה כיצד להתחבר ל-url נתון:

s = socket.socket()
# This assumes that if "type" is not specified, an address for
# SOCK_STREAM will be returned, which may be not true
s.connect(socket.getaddrinfo('www.micropython.org', 80)[0][-1])

שימוש מומלץ בפרמטרי הסינון:

s = socket.socket()
# Guaranteed to return an address which can be connect'ed to for
# stream operation.
s.connect(socket.getaddrinfo('www.micropython.org', 80, 0, SOCK_STREAM)[0][-1])

הבדל מ-CPython

CPython מעלה חריגת socket.gaierror (תת-מחלקה של OSError) במקרה של שגיאה בפונקציה זו. ב-MicroPython אין socket.gaierror והיא מעלה OSError ישירות. שימו לב שמספרי השגיאה של getaddrinfo() מהווים מרחב שמות נפרד ועשויים שלא להתאים למספרי השגיאה ממודול errno. כדי להבחין בשגיאות getaddrinfo(), הן מיוצגות על ידי מספרים שליליים, בעוד ששגיאות מערכת סטנדרטיות הן מספרים חיוביים (ניתן לגשת למספרי השגיאה באמצעות המאפיין e.args[0] מאובייקט חריגה). השימוש בערכים שליליים הוא פרט זמני שעשוי להשתנות בעתיד.

socket.inet_ntop(af: int, bin_addr: bytes) str

ממיר כתובת רשת בינארית bin_addr של משפחת הכתובות הנתונה af לייצוג טקסטואלי:

>>> socket.inet_ntop(socket.AF_INET, b"\x7f\0\0\1")
'127.0.0.1'
socket.inet_pton(af: int, txt_addr: str) bytes

ממיר כתובת רשת טקסטואלית txt_addr של משפחת הכתובות הנתונה af לייצוג בינארי:

>>> socket.inet_pton(socket.AF_INET, "1.2.3.4")
b'\x01\x02\x03\x04'

קבועים

socket.AF_INET: int

משפחת כתובות IPv4.

socket.AF_INET6: int

משפחת כתובות IPv6.

socket.SOCK_STREAM: int

סוג socket מסוג Stream (TCP).

socket.SOCK_DGRAM: int

סוג socket מסוג Datagram (UDP).

socket.SOCK_RAW: int

סוג socket גולמי (Raw).

socket.IPPROTO_IP: int

רמת פרוטוקול ה-IP. משמש כארגומנט level ל-setsockopt() יחד עם אפשרויות IP_*.

socket.IPPROTO_TCP: int

פרוטוקול ה-TCP. אינכם צריכים להעביר זאת ל-socket (סוג ה-socket‏ SOCK_STREAM בוחר אותו אוטומטית); השימוש האמיתי היחיד בו הוא כארגומנט level ל-setsockopt() יחד עם אפשרויות TCP_*.

socket.SOL_SOCKET: int

רמת אפשרות ה-socket. משמש כארגומנט level ל-setsockopt() יחד עם אפשרויות SO_*.

socket.SO_REUSEADDR: int

מאפשר ל-socket להיקשר לכתובת/פורט שעדיין נמצאים במצב TIME_WAIT.

socket.SO_BROADCAST: int

מתיר שליחת datagrams לכתובת שידור (broadcast).

socket.SO_KEEPALIVE: int

מאפשר שידור תקופתי של בדיקות keep-alive על socket מחובר.

socket.SO_SNDTIMEO: int

פסק זמן לשליחה, באלפיות שנייה, המועבר כארגומנט value ל-setsockopt().

socket.SO_RCVTIMEO: int

פסק זמן לקבלה, באלפיות שנייה, המועבר כארגומנט value ל-setsockopt().

socket.IP_ADD_MEMBERSHIP: int

הצטרפות לקבוצת multicast. אפשרות setsockopt() ברמת IPPROTO_IP.

socket.IP_DROP_MEMBERSHIP: int

עזיבת קבוצת multicast. אפשרות setsockopt() ברמת IPPROTO_IP.

socket.TCP_NODELAY: int

השבתת האלגוריתם של Nagle. אפשרות setsockopt() ברמת IPPROTO_TCP.

socket.MSG_PEEK: int

עבור recv() / recvfrom(): מחזיר נתונים מבלי להסירם מתור הקלט.

socket.MSG_DONTWAIT: int

עבור recv() / recvfrom(): מבצע את הפעולה במצב ללא חסימה.

מחלקות

class socket.socket(af: int = AF_INET, type: int = SOCK_STREAM, proto: int = IPPROTO_TCP, /)

יוצר socket חדש באמצעות משפחת הכתובות, סוג ה-socket ומספר הפרוטוקול הנתונים. ציון proto אינו נדרש ברוב המקרים (ואינו מומלץ); ארגומנט ה-type בוחר את הפרוטוקול הדרוש אוטומטית:

# Create STREAM TCP socket
socket(AF_INET, SOCK_STREAM)
# Create DGRAM UDP socket
socket(AF_INET, SOCK_DGRAM)
close() None

מסמן את ה-socket כסגור ומשחרר את כל המשאבים. ברגע שזה קורה, כל הפעולות העתידיות על אובייקט ה-socket ייכשלו. הקצה המרוחק יקבל חיווי EOF אם הפרוטוקול תומך בכך.

sockets נסגרים אוטומטית כאשר הם נאספים על ידי איסוף הזבל, אך מומלץ לסגור אותם במפורש באמצעות close() מיד עם סיום העבודה איתם.

bind(address: Any) None

קושר את ה-socket ל-address. ה-socket אסור שיהיה כבר קשור.

listen(backlog: int = 2) None

מאפשר לשרת לקבל חיבורים. אם backlog מצוין, הוא חייב להיות לפחות 0 (אם הוא נמוך יותר, הוא ייקבע ל-0); והוא מציין את מספר החיבורים שלא התקבלו שהמערכת תאפשר לפני שתסרב לחיבורים חדשים. אם אינו מצוין, נבחר ערך סביר כברירת מחדל.

accept() Tuple['socket', Tuple]

מקבל חיבור. ה-socket חייב להיות קשור לכתובת ולהאזין לחיבורים. ערך ההחזרה הוא זוג (conn, address) כאשר conn הוא אובייקט socket חדש שניתן להשתמש בו לשליחה ולקבלה של נתונים בחיבור, ו-address היא הכתובת הקשורה ל-socket בקצה השני של החיבור.

connect(address: Any) None

מתחבר ל-socket מרוחק בכתובת address.

send(bytes: bytes) int

שולח נתונים ל-socket. ה-socket חייב להיות מחובר ל-socket מרוחק. מחזיר את מספר הבייטים שנשלחו, שעשוי להיות קטן מאורך הנתונים (”short write“).

sendall(bytes: bytes) None

שולח את כל הנתונים ל-socket. ה-socket חייב להיות מחובר ל-socket מרוחק. בניגוד ל-send(), מתודה זו תנסה לשלוח את כל הנתונים, על ידי שליחת הנתונים מקטע אחר מקטע ברצף.

התנהגות מתודה זו על sockets ללא חסימה אינה מוגדרת. עקב כך, ב-MicroPython, מומלץ להשתמש במתודה write() במקום, אשר בעלת אותה מדיניות ”ללא short writes“ עבור sockets חוסמים, ותחזיר את מספר הבייטים שנשלחו עבור sockets ללא חסימה.

recv(bufsize: int, flags: int = 0) bytes

מקבל נתונים מה-socket. ערך ההחזרה הוא אובייקט bytes המייצג את הנתונים שהתקבלו. הכמות המרבית של נתונים שתתקבל בבת אחת מצוינת על ידי bufsize.

הארגומנט האופציונלי flags הוא OR ביטווייז של דגלי הודעה (MSG_PEEK, MSG_DONTWAIT), בעלי אותה משמעות כמו ב-CPython.

sendto(bytes: bytes, address: Any) int

שולח נתונים ל-socket. ה-socket אינו אמור להיות מחובר ל-socket מרוחק, מכיוון שה-socket היעד מצוין על ידי address.

recvfrom(bufsize: int, flags: int = 0) Tuple[bytes, Tuple]

מקבל נתונים מה-socket. ערך ההחזרה הוא זוג (bytes, address) כאשר bytes הוא אובייקט bytes המייצג את הנתונים שהתקבלו ו-address היא הכתובת של ה-socket השולח את הנתונים.

ראו את הפונקציה recv() להסבר על הארגומנט האופציונלי flags.

setsockopt(level: int, optname: int, value: int | bytes) None

מגדיר את הערך של אפשרות ה-socket הנתונה. הקבועים הסמליים הדרושים מוגדרים במודול socket (SO_* וכו«). ה-value יכול להיות מספר שלם או אובייקט דמוי bytes המייצג חוצץ (buffer).

settimeout(value: float | None) None

מגדיר פסק זמן על פעולות socket חוסמות. ארגומנט הערך יכול להיות מספר נקודה צפה אי-שלילי המבטא שניות, או None. אם ניתן ערך שאינו אפס, פעולות socket עוקבות יעלו חריגת OSError אם תקופת פסק הזמן חלפה לפני שהפעולה הושלמה. אם ניתן אפס, ה-socket מועבר למצב ללא חסימה. אם ניתן None, ה-socket מועבר למצב חוסם.

חלופה ניידת וכללית היא להשתמש באובייקט select.poll. זה מאפשר המתנה למספר אובייקטים בו-זמנית (ולא רק ל-sockets, אלא לאובייקטי stream כלליים התומכים ב-polling). דוגמה:

# Instead of:
s.settimeout(1.0)  # time in seconds
s.read(10)  # may timeout

# Use:
poller = select.poll()
poller.register(s, select.POLLIN)
res = poller.poll(1000)  # time in milliseconds
if not res:
    # s is still not ready for input, i.e. operation timed out

הבדל מ-CPython

CPython מעלה חריגת socket.timeout במקרה של פסק זמן, שהיא תת-מחלקה של OSError. במקום זאת, MicroPython מעלה OSError ישירות. אם תשתמשו ב-except OSError: כדי לתפוס את החריגה, הקוד שלכם יעבוד הן ב-MicroPython והן ב-CPython.

setblocking(flag: bool) None

מגדיר מצב חוסם או ללא חסימה של ה-socket: אם flag הוא false, ה-socket נקבע למצב ללא חסימה, אחרת למצב חוסם.

מתודה זו היא קיצור עבור קריאות settimeout() מסוימות:

  • sock.setblocking(True) שקול ל-sock.settimeout(None)

  • sock.setblocking(False) שקול ל-sock.settimeout(0)

makefile(mode: str = 'rb', buffering: int = 0, /) Any

מחזיר אובייקט קובץ המשויך ל-socket. הסוג המדויק המוחזר תלוי בארגומנטים שניתנו ל-makefile(). התמיכה מוגבלת למצבים בינאריים בלבד (»rb«, »wb«, ו-»rwb«). הארגומנטים של CPython: encoding, errors ו-newline אינם נתמכים.

הבדל מ-CPython

מכיוון ש-MicroPython אינו תומך בזרמים מאוחסנים בחוצץ (buffered), ערכי הפרמטר buffering מתעלמים מהם ומתייחסים אליהם כאילו היו 0 (ללא חוצץ).

הבדל מ-CPython

סגירת אובייקט הקובץ המוחזר על ידי makefile() תסגור גם את ה-socket המקורי.

read(size: int | None = None) bytes

קורא עד size בייטים מה-socket. מחזיר אובייקט bytes. אם size אינו ניתן, הוא קורא את כל הנתונים הזמינים מה-socket עד EOF; ככזה, המתודה לא תחזור עד שה-socket ייסגר. פונקציה זו מנסה לקרוא כמה שיותר נתונים כמבוקש (ללא ”short reads“). ייתכן שזה לא יתאפשר עם socket ללא חסימה, ואז יוחזרו פחות נתונים.

readinto(buf: bytearray | memoryview, nbytes: int | None = None) int

קורא בייטים לתוך buf. אם nbytes מצוין אז קורא לכל היותר אותו מספר בייטים. אחרת, קורא לכל היותר len(buf) בייטים. בדיוק כמו read(), מתודה זו עוקבת אחר מדיניות ”ללא short reads“.

ערך החזרה: מספר הבייטים שנקראו ואוחסנו לתוך buf.

readline() bytes

קורא שורה, המסתיימת בתו שורה חדשה.

ערך החזרה: השורה שנקראה.

write(buf: bytes) int

כותב את חוצץ הבייטים ל-socket. פונקציה זו תנסה לכתוב את כל הנתונים ל-socket (ללא ”short writes“). ייתכן שזה לא יתאפשר עם socket ללא חסימה, והערך המוחזר יהיה קטן מאורך buf.

ערך החזרה: מספר הבייטים שנכתבו.

הערה

MicroPython אינו מממש socket.error. ל-CPython יש חריגת socket.error שאינה מומלצת לשימוש שהיא כינוי של OSError; ב-MicroPython, השתמשו ב-OSError ישירות כדי לתפוס שגיאות הקשורות ל-socket.