socket编程【3】SocketServer模块

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还提供了其他一些方法,我们可以重载这些方法以实现特定功能:

  • verify_request(request, client_address):返回一个布尔值,如果该值为True ,则该请求将被处理,反之请求将被拒绝。此功能可以重写来实现对服务器的访问控制。默认的实现始终返回True。client_address可以限定客户端,比如只处理指定ip区间的请求,常用。
  • *process_request(request, client_address) *:调用finish_request()创建RequestHandlerClass的实例。如果需要,此功能可以创建新的进程或线程来处理请求,ForkingMixIn和ThreadingMixIn类做到这点,常用。
  • 实际处理RequestHandlerClass发起的请求并调用其handle()方法, 常用。

请求处理器

处理器接收数据并决定如何操作。它负责在socket层之上实现协议(i.e., HTTP, XML-RPC, or AMQP),读取数据,处理并写反应。可以重载的方法如下:

  • setup(): 准备请求处理. 默认什么都不做,StreamRequestHandler中会创建文件类似的对象以读写socket.
  • handle(): 处理请求。解析传入的请求,处理数据,并发送响应。默认什么都不做。常用变量:self.request,self.client_address,self.server。
  • finish(): 环境清理。默认什么都不做,如果setup产生异常,不会执行finish。

通常只需要重载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  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  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 , ('127.0.0.1', 63246)
EchoServer:finish request , ('127.0.0.1', 63246)
EchoRequestHandler:__init__
EchoRequestHandler:setup
EchoRequestHandler:handle
EchoRequestHandler:finish
EchoServer:close request 
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"

你可能感兴趣的:(socket编程【3】SocketServer模块)