美文网首页python进阶
socket编程【3】SocketServer模块

socket编程【3】SocketServer模块

作者: nummycode | 来源:发表于2016-11-21 21:05 被阅读302次

    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 <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"
    

    相关文章

      网友评论

      本文标题:socket编程【3】SocketServer模块

      本文链接:https://www.haomeiwen.com/subject/kviupttx.html