我有一台tcp服务器,它使用select调用从客户端多路读取。
我有一个客户端类(MClient),用于管理传入数据包的解码
while(1)
rlist, wlist, xlist = select( input_sockets, output_sockets, [] , 1)
for insock in rlist: #any clients????
if insock is server_socket:
new_socket, addr = server_socket.accept()
input_sockets.append(new_socket)
clients[insock.fileno()] = MClient(new_socket, addr, client_id) #dict of clients
else:
data = insock.recv(512)
if data:
clients[insock.fileno()].ProcessPacket(data)
else:
input_sockets.remove(insock)
del clients[insock.fileno()]
#handle writing to sockets
for outsock in wlist:
....not done yet
#do some other stuff not associated with the socket
我对如何处理将数据发送回客户端的困惑,即如何写入列表'output_sockets'。我正在考虑在我的MClient对象中设置一个标志,该标志指示我有要发送回客户端的数据。然后,我将在服务器循环中检查每个客户端,以查看是否设置了此标志,然后使用相应的套接字填充output_list。当套接字可用于写操作时,我将调用适当的客户端写函数。
这个方案看起来不是很优雅,我想在主服务器循环中处理编写。我将如何完成?
谢谢
最佳答案
这是我前一段时间写的,以了解有关使用单个线程处理多个连接的信息。它绝不是完美的,但可以说明您想要做什么。客户端对象管理连接的读写流,并确保服务器在正确的select()列表中具有客户端套接字。这实现了一个简单的协议(protocol),其中消息以换行符终止。 pumpXXXX()函数仅阻止读取/写入流并管理读取/写入缓冲区。仅当在缓冲区中找到换行符时,才处理完整消息。
import socket
import select
class Client(object):
'''This object is created for each client connection. It tracks
what has been read, what has been written, and processes complete
messages terminated by newlines. It responds by returning the
original message wrapped in square brackets and terminated by a
newline. '''
def __init__(self,who,sock,server):
'''who - client address
sock - client socket
server - server object for this client
'''
self.who = who
self.readbuf = ''
self.writbuf = ''
self.server = server
self.sock = sock
def close(self):
'''Removes client from server's reader/writer queues and
closes the connection.'''
self.sock.close()
if self.sock in self.server.readers:
self.server.readers.remove(self.sock)
if self.sock in self.server.writers:
self.server.writers.remove(self.sock)
self.server.data.pop(self.sock)
def pumprecv(self):
'''Server calls pumprecv() when something is readable from the
client socket. The data is appended to the client's read
buffer.mro Complete messages (if any) are then removed from
the buffer and processed.'''
try:
tmp = self.sock.recv(1000)
except socket.error,e:
print 'recv',e
self.close()
else:
if tmp:
self.readbuf += tmp
# Complete messages are processed
while '\n' in self.readbuf:
msg,self.readbuf = self.readbuf.split('\n',1)
print self.who,msg
self.writbuf += '[' + msg + ']\n'
# New data to send. Make sure client is in the
# server's writer queue.
if self.sock not in self.server.writers:
self.server.writers.append(self.sock)
else:
self.close()
def pumpsend(self):
try:
# send some data. tmp is #chars sent (may not be all in writbuf).
tmp = self.sock.send(self.writbuf)
except socket.error,e:
print 'send:',e
self.close()
else:
# Removed sent characters from writbuf.
self.writbuf = self.writbuf[tmp:]
# If writbuf is empty, remove socket from server's write queue.
if not self.writbuf:
self.server.writers.remove(self.sock)
class Server(object):
def __init__(self,ip='127.0.0.1',port=9999):
self.ssock = socket.socket()
self.ssock.bind((ip,port))
self.ssock.listen(5)
self.readers = [self.ssock]
self.data = {}
self.writers = []
self.quit = False
def pumpaccept(self):
'''Called when server socket is readable to accept a
connection and create a Client object.'''
csock,who = self.ssock.accept()
print 'Connected %s:%d' % who
self.readers.append(csock)
self.data[csock] = Client(who,csock,self)
def serve(self):
while not self.quit or self.writers:
readable,writable,other = select.select(self.readers,self.writers,[],1.0)
# Operate on copies of the queues since the pumpXXX() commands can modify the lists.
if self.ssock in readable[:]:
self.pumpaccept()
readable.remove(self.ssock)
for reader in readable[:]:
self.data[reader].pumprecv()
for writer in writable[:]:
self.data[writer].pumpsend()
if not readable and not writable and not other:
print '.',
if __name__ == '__main__':
srv = Server()
srv.serve()
我通过在一个控制台中启动服务器,然后在其他控制台中运行以下代码来测试多个连接来进行测试。建立多个连接,从不同的窗口交替发送,并发送部分消息以查看服务器如何响应。
>>> from socket import *
>>> s=socket()
>>> s.connect(('localhost',9999))
>>> s.send('one\ntwo\nthree')
13
>>> s.send('\nfour\n')
6
>>> s.recv(1024)
'[one]\n[two\three]\n[four]\n'
>>> s.close()
输出应如下所示:
. . . . . . . . . . . . . . . . . . . Connected 127.0.0.1:1514
. . . . . . . . . ('127.0.0.1', 1514) one
. . . . . . . ('127.0.0.1', 1514) two
. . . ('127.0.0.1', 1514) three
('127.0.0.1', 1514) four
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .