8.3.7 压缩网络数据
下一个例子中的代码会响应文件名请求,它将文件的一个压缩版本写至用来与客户通信套接字。这里认为地做了一些划分,以展示当数据传递到compress()或decompress()时,如果没有得到一个完整的压缩或未压缩输出块,则会缓冲。
import bz2
import logging
import socketserver
import binascii
BLOCK_SIZE =32
class Bz2RequestHandler(socketserver.BaseRequestHandler):
logger = logging.getLogger('Server')
def handle(self):
compressor = bz2.BZ2Compressor()
# Find out which file the client wants.
filename = self.request.recv(1024).decode('utf-8')
self.logger.debug('client asked for: "%s"',filename)
# Send chunks of the file as they are compressed.
with open(filename,'rb') as input:
while True:
block = input.read(BLOCK_SIZE)
if not block:
break
self.logger.debug('RAW %r',block)
compressed = compressor.compress(block)
if compressed:
self.logger.debug(
'SENDING %r',
binascii.hexlify(compressed))
self.request.send(compressed)
else:
self.logger.debug('BUFFERING')
# Send any data being buffered by the compressor.
remaining = compressor.flush()
while remaining:
to_send = remaining[:BLOCK_SIZE]
remaining =remaining[BLOCK_SIZE:]
self.logger.debug('FLUSHING %r',
binascii.hexlify(to_send))
self.request.send(to_send)
return
# 主程序在一个线程中结合SocketServer和Bz2RequestHandler以启动一个服务器。
if __name__ == '__main__':
import socket
import sys
from io import StringIO
import threading
logging.basicConfig(level=logging.DEBUG,
format='%(name)s:%(message)s',
)
# Set up a server,running in a separate thread.
address = ('localhost',0) # Let the kernel assign a port.
server = socketserver.TCPServer(address,Bz2RequestHandler)
ip,port = server.server_address # What port was assigned?
t = threading.Thread(target=server.serve_forever)
t.setDaemon(True)
t.start()
logger = logging.getLogger('Client')
# Connect to the server.
logger.info('Contacting server on %s:%s',ip,port)
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((ip,port))
# Ask for a file.
requested_file = (sys.argv[0]
if len(sys.argv) > 1
else 'lorem.txt')
logger.debug('sending filename:"%s"',requested_file)
len_sent = s.send(requested_file.encode('utf-8'))
# Receive a response.
buffer = StringIO()
decompressor = bz2.BZ2Decompressor()
while True:
response = s.recv(BLOCK_SIZE)
if not response:
break
logger.debug('READ %r',binascii.hexlify(response))
# Include any unconsumed data when feeding the
# decompressor.
decompressed = decompressor.decompress(response)
if decompressed:
logger.debug('DECOMPRESSED %r',decompressed)
buffer.write(decompressed.decode('utf-8'))
else:
logger.debug('BUFFERING')
full_response = buffer.getvalue()
lorem = open(requested_file,'rt').read()
logger.debug('response matches file contents: %s',
full_response == lorem)
# Clean up.
server.shutdown()
server.socket.close()
s.close()
然后这个程序打开一个套接字,作为客户与服务器通信,并请求文件(默认为lorem.txt)。
警告:这个实现存在明显的安全隐患。不要在开放的互联网或安全问题可能导致严重影响的环境中运行这个程序。