> php教程 > php手册 > 본문

SocketServer 모듈의 Python 심층 분석(1)(V2.7.11)

WBOY
풀어 주다: 2016-09-08 08:29:06
원래의
1161명이 탐색했습니다.

1. 소개(번역)

일반 소켓 서버 클래스

이 모듈은 다양한 측면에서 서버를 정의하려고 시도합니다.

소켓 기반 서버의 경우:
--주소 가족:
- AF_INET{,6}: IP 소켓(기본값)
- AF_UNIX: Unix 도메인 소켓
- 기타, AF_DECNET( 참조)(일반적으로 사용되지 않음)
--소켓 유형:
- SOCK_STREAM(신뢰할 수 있는 연결 TCP)
- SOCK_DGRAM(UDP)

요청 기반 서버의 경우:
-- 추가 요청을 하기 전에 클라이언트 주소를 인증해야 합니다. (이렇게 하면 인증될 때까지 요청을 해야 하는 모든 프로세스가 효과적으로 차단됩니다.)
-- 여러 요청을 처리하는 방법:
- 동기식(한 번에 하나의 요청만 처리 가능)
- 포크(요청을 처리하기 위해 새 프로세스 포크)
- 스레딩(요청을 처리하기 위해 새 스레드 생성)

이 모듈의 다양한 클래스 중에서 가장 간단한 서버 유형은 동기 TCP/IP 서버입니다. 이는 좋지 않은 클래스 디자인이지만 디자인의 유형 개념 중 일부를 보존합니다.

다음은 5개 클래스의 상속 다이어그램이며, 그 중 4개는 4가지 유형의 동기화 서버를 나타냅니다.
​​​​---------------
              베이스서버 |
​​​​---------------
|
            v
--------------------------- | TCP서버 |------->| UnixStreamServer |
--------------------------- |
            v
​​​​​---------------​​​​--------------- -----------
| UDP서버 |------>| UnixDatagramServer |
​​​​​---------------​​​​--------------- -----------
                             
참고: UnixDatagramServer는 UnixStreamServer가 아닌 UDPServer에서 상속됩니다. IP와 Unix 스트림 서버의 유일한 차이점은 두 서버 클래스의 내용 대부분이 단순한 반복입니다.


ForkingMixIn 및 TreadingMixIn 믹스인 클래스에 대해 포크 및 스레딩을 생성할 수 있습니다. 예: 스레딩 UDP 서버 클래스는 다음과 같이 생성됩니다. 클래스 ThreadingUDPServer(ThreadingMixIn, UDPServer): 통과 (자세한 내용은 아래 예시 참조)


Mix-in 이 클래스는 UDPServer를 정의하는 메서드를 재정의하므로 먼저 구현해야 합니다. 다른 멤버 변수를 설정하면 기본적인 서버 구축 방법도 변경됩니다.

서비스를 구현하려면 기본 클래스 BaseRequestHandler에서 해당 핸들 메서드를 재정의해야 합니다. 그런 다음 서비스 클래스를 재정의된 Handle 메서드 클래스와 결합하여 새 서비스 클래스를 실행합니다.

요청 처리 클래스의 TCP 및 UDP 메서드는 서로 다릅니다. 이는 요청 처리 하위 클래스 StreamRequestHandler 또는 DatagramRequestHandler를 사용하여 숨길 수 있습니다.

물론 다른 방법도 생각해 볼 수 있습니다.
예를 들어 서비스에 수정 요청된 메모리 상태가 포함되어 있는 경우 포크 서버를 사용하는 것은 의미가 없습니다. 왜냐하면 하위 프로세스의 수정은 상위 프로세스의 초기화 상태에 영향을 주지 않고 상위 프로세스는 초기화 상태에 영향을 미치지 않기 때문입니다. 수정된 매개변수를 다른 하위 프로세스에 전달하지 마세요. 이 경우 스레딩 서버를 사용할 수 있으며 두 요청이 동시에 도착하여 서버 상태에서 충돌을 일으키는 것을 방지하기 위해 "잠금"을 사용해야 할 가능성이 높습니다.
또한 HTTP 서버 등을 구축하는 경우 모든 데이터는 외부(예: 파일 시스템)에 저장되며, 클라이언트의 요청이 처리되고 클라이언트가 데이터를 읽는 속도가 느린 경우 동기 클래스는 서비스가 응답하지 않게 되어 시간이 오래 걸릴 수 있습니다.
어떤 경우에는 요청 동기화에 적절한 방법이 필요할 수 있지만 하위 프로세스에서 요청을 완료하려면 요청 데이터의 영향을 받을 수 있습니다. 이는 동기화 서버를 사용하고 요청 처리 클래스의 Handle 메서드에 포크 프로세스를 명시적으로 지정하여 달성할 수 있습니다.
여러 동시 요청을 처리하는 또 다른 방법은 완료된 요청의 명시적 형식을 유지하는 것입니다. select() 메서드를 사용하여 다음에 응답해야 하는 요청을 결정하거나 새 요청을 처리할지 여부를 결정합니다. 이는 스트리밍에 매우 중요합니다. 각 클라이언트가 오랫동안 연결을 설정해야 할 때 서비스를 제공합니다. (스레드와 하위 프로세스를 사용하지 않는다는 전제)

2. 모든 수업 방식 사용
<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>])
로그인 후 복사


3. BaseServer 및 BaseRequestHandler


Python은 네트워크 서비스를 두 가지 주요 클래스로 추상화합니다. 하나는 연결 관련 네트워크 작업을 처리하는 데 사용되는 Server 클래스이고, 다른 하나는 데이터 관련 작업을 처리하는 데 사용되는 RequestHandler 클래스입니다. 그리고 멀티 프로세스 또는 멀티 스레딩을 구현하기 위해 서버를 확장하기 위한 두 개의 MixIn 클래스를 제공합니다. 네트워크 서비스를 구축할 때 Server와 RequestHandler는 분리되지 않습니다. RequestHandler의 인스턴스 개체는 Server와 함께 작동합니다.

3.1 BaseServer 분석
외부에서 호출할 수 있는 BaseServer 메서드:
- __init__(서버_주소, RequestHandlerClass)
- Serve_forever(poll_interval=0.5)
- 종료()
-handle_request() #serve_forever()를 사용하지 않는 경우
- fileno() -> int # for select()
즉, init를 통해 초기화하고 Serve_forever() 및 handler_request() 메서드를 외부에 제공할 수 있습니다.

3.1.1 초기화 초기화

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
로그인 후 복사

__init__의 주요 기능은 서버 객체를 생성하고 server_address 및 RequestHandlerClass를 초기화하는 것입니다.
server_address는 호스트 주소와 포트를 포함하는 튜플입니다.


3.1.2 서브_영원히
    <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()
로그인 후 복사

여기서는 select() 함수가 사용됩니다. 즉, server_forever는 선택 폴링에 사용되는 시간을 나타내는 poll_interval=0.5 매개변수를 수락한 다음 무한 루프에 들어갑니다. 이 루프에서는 poll_interval마다 Poll을 선택합니다. 초(여기서는 차단)로 네트워크 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>
로그인 후 복사

 

英文说明已经说的很明确,该方法处理的是一个非阻塞请求,首先通过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><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>
로그인 후 복사

 


로그인 후 복사
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>
로그인 후 복사

 

上面已经提到,如果你没有用到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>
로그인 후 복사

 

      以上描述说明,所有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>
로그인 후 복사

 


 在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>
로그인 후 복사

 

继承自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>
로그인 후 복사

 

    最主要的功能是根据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>
로그인 후 복사

 

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





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

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


 







 
 
 

 


 

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 추천
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿