9.11. อ็อบเจกต์ Socket¶
อินเทอร์เฟซ Python สำหรับ transport layer คือคลาส socket.socket socket หนึ่งตัวแทน endpoint หนึ่งปลายของการสนทนาทางเครือข่าย -- ที่อยู่ พอร์ต และโปรโตคอล (UDP หรือ TCP) ที่การสนทนาใช้งาน บทควบคุมฮาร์ดแวร์เปิด instance ของ UART เพื่อสื่อสารบนสาย ส่วนหัวข้อนี้เปิด instance ของ socket เพื่อสื่อสารบนเครือข่าย รูปแบบเหมือนกัน แต่บริการพื้นฐานมีความสามารถมากกว่ามาก
9.11.1. การสร้าง socket¶
อาร์กิวเมนต์สามตัวอธิบาย socket: address family ที่มันใช้, socket type ที่มันมีให้, และ protocol ที่มันใช้ ค่าเริ่มต้นครอบคลุมกรณีที่ส่วนที่เหลือในหัวข้อนี้ใช้:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # IPv4 TCP
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # IPv4 UDP
ค่าคงที่สองตัวที่แอปพลิเคชันต้องเลือกใช้ในเกือบทุกกรณี:
AF_INET-- ที่อยู่ IPv4 (IP ตัวเลขบวกกับพอร์ต) เป็นตัวเลือกที่พบบ่อยที่สุดAF_INET6คือคู่เทียบสำหรับ IPv6SOCK_STREAM-- การเชื่อมต่อ TCP ซึ่งเป็น byte stream ที่เชื่อถือได้SOCK_DGRAMคือคู่เทียบสำหรับ UDP
อาร์กิวเมนต์ที่สาม (protocol) ให้ไว้ที่ศูนย์ ซึ่งจะเลือกค่าเริ่มต้นที่ถูกต้องตามสองอาร์กิวเมนต์แรก constructor ฉบับเต็มอยู่ในเอกสาร socket --- socket module
9.11.2. ที่อยู่บน socket¶
ที่อยู่ socket คือ tuple ของ (host, port)
("192.168.1.50", 80)
("0.0.0.0", 8000)
host คือที่อยู่ IP เป็น string ส่วน port คือจำนวนเต็ม 16 บิตที่อธิบายไว้ใน Ports
สตริง พิเศษ สำหรับ host บางตัวที่ควรรู้จัก:
"0.0.0.0"หมายถึง "ทุก IPv4 interface บนอุปกรณ์นี้" เซิร์ฟเวอร์ที่ bind กับที่อยู่นี้รับการเชื่อมต่อบนที่อยู่ใด ๆ ที่กล้องมี"127.0.0.1"คือ localhost -- traffic ที่ไปยังที่อยู่นี้จะไม่ออกจากอุปกรณ์เลย ใช้ประโยชน์สำหรับการทดสอบ"255.255.255.255"คือที่อยู่ broadcast ท้องถิ่น UDP datagram ที่ส่งไปยังที่อยู่นี้จะไปถึงทุกอุปกรณ์บน segment ท้องถิ่น
ชื่อโดเมนเช่น "example.com" ไม่ใช่ สตริง host ที่ถูกต้องในที่อยู่ socket ต้องแปลงเป็น IP ก่อน หน้า ชื่อและ DNS อธิบายการเรียก getaddrinfo() ที่ทำงานนี้
9.11.3. สองบทบาท¶
วงจรชีวิตของ socket ขึ้นอยู่กับว่ามันอยู่ฝั่งใดของการสนทนา socket แบบ ไคลเอนต์ เรียก connect() (หรือสำหรับ UDP ก็แค่ sendto()) เพื่อสื่อสารกับเซิร์ฟเวอร์ที่รู้จัก socket แบบ เซิร์ฟเวอร์ เรียก bind() เพื่อจองพอร์ต จากนั้นเรียก listen() และ accept() (สำหรับ TCP) หรือ recvfrom() (สำหรับ UDP) เพื่อรับ traffic ที่เข้ามา
constructor socket เดียวกันถูกใช้ในทั้งสองกรณี มีเพียงเมธอดที่เรียกภายหลังเท่านั้นที่ต่างกัน สามหน้าถัดไปจะอธิบายรูปแบบที่ใช้งานได้จริง:
UDP sockets -- ส่งและรับ datagram
TCP sockets -- ไคลเอนต์และเซิร์ฟเวอร์ TCP
Sockets กับ asyncio -- ทุกอย่างข้างต้น แต่อยู่ภายใน event loop ของ
asyncio
9.11.4. การปิด socket¶
ทุก socket เก็บสถานะระบบปฏิบัติการขนาดเล็ก (การจอง port, buffer, สถานะ TCP ของการเชื่อมต่อ) เมื่อแอปพลิเคชันใช้งานเสร็จ close() จะปล่อยสถานะนั้น socket ที่ถูกลืมไว้คือการรั่วไหลช้า ๆ ที่สะสม ในลูปที่เปิดการเชื่อมต่อ การไม่เรียก close จะทำให้ pool ของ socket ที่มีอยู่ของกล้องหมดลงในที่สุด
รูปแบบที่สะอาดที่สุดคือคำสั่ง with
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(addr)
s.send(b"GET / HTTP/1.0\r\n\r\n")
...
# socket is closed automatically here, even on error
Socket รองรับโปรโตคอล context manager ที่อธิบายไว้ในบท Python overview ดังนั้น block ของ with จะรับประกันว่า close() จะถูกเรียกไม่ว่า block จะจบลงตามปกติหรือมีการ raise exception
9.11.5. ข้อมูลอ้างอิง socket¶
หน้านี้และหน้าถัดไปจะอธิบาย API ในรูปแบบเชิงเรื่องราว สำหรับข้อมูลอ้างอิงระดับอาร์กิวเมนต์เต็มรูปแบบของทุกเมธอด ทุก flag และทุก constant ที่โมดูลเปิดเผย ดูได้ที่ socket --- socket module ข้อมูลอ้างอิงยังเป็นที่ที่ควรค้นหาสำหรับการดำเนินงานที่ไม่ค่อยพบ (socket options, multicast group membership, IPv6 scope IDs) ซึ่งหัวข้อนี้ไม่ได้ครอบคลุม