11.5.5 回送示例
下面这个例子实现了一对简单的服务器/请求处理器,将接受TCP连接,并回送客户发送的所有数据。首先来看看请求处理器。
import logging
import sys
import socketserver
logging.basicConfig(level=logging.DEBUG,
format='%(name)s:%(message)s',
)
class EchoRequestHandler(socketserver.BaseRequestHandler):
def __init__(self,request,client_address,server):
self.logger = logging.getLogger('EchoRequestHandler')
self.logger.debug('__init__')
socketserver.BaseRequestHandler.__init__(self,request,
client_address,
server)
return
def setup(self):
self.logger.debug('handle')
# Echo the data bvack to the client.
data = self.request.recv(1024)
self.logger.debug('recv()->"%s"',data)
self.request.send(data)
return
def finish(self):
self.logger.debug('finish')
return socketserver.BaseRequestHandler.finish(self)
# 真正需要实现的只有一个方法,即EchoRequestHandler.handle(),不过这里包含了前面
# 提到的所有方法以便展示调用顺序。EchoServer类的工作与TCPServer相同,只不过在
# 调用各个方法时会记录日志。
class EchoServer(socketserver.TCPServer):
def __init__(self,server_address,handler_class=EchoRequestHandler,
):
self.logger = logging.getLogger('EchoServer')
self.logger.debug('__init__')
socketserver.TCPServer.__init__(self,server_address,
handler_class)
return
def server_activate(self):
self.logger.debug('server_activate')
socketserver.TCPServer.server_activate(self)
return
def server_forever(self,poll_interval=0.5):
self.logger.debug('waiting for request')
self.logger.info(
'Handling requests,press <Ctrl-C> to quit'
)
socketserver.TCPServer.serve_forever(self,poll_interval)
return
def handle_request(self):
self.logger.debug('handle_request')
return socketserver.TCPServer.handle_request(self)
def verify_request(self,request,client_address):
self.logger.debug('verify_request(%s,%s)',
request,client_address)
return socketserver.TCPServer.verify_request(
self,request,client_address,
)
def process_request(self,request,client_address):
self.logger.debug('process_request(%s,%s)',
request,client_address)
return socketserver.TCPServer.process_request(
self,request,client_address,
)
def server_close(self):
self.logger.debug('server_close')
return socketserver.TCPServer.server_close(self)
def finish_request(self,request,client_address):
self.logger.debug('finish_request(%s,%s)',
request,client_address)
return socketserver.TCPServer.finish_request(
self,request,client_address,
)
def close_request(self,request_address):
self.logger.debug('close_request(%s)',request_address)
return socketserver.TCPServer.close_request(
self,request_address,
)
def shutdown(self):
self.logger.debug('shutdown()')
return socketserver.TCPServer.shutdown(self)
# 最后一步是增加一个主程序,这会建立服务器,使它在一个线程中运行,并且向这个
# 服务器发送数据以展示回送数据时会调用哪些方法。
if __name__ == '__main__':
import socket
import threading
address = ('localhost',0) # Let the kernel assign a port.
server = EchoServer(address,EchoRequestHandler)
ip,port = server.server_address # What port was assigned?
# Start the server in a thread.
t = threading.Thread(target=server.serve_forever)
t.setDaemon(True) # Don't hang on exit.
t.start()
logger = logging.getLogger('client')
logger.info('Server on %s:%s',ip,port)
# Connect to the server.
logger.debug('creating socket')
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
logger.debug('conneting to server')
s.connect((ip,port))
# Send the data.
message = 'Hello,world'.encode()
logger.debug('sending data: %r',message)
len_sent = s.send(message)
# Receive a response.
logger.debug('waiting for response')
response = s.recv(len_sent)
logger.debug('response from server:%r',response)
# Clean up.
server.shutdown()
logger.debug('closing socket')
s.close()
logger.debug('done')
server.socket.close()
运行这个程序会生成以下输出:
每次程序运行时使用的端口号都会改变,因为内核会自动分配可用的端口。要让服务器每次监听一个特定的端口,需要在地址元组中提供该端口号而不是0。
以下是这个服务器的“压缩”版本,这里没有日志记录调用。请求处理器类中只需要提供handle()方法。
import socketserver
class EchoRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
# Echo the data back to the client.
data = self.request.recv(1024)
self.request.send(data)
return
if __name__ == '__main__':
import socket
import threading
address = ('localhost',0) # Let the kernel assign a port.
server = socketserver.TCPServer(address,EchoRequestHandler)
ip,port = server.server_address # What port was assigned?
t = threading.Thread(target=server.serve_forever)
t.setDaemon(True) # Don't hang on exit.
t.start()
# Connect to the server.
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((ip,port))
# Send the data.
message = 'Hello,world'.encode()
print('Sending : {!r}'.format(message))
len_sent = s.send(message)
# Receive a response.
response = s.recv(len_sent)
print('Received: {!r}'.format(response))
# Clean up.
server.shutdown()
s.close()
server.socket.close()
这里并不需要特殊的服务器类,因为TCPServer会处理所有服务器需求。
运行结果: