Python socket编程核心模式
Python socket编程核心模式
socket是网络通信的基础抽象。Python的socket模块直接封装了伯克利套接字API。
创建TCP服务器:
import socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(('localhost', 8888))
server.listen(5)
def handle_client(conn):
data = conn.recv(1024)
conn.send(data) # echo
conn.close()
while True:
conn, addr = server.accept()
handle_client(conn)
TCP服务器创建过程:socket -> bind -> listen -> accept循环。
创建TCP客户端:
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 8888))
client.send(b'Hello')
response = client.recv(1024)
client.close()
TCP客户端:socket -> connect -> send/recv -> close。
UDP服务器:
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(('localhost', 9999))
data, addr = server.recvfrom(1024)
server.sendto(b'Reply', addr)
UDP无连接。recvfrom返回数据和地址,sendto发送到指定地址。
非阻塞IO:
server.setblocking(False)
server.settimeout(5.0) # 或设置超时
try:
conn, addr = server.accept()
except socket.timeout:
print("No connection within 5 seconds")
setblocking(False)使所有操作立即返回(可能抛出BlockingIOError)。settimeout设置超时值。
selectors模块的多路复用:
import selectors
import socket
sel = selectors.DefaultSelector()
server = socket.socket()
server.bind(('localhost', 8888))
server.listen(100)
server.setblocking(False)
sel.register(server, selectors.EVENT_READ, data='accept')
while True:
events = sel.select(timeout=None)
for key, mask in events:
if key.data == 'accept':
conn, addr = key.fileobj.accept()
conn.setblocking(False)
sel.register(conn, selectors.EVENT_READ, data='echo')
else:
data = key.fileobj.recv(1024)
if data:
key.fileobj.send(data)
else:
sel.unregister(key.fileobj)
key.fileobj.close()
selectors模块封装了select/poll/epoll的差异。使用统一的接口处理IO事件。
sendall保证完整发送:
data = b'x' * 100000
sent = client.send(data) # 可能只发送部分数据
client.sendall(data) # 直到全部发送完成
sendall循环调用send直到数据全部发送或出错。
套接字选项:
import socket
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
s.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)
s.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536)
SO_REUSEADDR允许地址重用。TCP_NODELAY禁用Nagle算法。SO_KEEPALIVE启用心跳。
getsockopt查看当前选项值:
bufsize = s.getsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF)
print(f"Send buffer: {bufsize}")
套接字超时异常处理:
import socket
s = socket.socket()
s.settimeout(2.0)
try:
s.connect(('192.168.1.1', 80))
except socket.timeout:
print("Connection timed out")
except ConnectionRefusedError:
print("Connection refused")
except OSError as e:
print(f"Socket error: {e}")
finally:
s.close()
socket.gethostbyname域名解析:
ip = socket.gethostbyname('example.com')
print(f"IP: {ip}")
hostname, aliases, addresses = socket.gethostbyaddr(ip)
print(f"Hostname: {hostname}")
getaddrinfo返回地址信息列表:
info = socket.getaddrinfo('example.com', 80, socket.AF_INET, socket.SOCK_STREAM)
for family, type, proto, canonname, sockaddr in info:
print(f"Family: {family}, Type: {type}, Addr: {sockaddr}")
创建原始套接字(需要root权限):
try:
raw = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)
raw.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
except PermissionError:
print("Raw sockets require root/admin")
