阻塞I/O的socket服务端
使用socket模块与concurrent.futures实现阻塞式I/O的socket服务端。
开启多个子线程,每个子线程单独负责一个链接,这意味着该服务器最大的并发量取决于你CPU能够打开的最大有效线程数:
import socket
import threading
import concurrent.futures
class TcpServer:
def __init__(self, bind_addr=("localhost", 8001), allow_reuse_address=False):
self.socket = socket.socket()
self.backlog = 5
self.bufsize = 1024
self.server_addr = bind_addr
self.allow_reuse_address = allow_reuse_address
# 默认创建的线程数为cpu核心数 * 5
self.executor = concurrent.futures.ThreadPoolExecutor()
def run_server(self):
"""运行服务"""
self.server_bind()
self.server_activate()
self.handle_request()
def server_bind(self):
"""绑定地址"""
if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_addr)
def server_activate(self):
"""开启监听"""
self.socket.listen(self.backlog)
def handle_request(self):
"""处理链接"""
while 1:
# 阻塞点1:accept()函数会导致程序卡住,直至有新的链接请求到来
conn, addr = self.socket.accept()
self.executor.submit(
self.handle_communicate, conn, addr)
def handle_communicate(self, conn, addr):
"""多线程处理通信"""
thName = threading.current_thread().name
print(f"{addr} connect server, handle thread : {thName}")
while 1:
try:
# 阻塞点2:recv()函数会导致程序卡住,直至有新的信息放入conn双向链接通道中
data = conn.recv(self.bufsize)
if not data:
raise Exception(
f"{addr} close connect, handle thread : {thName}")
conn.send(data.upper())
except Exception as e:
self.close_connect(conn, e)
break
def close_connect(self, conn, msg):
"""关闭链接"""
conn.close()
print(msg)
if __name__ == "__main__":
server = TcpServer(allow_reuse_address=True)
server.run_server()
非阻塞I/O的socket服务端
将上述的socket服务端改为非阻塞的:
import socket
import threading
import concurrent.futures
class TcpServer:
def __init__(self, bind_addr=("localhost", 8001), allow_reuse_address=False):
self.socket = socket.socket()
# 设置为非阻塞
self.socket.setblocking(False)
self.backlog = 5
self.bufsize = 1024
self.server_addr = bind_addr
self.allow_reuse_address = allow_reuse_address
# 默认创建的线程数为cpu核心数 * 5
self.executor = concurrent.futures.ThreadPoolExecutor()
def run_server(self):
"""运行服务"""
self.server_bind()
self.server_activate()
self.handle_request()
def server_bind(self):
"""绑定地址"""
if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_addr)
def server_activate(self):
"""开启监听"""
self.socket.listen(self.backlog)
def handle_request(self):
"""处理链接"