在 Python TCP 服务器中,可以使用 select
调用来复用来自客户端的读写操作。当有客户端连接到服务器时,服务器会创建一个 MClient
对象来管理该客户端的收发数据包。服务器会不断地循环,检查是否有可读或可写的套接字,并相应地调用 MClient
对象的 ProcessPacket
或 write
方法。
但是,如果需要向客户端发送数据,该怎么做呢?一种方法是在 MClient
对象中设置一个标志,表示有数据要发送给客户端。然后,在服务器循环中,检查每个客户端是否设置了这个标志,并将相应的套接字添加到 output_sockets
列表中。当套接字可写时,就可以调用相应的客户端的 write
方法将数据发送出去。
这种方法虽然可行,但并不优雅,因为需要在服务器循环中不断地检查每个客户端的标志。有没有办法直接在服务器循环中处理写入操作呢?
- 解决方案
一种更优雅的方法是使用 callback
函数。当一个客户端有数据要发送时,可以将其 write
方法作为 callback
函数注册到服务器的 output_sockets
列表中。当服务器循环发现一个套接字可写时,它会自动调用相应的 callback
函数,将数据发送出去。
下面是一个使用 callback
函数的示例代码:
import socket
import select
class MClient(object):
def __init__(self, sock, addr, client_id):
self.sock = sock
self.addr = addr
self.client_id = client_id
self.data_to_send = b''
def ProcessPacket(self, data):
# Process the incoming data packet
def write(self, data):
# Append the data to the data_to_send buffer
self.data_to_send += data
class Server(object):
def __init__(self, ip, port):
self.ip = ip
self.port = port
self.ssock = socket.socket()
self.ssock.bind((ip, port))
self.ssock.listen(5)
self.readers = [self.ssock]
self.writers = []
self.callbacks = {}
self.data = {}
def pumpaccept(self):
csock, who = self.ssock.accept()
print('Connected %s:%d' % who)
self.readers.append(csock)
self.data[csock] = MClient(csock, who, client_id)
def serve(self):
while True:
readable, writable, other = select.select(self.readers, self.writers, [], 1.0)
if self.ssock in readable:
self.pumpaccept()
readable.remove(self.ssock)
for reader in readable:
self.data[reader].ProcessPacket(reader.recv(1024))
for writer in writable:
self.callbacks[writer]()
self.writers.remove(writer)
if __name__ == '__main__':
srv = Server('127.0.0.1', 9999)
srv.serve()
在上面的代码中,当一个客户端有数据要发送时,它会调用 write
方法将数据添加到 data_to_send
缓冲区中。然后,它会将自己的套接字和 write
方法作为 callback
函数注册到服务器的 callbacks
字典中。当服务器循环发现该套接字可写时,它会自动调用相应的 callback
函数,将数据发送出去。
这样,就可以在服务器循环中直接处理写入操作,而不需要不断地检查每个客户端的标志。