我正在测试一个扭曲的客户端服务器。有三个相同的服务器,每个服务器通过sendall()发送大量数据。有一个客户端实现一个反应堆并接收如下数据:
Task 1: got 60 bytes of poetry from 127.0.0.1:10000
Task 2: got 10 bytes of poetry from 127.0.0.1:10001
Task 3: got 10 bytes of poetry from 127.0.0.1:10002
Task 1: got 30 bytes of poetry from 127.0.0.1:10000
Task 3: got 10 bytes of poetry from 127.0.0.1:10002
Task 2: got 10 bytes of poetry from 127.0.0.1:10001
...
Task 1: 3003 bytes of poetry
Task 2: 623 bytes of poetry
Task 3: 653 bytes of poetry
Got 3 poems in 0:00:10.134220
我很困惑客户端知道(1)在for
中执行poetry_main()
循环直到收到所有数据,即使每次客户端收到一小块后发生socket.error
来自服务器(2)的数据使用doRead
函数;除了类函数定义之外,它不会被引用。如果有人可以解释这个将是很好的!
客户
# This is the Twisted Get Poetry Now! client, version 1.0.
# NOTE: This should not be used as the basis for production code.
# It uses low-level Twisted APIs as a learning exercise.
import datetime, errno, optparse, socket
from twisted.internet import main
def parse_args():
usage = """usage: %prog [options] [hostname]:port ...
This is the Get Poetry Now! client, Twisted version 1.0.
Run it like this:
python get-poetry.py port1 port2 port3 ...
If you are in the base directory of the twisted-intro package,
you could run it like this:
python twisted-client-1/get-poetry.py 10001 10002 10003
to grab poetry from servers on ports 10001, 10002, and 10003.
Of course, there need to be servers listening on those ports
for that to work.
"""
parser = optparse.OptionParser(usage)
_, addresses = parser.parse_args()
if not addresses:
print parser.format_help()
parser.exit()
def parse_address(addr):
if ':' not in addr:
host = '127.0.0.1'
port = addr
else:
host, port = addr.split(':', 1)
if not port.isdigit():
parser.error('Ports must be integers.')
return host, int(port)
return map(parse_address, addresses)
class PoetrySocket(object):
poem = ''
def __init__(self, task_num, address):
self.task_num = task_num
self.address = address
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.connect(address)
self.sock.setblocking(0)
# tell the Twisted reactor to monitor this socket for reading
from twisted.internet import reactor
reactor.addReader(self)
def fileno(self):
try:
return self.sock.fileno()
except socket.error:
return -1
def connectionLost(self, reason):
self.sock.close()
# stop monitoring this socket
from twisted.internet import reactor
reactor.removeReader(self)
# see if there are any poetry sockets left
for reader in reactor.getReaders():
if isinstance(reader, PoetrySocket):
return
reactor.stop() # no more poetry
def doRead(self):
bytes = ''
count = 0
while True:
count += 1
try:
bytesread = self.sock.recv(1024)
if not bytesread:
break
else:
bytes += bytesread
print ('bytesread read: ', bytes)
except socket.error, e:
if e.args[0] == errno.EWOULDBLOCK:
break
return main.CONNECTION_LOST
print ('count read: ', count)
print ('task? : ', bytes)
if not bytes:
print 'Task %d finished' % self.task_num
return main.CONNECTION_DONE
else:
msg = 'Task %d: got %d bytes of poetry from %s'
print msg % (self.task_num, len(bytes), self.format_addr())
self.poem += bytes
def logPrefix(self):
return 'poetry'
def format_addr(self):
host, port = self.address
return '%s:%s' % (host or '127.0.0.1', port)
def poetry_main():
addresses = parse_args()
start = datetime.datetime.now()
sockets = [PoetrySocket(i + 1, addr) for i, addr in enumerate(addresses)]
from twisted.internet import reactor
reactor.run()
elapsed = datetime.datetime.now() - start
for i, sock in enumerate(sockets):
print 'Task %d: %d bytes of poetry' % (i + 1, len(sock.poem))
print 'Got %d poems in %s' % (len(addresses), elapsed)
if __name__ == '__main__':
poetry_main()
服务器
# This is the blocking version of the Slow Poetry Server.
import optparse, os, socket, time
def parse_args():
usage = """usage: %prog [options] poetry-file
This is the Slow Poetry Server, blocking edition.
Run it like this:
python slowpoetry.py <path-to-poetry-file>
If you are in the base directory of the twisted-intro package,
you could run it like this:
python blocking-server/slowpoetry.py poetry/ecstasy.txt
to serve up John Donne's Ecstasy, which I know you want to do.
"""
parser = optparse.OptionParser(usage)
help = "The port to listen on. Default to a random available port."
parser.add_option('--port', type='int', help=help)
help = "The interface to listen on. Default is localhost."
parser.add_option('--iface', help=help, default='localhost')
help = "The number of seconds between sending bytes."
parser.add_option('--delay', type='float', help=help, default=.7)
help = "The number of bytes to send at a time."
parser.add_option('--num-bytes', type='int', help=help, default=10)
options, args = parser.parse_args()
if len(args) != 1:
parser.error('Provide exactly one poetry file.')
poetry_file = args[0]
if not os.path.exists(args[0]):
parser.error('No such file: %s' % poetry_file)
return options, poetry_file
def send_poetry(sock, poetry_file, num_bytes, delay):
"""Send some poetry slowly down the socket."""
inputf = open(poetry_file)
while True:
bytes = inputf.read(num_bytes)
if not bytes: # no more poetry :(
sock.close()
inputf.close()
return
print 'Sending %d bytes' % len(bytes)
try:
sock.sendall(bytes) # this is a blocking call
except socket.error:
sock.close()
inputf.close()
return
time.sleep(delay)
def serve(listen_socket, poetry_file, num_bytes, delay):
while True:
sock, addr = listen_socket.accept()
print 'Somebody at %s wants poetry!' % (addr,)
send_poetry(sock, poetry_file, num_bytes, delay)
def main():
options, poetry_file= parse_args()
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((options.iface, options.port or 0))
sock.listen(5)
print 'Serving %s on port %s.' % (poetry_file, sock.getsockname()[1])
serve(sock, poetry_file, options.num_bytes, options.delay)
if __name__ == '__main__':
main()