socket --- mô-đun socket

Mô-đun này cung cấp quyền truy cập vào giao diện socket BSD.

Khác biệt so với CPython

Để đạt hiệu quả và nhất quán, các đối tượng socket trong MicroPython triển khai trực tiếp giao diện stream (giống file). Trong CPython, bạn cần chuyển đổi socket thành đối tượng giống file bằng phương thức makefile(). Phương thức này vẫn được MicroPython hỗ trợ (nhưng là no-op), vì vậy khi cần tương thích với CPython, hãy chắc chắn sử dụng nó.

Định dạng địa chỉ socket

Định dạng địa chỉ socket gốc của mô-đun socket là một kiểu dữ liệu mờ được trả về bởi hàm getaddrinfo(), hàm này phải được dùng để phân giải địa chỉ dạng văn bản (bao gồm địa chỉ số):

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)

Sử dụng getaddrinfo() là cách hiệu quả nhất (cả về bộ nhớ và sức mạnh xử lý) và di động nhất để làm việc với địa chỉ.

Mô-đun socket cũng cung cấp cách tương thích CPython để chỉ định địa chỉ bằng cách dùng tuple, như mô tả bên dưới. Trên OpenMV Cam, mô-đun socket được tích hợp sẵn; địa chỉ số có thể được cung cấp trực tiếp theo định dạng tuple, nhưng tên miền phải được phân giải trước bằng getaddrinfo().

Tóm tắt:

  • Luôn dùng getaddrinfo() để phân giải tên máy chủ.

  • Địa chỉ tuple được mô tả dưới đây có thể được sử dụng như lối tắt cho địa chỉ số, cho các thử nghiệm nhanh và sử dụng tương tác.

Định dạng địa chỉ tuple cho mô-đun socket:

  • IPv4: (ipv4_address, port), trong đó ipv4_address là chuỗi địa chỉ IPv4 số theo ký hiệu dấu chấm, ví dụ: "8.8.8.8", và port là số nguyên cổng trong phạm vi 1-65535. Tên miền không được chấp nhận làm ipv4_address; hãy phân giải chúng trước bằng getaddrinfo().

  • IPv6: (ipv6_address, port, flowinfo, scopeid), trong đó ipv6_address là chuỗi địa chỉ IPv6 số theo ký hiệu dấu hai chấm, ví dụ: "2001:db8::1", và port là số nguyên cổng trong phạm vi 1-65535. flowinfo phải là 0. scopeid là mã định danh phạm vi giao diện cho các địa chỉ link-local. Tên miền không được chấp nhận làm ipv6_address; hãy phân giải chúng trước bằng getaddrinfo().

Các hàm

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

Dịch đối số host/port thành một chuỗi các 5-tuple chứa tất cả các đối số cần thiết để tạo socket kết nối đến dịch vụ đó. Các đối số af, typeproto (có cùng nghĩa như đối với hàm socket) có thể được dùng để lọc loại địa chỉ nào được trả về. Nếu một tham số không được chỉ định hoặc bằng không, tất cả các kết hợp địa chỉ có thể được trả về (yêu cầu lọc ở phía người dùng).

Danh sách kết quả gồm các 5-tuple có cấu trúc sau:

(family, type, proto, canonname, sockaddr)

Ví dụ sau đây cho thấy cách kết nối đến một url đã cho:

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])

Cách sử dụng được khuyến nghị của các tham số lọc:

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])

Khác biệt so với CPython

CPython gây ra ngoại lệ socket.gaierror (lớp con của OSError) trong trường hợp lỗi trong hàm này. MicroPython không có socket.gaierror và gây ra OSError trực tiếp. Lưu ý rằng các số lỗi của getaddrinfo() tạo thành một không gian tên riêng biệt và có thể không khớp với các số lỗi từ mô-đun errno. Để phân biệt các lỗi của getaddrinfo(), chúng được biểu diễn bằng các số âm, trong khi các lỗi hệ thống tiêu chuẩn là các số dương (các số lỗi có thể truy cập bằng thuộc tính e.args[0] từ đối tượng ngoại lệ). Việc sử dụng các giá trị âm là một chi tiết tạm thời có thể thay đổi trong tương lai.

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

Chuyển đổi địa chỉ mạng nhị phân bin_addr của họ địa chỉ đã cho af thành biểu diễn dạng văn bản:

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

Chuyển đổi địa chỉ mạng dạng văn bản txt_addr của họ địa chỉ đã cho af thành biểu diễn nhị phân:

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

Các hằng số

socket.AF_INET: int

Họ địa chỉ IPv4.

socket.AF_INET6: int

Họ địa chỉ IPv6.

socket.SOCK_STREAM: int

Kiểu socket luồng (TCP).

socket.SOCK_DGRAM: int

Kiểu socket datagram (UDP).

socket.SOCK_RAW: int

Kiểu socket thô.

socket.IPPROTO_IP: int

Cấp độ giao thức IP. Được dùng làm đối số level cho setsockopt() cùng với các tùy chọn IP_*.

socket.IPPROTO_TCP: int

Giao thức TCP. Bạn không cần truyền cái này cho socket (kiểu socket SOCK_STREAM tự động chọn nó); chỉ có mục đích thực sự duy nhất là làm đối số level cho setsockopt() cùng với các tùy chọn TCP_*.

socket.SOL_SOCKET: int

Cấp độ tùy chọn socket. Được dùng làm đối số level cho setsockopt() cùng với các tùy chọn SO_*.

socket.SO_REUSEADDR: int

Cho phép socket kết buộc với một địa chỉ/cổng vẫn còn ở trạng thái TIME_WAIT.

socket.SO_BROADCAST: int

Cho phép gửi datagram đến địa chỉ broadcast.

socket.SO_KEEPALIVE: int

Bật truyền định kỳ các thăm dò keep-alive trên socket đã kết nối.

socket.SO_SNDTIMEO: int

Thời gian chờ gửi, tính bằng mili giây, được truyền vào như đối số value cho setsockopt().

socket.SO_RCVTIMEO: int

Thời gian chờ nhận, tính bằng mili giây, được truyền vào như đối số value cho setsockopt().

socket.IP_ADD_MEMBERSHIP: int

Tham gia một nhóm multicast. Tùy chọn setsockopt() cấp IPPROTO_IP.

socket.IP_DROP_MEMBERSHIP: int

Rời khỏi một nhóm multicast. Tùy chọn setsockopt() cấp IPPROTO_IP.

socket.TCP_NODELAY: int

Tắt thuật toán Nagle. Tùy chọn setsockopt() cấp IPPROTO_TCP.

socket.MSG_PEEK: int

Cho recv() / recvfrom(): trả về dữ liệu mà không xóa nó khỏi hàng đợi đầu vào.

socket.MSG_DONTWAIT: int

Cho recv() / recvfrom(): thực hiện thao tác ở chế độ không chặn.

Các lớp

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

Tạo một socket mới bằng cách sử dụng họ địa chỉ, kiểu socket và số giao thức đã cho. Chỉ định proto trong hầu hết các trường hợp là không cần thiết (và không được khuyến nghị); đối số type tự động chọn giao thức cần thiết:

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

Đánh dấu socket đóng và giải phóng tất cả tài nguyên. Sau khi điều đó xảy ra, tất cả các thao tác tương lai trên đối tượng socket sẽ thất bại. Phía cuối từ xa sẽ nhận chỉ báo EOF nếu được giao thức hỗ trợ.

Các socket được tự động đóng khi chúng được thu gom rác, nhưng được khuyến nghị close() chúng một cách rõ ràng ngay khi bạn hoàn thành việc làm việc với chúng.

bind(address: Any) None

Kết buộc socket với address. Socket chưa được kết buộc trước đó.

listen(backlog: int = 2) None

Bật máy chủ để chấp nhận kết nối. Nếu backlog được chỉ định, nó phải ít nhất là 0 (nếu nhỏ hơn, nó sẽ được đặt thành 0); và chỉ định số lượng kết nối chưa được chấp nhận mà hệ thống sẽ cho phép trước khi từ chối kết nối mới. Nếu không được chỉ định, một giá trị mặc định hợp lý sẽ được chọn.

accept() Tuple['socket', Tuple]

Chấp nhận một kết nối. Socket phải được kết buộc với một địa chỉ và đang lắng nghe kết nối. Giá trị trả về là một cặp (conn, address) trong đó conn là đối tượng socket mới có thể dùng để gửi và nhận dữ liệu trên kết nối, và address là địa chỉ được kết buộc với socket ở phía còn lại của kết nối.

connect(address: Any) None

Kết nối đến socket từ xa tại address.

send(bytes: bytes) int

Gửi dữ liệu đến socket. Socket phải được kết nối đến socket từ xa. Trả về số byte đã gửi, có thể nhỏ hơn độ dài dữ liệu ("ghi ngắn").

sendall(bytes: bytes) None

Gửi tất cả dữ liệu đến socket. Socket phải được kết nối đến socket từ xa. Không giống như send(), phương thức này sẽ cố gắng gửi tất cả dữ liệu, bằng cách gửi dữ liệu theo từng đoạn liên tiếp.

Hành vi của phương thức này trên các socket không chặn là không xác định. Do đó, trên MicroPython, được khuyến nghị sử dụng phương thức write() thay thế, có cùng chính sách "không ghi ngắn" cho các socket chặn, và sẽ trả về số byte đã gửi trên các socket không chặn.

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

Nhận dữ liệu từ socket. Giá trị trả về là đối tượng bytes đại diện cho dữ liệu đã nhận. Lượng dữ liệu tối đa cần nhận mỗi lần được chỉ định bởi bufsize.

Đối số flags tùy chọn là OR theo bit của các cờ tin nhắn (MSG_PEEK, MSG_DONTWAIT), có cùng nghĩa như trong CPython.

sendto(bytes: bytes, address: Any) int

Gửi dữ liệu đến socket. Socket không nên được kết nối đến socket từ xa, vì socket đích được chỉ định bởi address.

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

Nhận dữ liệu từ socket. Giá trị trả về là một cặp (bytes, address) trong đó bytes là đối tượng bytes đại diện cho dữ liệu đã nhận và address là địa chỉ của socket đang gửi dữ liệu.

Xem hàm recv() để giải thích về đối số flags tùy chọn.

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

Đặt giá trị của tùy chọn socket đã cho. Các hằng số biểu tượng cần thiết được định nghĩa trong mô-đun socket (SO_* v.v.). value có thể là số nguyên hoặc đối tượng giống bytes đại diện cho một bộ đệm.

settimeout(value: float | None) None

Đặt thời gian chờ cho các thao tác socket chặn. Đối số value có thể là một số dấu phẩy động không âm biểu thị giây, hoặc None. Nếu một giá trị khác không được cho, các thao tác socket tiếp theo sẽ gây ra ngoại lệ OSError nếu khoảng thời gian chờ đã trôi qua trước khi thao tác hoàn thành. Nếu đưa vào không, socket được đặt ở chế độ không chặn. Nếu đưa vào None, socket được đặt ở chế độ chặn.

Một giải pháp thay thế di động và chung là sử dụng đối tượng select.poll. Điều này cho phép chờ trên nhiều đối tượng cùng một lúc (và không chỉ trên socket, mà trên các đối tượng stream chung hỗ trợ polling). Ví dụ:

# 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

Khác biệt so với CPython

CPython gây ra ngoại lệ socket.timeout trong trường hợp hết thời gian chờ, đây là lớp con của OSError. MicroPython gây ra OSError trực tiếp thay thế. Nếu bạn dùng except OSError: để bắt ngoại lệ, mã của bạn sẽ hoạt động cả trong MicroPython và CPython.

setblocking(flag: bool) None

Đặt chế độ chặn hoặc không chặn của socket: nếu flag là false, socket được đặt thành không chặn, nếu không thì ở chế độ chặn.

Phương thức này là viết tắt cho một số lệnh gọi settimeout() nhất định:

  • sock.setblocking(True) tương đương với sock.settimeout(None)

  • sock.setblocking(False) tương đương với sock.settimeout(0)

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

Trả về một đối tượng file được liên kết với socket. Kiểu trả về chính xác phụ thuộc vào các đối số được truyền cho makefile(). Hỗ trợ bị giới hạn chỉ ở các chế độ nhị phân ('rb', 'wb', và 'rwb'). Các đối số CPython: encoding, errorsnewline không được hỗ trợ.

Khác biệt so với CPython

Vì MicroPython không hỗ trợ các luồng có bộ đệm, các giá trị của tham số buffering bị bỏ qua và được coi như là 0 (không có bộ đệm).

Khác biệt so với CPython

Đóng đối tượng file được trả về bởi makefile() SẼ đóng socket gốc cùng với nó.

read(size: int | None = None) bytes

Đọc tối đa size byte từ socket. Trả về đối tượng bytes. Nếu size không được cho, nó đọc tất cả dữ liệu có sẵn từ socket cho đến EOF; như vậy phương thức sẽ không trả về cho đến khi socket bị đóng. Hàm này cố gắng đọc nhiều dữ liệu nhất có thể (không có "đọc ngắn"). Tuy nhiên điều này có thể không thực hiện được với socket không chặn, và khi đó sẽ trả về ít dữ liệu hơn.

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

Đọc byte vào buf. Nếu nbytes được chỉ định thì đọc tối đa nhiều byte đó. Nếu không, đọc tối đa len(buf) byte. Giống như read(), phương thức này tuân theo chính sách "không đọc ngắn".

Giá trị trả về: số byte đã đọc và lưu vào buf.

readline() bytes

Đọc một dòng, kết thúc bằng ký tự xuống dòng.

Giá trị trả về: dòng đã đọc.

write(buf: bytes) int

Ghi bộ đệm byte vào socket. Hàm này sẽ cố gắng ghi tất cả dữ liệu vào socket (không có "ghi ngắn"). Tuy nhiên điều này có thể không thực hiện được với socket không chặn, và giá trị trả về sẽ nhỏ hơn độ dài của buf.

Giá trị trả về: số byte đã ghi.

Ghi chú

MicroPython không triển khai socket.error. CPython có ngoại lệ socket.error đã bị phản đối là bí danh của OSError; trong MicroPython, hãy dùng OSError trực tiếp để bắt các lỗi liên quan đến socket.