Heim > php教程 > php手册 > Hauptteil

Python 深入剖析SocketServer模块(一)(V2.7.11)

WBOY
Freigeben: 2016-09-08 08:29:06
Original
1162 Leute haben es durchsucht

一、简介(翻译)

 

 通用socket server 类

 该模块尽力从各种不同的方面定义server:

 对于socket-based servers:
 -- address family:
    - AF_INET{,6}: IP socket (default)
    - AF_UNIX: Unix domain sockets
    - others, 如 AF_DECNET (见) (不常用)
 -- socket type:
    - SOCK_STREAM (可靠连接 TCP)
    - SOCK_DGRAM (UDP)
 
 对于request-based servers:
-- client address在发出进一步的请求之前需要认证(这实际上把所有需要发出请求的进程在通过认证之前给阻塞住了)
-- 如何处理多请求:
   - 同步 (一次只能处理一个请求)
   - forking (fork一个新的进程来处理一个请求)
   - threading (创建一个新的线程来处理一个请求)
 
   在这个模块的各种类中,最简单的服务器类型就是synchronous TCP/IP server。这是一个糟糕的类设计,但是也保存了一些设计的类型理念。

下面是五个类的继承关系图表,其中的四个代表四种类型的同步服务器:
        +--------------+
        | BaseServer |
        +--------------+
              |
              v
        +------------+            +----------------------+
        | TCPServer |------->| UnixStreamServer |
        +------------+            +----------------------+
              |
              v
        +-------------+           +--------------------------+
        | UDPServer |------->| UnixDatagramServer |
        +-------------+           +--------------------------+
       
注意:UnixDatagramServer继承于UDPServer,而不是UnixStreamServer,IP和Unix stream server之间仅有的差异就是address family,两个服务器类的内容多数是简单的重复。

  forking和threading 可以被创建用于ForkingMixIn和TreadingMixIn mix-in类。例如: threading UDP server类会被如下方式创建:
 class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
 (详细可见后文示例)

  Mix-in 这个类必须首先实现,因为它重写了定义UDPServer的方法。设置不同的成员变量也改变了基本的服务器构造方法。

  为了实现一个服务,你必须从基类BaseRequestHandler中重新定义它的handle方法。然后通过把服务类与你重写的Handle方法类结合,以此运行新的服务类。

  请求处理类的TCP和UDP的方式是不同的,这个可以通过使用请求处理的子类StreamRequestHandler或者DatagramRequestHandler来隐藏。

当然,你还可以思考其他的方法。
    例如,如果服务中包含请求修改的内存的状态,那么使用forking server没有任何意义(因为在子进程中修改将不对父进程的初始化状态有影响,父进程也不会把这个修改的参数传递给其他子进程)。这种情况下,你可以使用threading server,而且你更有可能需要用到“锁”,以此来避免两个请求同时到达而使服务器状态产生冲突。
    此外,如果你在搭建如HTTP服务器等,所有的数据都会存储在外部(如文件系统中),当客户端的一项请求被处理时,并且客户端的读取数据的速度很慢,synchronous class将会使服务不做出响应,这可能需要维持很长时间。
    在一些情况下,请求同步可能需要恰当的方法,但是为了在子进程中完成请求要受到请求数据的影响。这可以通过使用同步服务器来实现,并且在请求处理类中的Handle方法中明确指定fork的进程。
    另一种处理多个同时发生的请求的方法是维系一张明确的完成请求的表单,使用select()方法来判定哪个请求应该在接下来做出响应(或者判断是否要处理新到来的请求),当每一个客户端需要建立很长时间的连接时,这对于stream services来说非常重要。(前提是不使用线程和子进程的方法)

二、用到的所有的类方法
<span style="color: #0000ff;">import</span><span style="color: #000000;"> socket
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> select
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> sys
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> os
</span><span style="color: #0000ff;">import</span><span style="color: #000000;"> errno
</span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
    </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> threading
</span><span style="color: #0000ff;">except</span><span style="color: #000000;"> ImportError:
    </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> dummy_threading as threading

</span><span style="color: #800080;">__all__</span> = [<span style="color: #800000;">"</span><span style="color: #800000;">TCPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">UDPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">ForkingUDPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">ForkingTCPServer</span><span style="color: #800000;">"</span><span style="color: #000000;">,
           </span><span style="color: #800000;">"</span><span style="color: #800000;">ThreadingUDPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">ThreadingTCPServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">BaseRequestHandler</span><span style="color: #800000;">"</span><span style="color: #000000;">,
           </span><span style="color: #800000;">"</span><span style="color: #800000;">StreamRequestHandler</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">DatagramRequestHandler</span><span style="color: #800000;">"</span><span style="color: #000000;">,
           </span><span style="color: #800000;">"</span><span style="color: #800000;">ThreadingMixIn</span><span style="color: #800000;">"</span>, <span style="color: #800000;">"</span><span style="color: #800000;">ForkingMixIn</span><span style="color: #800000;">"</span><span style="color: #000000;">]
</span><span style="color: #0000ff;">if</span> hasattr(socket, <span style="color: #800000;">"</span><span style="color: #800000;">AF_UNIX</span><span style="color: #800000;">"</span><span style="color: #000000;">):
    </span><span style="color: #800080;">__all__</span>.extend([<span style="color: #800000;">"</span><span style="color: #800000;">UnixStreamServer</span><span style="color: #800000;">"</span>,<span style="color: #800000;">"</span><span style="color: #800000;">UnixDatagramServer</span><span style="color: #800000;">"</span><span style="color: #000000;">,
                    </span><span style="color: #800000;">"</span><span style="color: #800000;">ThreadingUnixStreamServer</span><span style="color: #800000;">"</span><span style="color: #000000;">,
                    </span><span style="color: #800000;">"</span><span style="color: #800000;">ThreadingUnixDatagramServer</span><span style="color: #800000;">"</span>])
Nach dem Login kopieren

 


三、BaseServer和BaseRequestHandler


        Python把网络服务抽象成两个主要的类,一个是Server类,用于处理连接相关的网络操作,另外一个则是RequestHandler类,用于处理数据相关的操作。并且提供两个MixIn 类,用于扩展 Server,实现多进程或多线程。在构建网络服务的时候,Server 和 RequestHandler 并不是分开的,RequestHandler的实例对象在Server 内配合 Server工作。

       3.1 BaseServer分析
  BaseServer可供外部调用的方法:
- __init__(server_address, RequestHandlerClass)
- serve_forever(poll_interval=0.5)
- shutdown()
- handle_request()  # if you do not use serve_forever()
- fileno() -> int   # for select()
即我们可以通过init初始化,对外提供serve_forever()和handle_request()方法

3.1.1 init初始化

timeout =<span style="color: #000000;"> None

    </span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span><span style="color: #000000;">(self, server_address, RequestHandlerClass):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Constructor.  May be extended, do not override.</span><span style="color: #800000;">"""</span><span style="color: #000000;">
        self.server_address </span>=<span style="color: #000000;"> server_address
        self.RequestHandlerClass </span>=<span style="color: #000000;"> RequestHandlerClass
        self.</span><span style="color: #800080;">__is_shut_down</span> =<span style="color: #000000;"> threading.Event()
        self.</span><span style="color: #800080;">__shutdown_request</span> = False
Nach dem Login kopieren

 

__init__的主要作用就是创建server对象,并初始化server_address和RequestHandlerClass。
server_address就是包含主机地址和端口的元组。
 


3.1.2 serve_forever
 
    <span style="color: #0000ff;">def</span> serve_forever(self, poll_interval=0.5<span style="color: #000000;">):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Handle one request at a time until shutdown.

        Polls for shutdown every poll_interval seconds. Ignores
        self.timeout. If you need to do periodic tasks, do them in
        another thread.
        </span><span style="color: #800000;">"""</span><span style="color: #000000;">
        self.</span><span style="color: #800080;">__is_shut_down</span><span style="color: #000000;">.clear()
        </span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
            </span><span style="color: #0000ff;">while</span> <span style="color: #0000ff;">not</span> self.<span style="color: #800080;">__shutdown_request</span><span style="color: #000000;">:
                
                r, w, e </span>=<span style="color: #000000;"> _eintr_retry(select.select, [self], [], [],
                                       poll_interval)
                </span><span style="color: #0000ff;">if</span> self <span style="color: #0000ff;">in</span><span style="color: #000000;"> r:
                    self._handle_request_noblock()
        </span><span style="color: #0000ff;">finally</span><span style="color: #000000;">:
            self.</span><span style="color: #800080;">__shutdown_request</span> =<span style="color: #000000;"> False
            self.</span><span style="color: #800080;">__is_shut_down</span>.set()
Nach dem Login kopieren

 

   这里用到了select()函数,即server_forever接受了一个poll_interval=0.5的参数传入,这表示用于select轮询的时间,然后进入一个无限循环中,在这个循环中,select每隔poll_interval秒轮询一次(阻塞于此),以此来进行网络IO的监听。一旦有新的网络连接请求到来,则会调用_handle_request_noblock()方法处理新的连接。


3.1.3 _handle_request_noblock()
    <span style="color: #0000ff;">def</span><span style="color: #000000;"> _handle_request_noblock(self):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Handle one request, without blocking.

        I assume that select.select has returned that the socket is
        readable before this function was called, so there should be
        no risk of blocking in get_request().
        </span><span style="color: #800000;">"""</span>
        <span style="color: #0000ff;">try</span><span style="color: #000000;">:
            request, client_address </span>=<span style="color: #000000;"> self.get_request()
        </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> socket.error:
            </span><span style="color: #0000ff;">return</span>
        <span style="color: #0000ff;">if</span><span style="color: #000000;"> self.verify_request(request, client_address):
            </span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
                self.process_request(request, client_address)
            </span><span style="color: #0000ff;">except</span><span style="color: #000000;">:
                self.handle_error(request, client_address)
                self.shutdown_request(request)</span>
Nach dem Login kopieren

 

英文说明已经说的很明确,该方法处理的是一个非阻塞请求,首先通过get_request()方法获取连接,具体实现在其子类,一旦获取了连接,立即调用verify_request认证连接信息,通过认证,则调用process_request()方法处理请求,如果中途出现错误,则调用handle_error处理错误,同时,调用shutdown_request()方法结束这个连接。
那下面我们就先看上面提到的其他几个函数调用:
    <span style="color: #0000ff;">def</span><span style="color: #000000;"> verify_request(self, request, client_address):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Verify the request.  May be overridden.

        Return True if we should proceed with this request.

        </span><span style="color: #800000;">"""</span>
        <span style="color: #0000ff;">return</span><span style="color: #000000;"> True


    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> process_request(self, request, client_address):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Call finish_request.

        Overridden by ForkingMixIn and ThreadingMixIn.

        </span><span style="color: #800000;">"""</span><span style="color: #000000;">
        self.finish_request(request, client_address)
        self.shutdown_request(request)

    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> handle_error(self, request, client_address):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Handle an error gracefully.  May be overridden.

        The default is to print a traceback and continue.

        </span><span style="color: #800000;">"""</span>
        <span style="color: #0000ff;">print</span> <span style="color: #800000;">'</span><span style="color: #800000;">-</span><span style="color: #800000;">'</span>*40
        <span style="color: #0000ff;">print</span> <span style="color: #800000;">'</span><span style="color: #800000;">Exception happened during processing of request from</span><span style="color: #800000;">'</span><span style="color: #000000;">,
        </span><span style="color: #0000ff;">print</span><span style="color: #000000;"> client_address
        </span><span style="color: #0000ff;">import</span><span style="color: #000000;"> traceback
        traceback.print_exc() </span><span style="color: #008000;">#</span><span style="color: #008000;"> XXX But this goes to stderr!</span>
        <span style="color: #0000ff;">print</span> <span style="color: #800000;">'</span><span style="color: #800000;">-</span><span style="color: #800000;">'</span>*40


    <span style="color: #0000ff;">def</span><span style="color: #000000;"> shutdown_request(self, request):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called to shutdown and close an individual request.</span><span style="color: #800000;">"""</span><span style="color: #000000;">
        self.close_request(request)</span><pre name="<span" style="color: #800000;">"<span style="color: #800000;">code</span><span style="color: #800000;">"</span> <span style="color: #0000ff;">class</span>=<span style="color: #800000;">"</span><span style="color: #800000;">python</span><span style="color: #800000;">"</span>>    

    <span style="color: #0000ff;">def</span><span style="color: #000000;"> finish_request(self, request, client_address):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Finish one request by instantiating RequestHandlerClass.</span><span style="color: #800000;">"""</span><span style="color: #000000;">
        self.RequestHandlerClass(request, client_address, self)</span>
Nach dem Login kopieren

 


Nach dem Login kopieren
verify_request()方法对request进行验证,通常会被子类重写。
process_request需要注意一下,它被ForkingMixIn 和 ThreadingMixIn重写,因此是mixin的入口,ForkingMixIn和ThreadingMixIn分别进行多进程和多线程的配置,并且调用finish_request()完成请求,调用shutdown_request()结束请求。
handle_error也可以被子类重写,打印错误的信息和客户端地址。
finish_request()处理完毕请求,在__init__中创建requestHandler对象,并通过requestHandler做出具体的处理。

3.1.4 handle_request()
    <span style="color: #0000ff;">def</span><span style="color: #000000;"> handle_request(self):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Handle one request, possibly blocking.

        Respects self.timeout.
        </span><span style="color: #800000;">"""</span>
        <span style="color: #008000;">#</span><span style="color: #008000;"> Support people who used socket.settimeout() to escape</span>
        <span style="color: #008000;">#</span><span style="color: #008000;"> handle_request before self.timeout was available.</span>
        timeout =<span style="color: #000000;"> self.socket.gettimeout()
        </span><span style="color: #0000ff;">if</span> timeout <span style="color: #0000ff;">is</span><span style="color: #000000;"> None:
            timeout </span>=<span style="color: #000000;"> self.timeout
        </span><span style="color: #0000ff;">elif</span> self.timeout <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None:
            timeout </span>=<span style="color: #000000;"> min(timeout, self.timeout)
        fd_sets </span>=<span style="color: #000000;"> _eintr_retry(select.select, [self], [], [], timeout)
        </span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> fd_sets[0]:
            self.handle_timeout()
            </span><span style="color: #0000ff;">return</span><span style="color: #000000;">
        self._handle_request_noblock()</span>
Nach dem Login kopieren

 

上面已经提到,如果你没有用到server_forever()方法,说明你希望使用的是阻塞请求来处理连接,如英文描述所说,该方法只是处理一个阻塞的请求,仍然使用select()方法轮询来监听网络连接,但是需要考虑时间超时影响,一旦超时,调用handle_timeout()方法处理超时,一般在子类重写该方法;如果在超时之前监听到了网络的连接请求,则同server_forever一样,调用_handle_request_noblock()方法,完成对新的连接的请求处理。

3.2 BaseRequestHandler分析
<span style="color: #0000ff;">class</span><span style="color: #000000;"> BaseRequestHandler:

    </span><span style="color: #800000;">"""</span><span style="color: #800000;">Base class for request handler classes.

    This class is instantiated for each request to be handled.  The
    constructor sets the instance variables request, client_address
    and server, and then calls the handle() method.  To implement a
    specific service, all you need to do is to derive a class which
    defines a handle() method.

    The handle() method can find the request as self.request, the
    client address as self.client_address, and the server (in case it
    needs access to per-server information) as self.server.  Since a
    separate instance is created for each request, the handle() method
    can define arbitrary other instance variariables.

    </span><span style="color: #800000;">"""</span>

    <span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span><span style="color: #000000;">(self, request, client_address, server):
        self.request </span>=<span style="color: #000000;"> request
        self.client_address </span>=<span style="color: #000000;"> client_address
        self.server </span>=<span style="color: #000000;"> server
        self.setup()
        </span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
            self.handle()
        </span><span style="color: #0000ff;">finally</span><span style="color: #000000;">:
            self.finish()

    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> setup(self):
        </span><span style="color: #0000ff;">pass</span>

    <span style="color: #0000ff;">def</span><span style="color: #000000;"> handle(self):
        </span><span style="color: #0000ff;">pass</span>

    <span style="color: #0000ff;">def</span><span style="color: #000000;"> finish(self):
        </span><span style="color: #0000ff;">pass</span>
Nach dem Login kopieren

 

      以上描述说明,所有requestHandler都继承BaseRequestHandler基类,该类会处理每一个请求。在__init__中初始化实例变量request、client_address、server,然后调用handle()方法完成请求处理。那么,我们唯一需要做的就是重写好Handle()方法,处理所有的请求。
 
总结:构建一个网络服务,需要一个BaseServer用于处理网络IO,同时在内部创建requestHandler对象,对所有具体的请求做处理。

四、各种子类

4.1 由BaseServer衍生的子类

4.1.1 TCPServer
<span style="color: #0000ff;">class</span><span style="color: #000000;"> TCPServer(BaseServer):
    address_family </span>=<span style="color: #000000;"> socket.AF_INET

    socket_type </span>=<span style="color: #000000;"> socket.SOCK_STREAM

    request_queue_size </span>= 5<span style="color: #000000;">

    allow_reuse_address </span>=<span style="color: #000000;"> False

    </span><span style="color: #0000ff;">def</span> <span style="color: #800080;">__init__</span>(self, server_address, RequestHandlerClass, bind_and_activate=<span style="color: #000000;">True):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Constructor.  May be extended, do not override.</span><span style="color: #800000;">"""</span><span style="color: #000000;">
        BaseServer.</span><span style="color: #800080;">__init__</span><span style="color: #000000;">(self, server_address, RequestHandlerClass)
        self.socket </span>=<span style="color: #000000;"> socket.socket(self.address_family,
                                    self.socket_type)
        </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> bind_and_activate:
            </span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
                self.server_bind()
                self.server_activate()
            </span><span style="color: #0000ff;">except</span><span style="color: #000000;">:
                self.server_close()
                </span><span style="color: #0000ff;">raise</span>

    <span style="color: #0000ff;">def</span><span style="color: #000000;"> server_bind(self):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called by constructor to bind the socket.

        May be overridden.

        </span><span style="color: #800000;">"""</span>
        <span style="color: #0000ff;">if</span><span style="color: #000000;"> self.allow_reuse_address:
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, </span>1<span style="color: #000000;">)
        self.socket.bind(self.server_address)
        self.server_address </span>=<span style="color: #000000;"> self.socket.getsockname()

    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> server_activate(self):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called by constructor to activate the server.

        May be overridden.

        </span><span style="color: #800000;">"""</span><span style="color: #000000;">
        self.socket.listen(self.request_queue_size)

    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> server_close(self):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called to clean-up the server.

        May be overridden.

        </span><span style="color: #800000;">"""</span><span style="color: #000000;">
        self.socket.close()

    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> fileno(self):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Return socket file number.

        Interface required by select().

        </span><span style="color: #800000;">"""</span>
        <span style="color: #0000ff;">return</span><span style="color: #000000;"> self.socket.fileno()

    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_request(self):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Get the request and client address from the socket.

        May be overridden.

        </span><span style="color: #800000;">"""</span>
        <span style="color: #0000ff;">return</span><span style="color: #000000;"> self.socket.accept()

    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> shutdown_request(self, request):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called to shutdown and close an individual request.</span><span style="color: #800000;">"""</span>
        <span style="color: #0000ff;">try</span><span style="color: #000000;">:
            </span><span style="color: #008000;">#</span><span style="color: #008000;">explicitly shutdown.  socket.close() merely releases</span>
            <span style="color: #008000;">#</span><span style="color: #008000;">the socket and waits for GC to perform the actual close.</span>
<span style="color: #000000;">            request.shutdown(socket.SHUT_WR)
        </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> socket.error:
            </span><span style="color: #0000ff;">pass</span> <span style="color: #008000;">#</span><span style="color: #008000;">some platforms may raise ENOTCONN here</span>
<span style="color: #000000;">        self.close_request(request)

    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> close_request(self, request):
        </span><span style="color: #800000;">"""</span><span style="color: #800000;">Called to clean up an individual request.</span><span style="color: #800000;">"""</span><span style="color: #000000;">
        request.close()</span>
Nach dem Login kopieren

 


 在BaseServer基础上增加了一个TCP的socket连接,使用server_bind、server_activate、server_close处理TCP启停等操作,同时增加了get_request、shutdown_request、close_request处理客户端请求。

4.1.2 UDPServer
<span style="color: #0000ff;">class</span><span style="color: #000000;"> UDPServer(TCPServer):

    </span><span style="color: #800000;">"""</span><span style="color: #800000;">UDP server class.</span><span style="color: #800000;">"""</span><span style="color: #000000;">

    allow_reuse_address </span>=<span style="color: #000000;"> False

    socket_type </span>=<span style="color: #000000;"> socket.SOCK_DGRAM

    max_packet_size </span>= 8192

    <span style="color: #0000ff;">def</span><span style="color: #000000;"> get_request(self):
        data, client_addr </span>=<span style="color: #000000;"> self.socket.recvfrom(self.max_packet_size)
        </span><span style="color: #0000ff;">return</span><span style="color: #000000;"> (data, self.socket), client_addr

    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> server_activate(self):
        </span><span style="color: #008000;">#</span><span style="color: #008000;"> No need to call listen() for UDP.</span>
        <span style="color: #0000ff;">pass</span>

    <span style="color: #0000ff;">def</span><span style="color: #000000;"> shutdown_request(self, request):
        </span><span style="color: #008000;">#</span><span style="color: #008000;"> No need to shutdown anything.</span>
<span style="color: #000000;">        self.close_request(request)

    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> close_request(self, request):
        </span><span style="color: #008000;">#</span><span style="color: #008000;"> No need to close anything.</span>
        <span style="color: #0000ff;">pass</span>
Nach dem Login kopieren

 

继承自TCPServer,将socket改为了SOCK_DGRAM型,并修改了get_request,用于从SOCK_DGRAM中获取request。同时server_activate、shutdown_request、close_request都改成了空(UDP不需要),比TCP简单一些。

4.2 由BaseRequestHandler衍生的子类

4.2.1 StreamRequestHandler
<span style="color: #0000ff;">class</span><span style="color: #000000;"> StreamRequestHandler(BaseRequestHandler):
    rbufsize </span>= -1<span style="color: #000000;">
    wbufsize </span>=<span style="color: #000000;"> 0
    timeout </span>=<span style="color: #000000;"> None
    disable_nagle_algorithm </span>=<span style="color: #000000;"> False
    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> setup(self):
        self.connection </span>=<span style="color: #000000;"> self.request
        </span><span style="color: #0000ff;">if</span> self.timeout <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None:
            self.connection.settimeout(self.timeout)
        </span><span style="color: #0000ff;">if</span><span style="color: #000000;"> self.disable_nagle_algorithm:
            self.connection.setsockopt(socket.IPPROTO_TCP,
                                       socket.TCP_NODELAY, True)
        self.rfile </span>= self.connection.makefile(<span style="color: #800000;">'</span><span style="color: #800000;">rb</span><span style="color: #800000;">'</span><span style="color: #000000;">, self.rbufsize)
        self.wfile </span>= self.connection.makefile(<span style="color: #800000;">'</span><span style="color: #800000;">wb</span><span style="color: #800000;">'</span><span style="color: #000000;">, self.wbufsize)

    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> finish(self):
        </span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> self.wfile.closed:
            </span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
                self.wfile.flush()
            </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> socket.error:
                </span><span style="color: #008000;">#</span><span style="color: #008000;"> An final socket error may have occurred here, such as</span>
                <span style="color: #008000;">#</span><span style="color: #008000;"> the local error ECONNABORTED.</span>
                <span style="color: #0000ff;">pass</span><span style="color: #000000;">
        self.wfile.close()
        self.rfile.close()</span>
Nach dem Login kopieren

 

    最主要的功能是根据socket生成了读写socket用的两个文件对象(可以理解为句柄)rfile和wfile

4.2.2 DatagramRequestHandler
<span style="color: #0000ff;">class</span><span style="color: #000000;"> DatagramRequestHandler(BaseRequestHandler):

    </span><span style="color: #008000;">#</span><span style="color: #008000;"> XXX Regrettably, I cannot get this working on Linux;</span>
    <span style="color: #008000;">#</span><span style="color: #008000;"> s.recvfrom() doesn't return a meaningful client address.</span>

    <span style="color: #800000;">"""</span><span style="color: #800000;">Define self.rfile and self.wfile for datagram sockets.</span><span style="color: #800000;">"""</span>

    <span style="color: #0000ff;">def</span><span style="color: #000000;"> setup(self):
        </span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
            </span><span style="color: #0000ff;">from</span> cStringIO <span style="color: #0000ff;">import</span><span style="color: #000000;"> StringIO
        </span><span style="color: #0000ff;">except</span><span style="color: #000000;"> ImportError:
            </span><span style="color: #0000ff;">from</span> StringIO <span style="color: #0000ff;">import</span><span style="color: #000000;"> StringIO
        self.packet, self.socket </span>=<span style="color: #000000;"> self.request
        self.rfile </span>=<span style="color: #000000;"> StringIO(self.packet)
        self.wfile </span>=<span style="color: #000000;"> StringIO()

    </span><span style="color: #0000ff;">def</span><span style="color: #000000;"> finish(self):
        self.socket.sendto(self.wfile.getvalue(), self.client_address)</span>
Nach dem Login kopieren

 

同样是生成rfile和wfile,但UDP不直接关联socket。这里的rfile是直接由从UDP中读取的数据生成的,wfile则是新建了一个StringIO,用于写数据。





(题目起的有点大,部分剖析的不好,等之后再往祖坟上刨。。。。^-^)

参考博客:http://www.cnblogs.com/tuzkee/p/3573210.html
             http://www.jianshu.com/p/357e436936bf


 







 
 
 

 


 

Quelle:php.cn
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Empfehlungen
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage