Python提供了SocketServer用于创建网络服务器。
SocketServer中定义了五个不同的服务器类,它们之间的关系如下:
+------------+
| BaseServer |
+------------+
|
v
+-----------+ +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+ +------------------+
|
v
+-----------+ +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+ +--------------------+
BaseServer是基类,它不能实例化使用,TCPServer使用TCP协议通信,UDPServer使用UDP协议通信,UnixStreamServer和UnixDatagramServer使用Unix域套接字,只适用于UNIX平台。
创建服务器的步骤。首先,你必须创建一个请求处理类,它是BaseRequestHandler的子类并重载其handle()方法。其次,你必须实例化一个服务器类,传入服务器的地址和请求处理程序类。最后,调用handle_request()或serve_forever()。
BaseServer还提供了其他一些方法,我们可以重载这些方法以实现特定功能:
处理器接收数据并决定如何操作。它负责在socket层之上实现协议(i.e., HTTP, XML-RPC, or AMQP),读取数据,处理并写反应。可以重载的方法如下:
通常只需要重载handle。
self.request的类型和数据报或流的服务不同。
对于流服务,self.request是socket 对象;对于数据报服务,self.request是字符串和socket 。
可以在子类StreamRequestHandler或DatagramRequestHandler中重载,重写setup()和finish() ,并提供self.rfile和self.wfile属性。
self.rfile和self.wfile可以读取或写入,以获得请求数据或将数据返回到客户端。
下面实现一个简单的带日志功能的Echo服务器,它接收TCP连接,然后响应客户端发送哦所有数据:
import sys
import logging
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("setup")
return SocketServer.BaseRequestHandler.setup(self)
def handle(self):
self.logger.debug("handle")
data = self.request.recv(1024)
self.request.send(data)
return
def finish(self):
self.logger.debug("finish")
return SocketServer.BaseRequestHandler.finish(self)
# 服务器类
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 serve_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, request, client_address):
self.logger.debug("verify_request %s, %s",request, client_address)
return SocketServer.TCPServer.verify_request(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)
server = EchoServer(address, EchoRequestHandler)
ip, port = server.server_address
t = threading.Thread(target=server.serve_forever)
t.setDaemon(True)
t.start()
logger = logging.getLogger("client")
logger.debug("Server on %s:%s", ip, port)
logger.debug("creating socket")
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
logger.debug("connecting to server")
s.connect((ip, port))
message = "Hello, world"
logger.debug("sending data: %s", message)
len_sent = s.send(message)
logger.debug("waiting for response")
response = s.recv(len_sent)
logger.debug("response from server %s", response)
server.shutdown()
logger.debug("closing socket")
s.close()
logger.debug("done")
server.socket.close()
运行代码,输出以下结果:
EchoServer:__init__
EchoServer:server activate
EchoServer:waiting for request
EchoServer:Handling requests, press <Ctrl-C> to quit
client:Server on 127.0.0.1:63245
client:creating socket
client:connecting to server
client:sending data: Hello, world
client:waiting for response
EchoServer:process request <socket._socketobject object at 0x026FD6C0>, ('127.0.0.1', 63246)
EchoServer:finish request <socket._socketobject object at 0x026FD6C0>, ('127.0.0.1', 63246)
EchoRequestHandler:__init__
EchoRequestHandler:setup
EchoRequestHandler:handle
EchoRequestHandler:finish
EchoServer:close request <socket._socketobject object at 0x026FD6C0>
client:response from server Hello, world
EchoServer:shutdown
client:closing socket
TCPServer,UDPServer,UnixStreamServer,UnixDatagramServer。这4个类是同步进行处理的,另外通过ForkingMixIn和ThreadingMixIn类来支持异步,这两个类重载了process_request()方法,当准备 处理一个请求时,就开始一个新的进程或者线程,从而把具体工作放到进程或者线程中运行。
对于线程,需要使用ThreadingMiXin:
import threading
import socket
import SocketServer
class ThreadEchoRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
data = self.request.recv(1024)
cur_thread = threading.currentThread()
response = "%s:%s" % (cur_thread.getName(), data)
print "ok" , response
self.request.send(response)
return
class ThreadEchoServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
pass
if __name__ == "__main__":
address = ("localhost", 0)
server = ThreadEchoServer(address, ThreadEchoRequestHandler)
ip, port = server.server_address
t = threading.Thread(target=server.serve_forever)
t.setDaemon(True)
t.start()
print "Server loop running in thread: ", t.getName()
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
message = "Hello, world"
len_sent = s.send(message)
response = s.recv(1024)
print "Received:%s" % response
s.close()
server.socket.close()
运行结果如下:
Server loop running in thread: Thread-1
ok Thread-2:Hello, world
Received:Thread-2:Hello, world
对于不同进程,则使用ForkingMixIn:
import os
import SocketServer
class ForkingEchoRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
# Echo the back to the client
data = self.request.recv(1024)
cur_pid = os.getpid()
response = '%s: %s' % (cur_pid, data)
self.request.send(response)
return
class ForkingEchoServer(SocketServer.ForkingMixIn, SocketServer.TCPServer):
pass
if __name__ == '__main__':
import socket
import threading
address = ('localhost', 0) # let the kernel give us a port
server = ForkingEchoServer(address, ForkingEchoRequestHandler)
ip, port = server.server_address # find out what port we were given
t = threading.Thread(target=server.serve_forever)
t.setDaemon(True) # don't hang on exit
t.start()
print 'Server loop running in process:', os.getpid()
# Connect to the server
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((ip, port))
# Send the data
message = 'Hello, world'
print 'Sending : "%s"' % message
len_sent = s.send(message)
# Receive a response
response = s.recv(1024)
print 'Received: "%s"' % response
# Clean up
s.close()
server.socket.close()
运行结果如下:
Server loop running in process: 96038
Sending : "Hello, world"
Received: "96040: Hello, world"