socketserver 원본 읽기 노트

9063 단어
cketserver는python 표준 라이브러리의 모듈로 네트워크 서버 프레임워크(A framework for network servers)로서 네트워크 서버 작성 절차를 간소화하고 표준 라이브러리의 다른 모듈과 제3자 모듈의 밑바닥 서버 기반을 맡는 역할을 한다.
socketserver는python3의 새로운 이름으로python2에서 SocketServer라고 하는데 이번에 읽은 것은 0.4버전입니다.
이 프레임워크는 주로 ServerClass와 RequestHandlerClass 두 종류를 사용한다.
  • ServerClass 처리 서버와 클라이언트의 통신
  • RequestHandlerClass 처리 데이터의 해석, 수신 및 발송;주요 비즈니스 논리
  • ServerClass
  • BaseServer 추상적 기류
  • TCPServer 처리 흐름식 플러그인
  • UnixStreamServer 로컬 처리 스트림 소켓 처리, UNIX 플랫폼만 적용
  • UDPServer 처리 데이터 패키지
  • UnixDatagramServer 로컬 처리 데이터 보고 소켓을 처리하고 UNIX 플랫폼만 적용
  • RequestHandlerClass
  • BaseRequestHandler처리기류
  • StreamRequestHandler 처리 흐름식 플러그인
  • DatagramRequestHandler 처리 데이터 패키지
  • 통신과 데이터 처리에 대한 결합을 통해 서로 다른 조합의 서버가 서로 다른 플러그인을 처리하는 데 사용할 수 있지만 이런 서버는 동기화 처리 요청일 뿐이고 비동기화 처리 요청을 실현하려면 사용해야 한다MixINClass
  • ForkingMixIn 다중 프로세스를 이용하여 비동기화를 실현하고 요청을 받은 후fork한 프로세스가 응답 처리
  • ThreadingMixIn 다중 스레드를 이용하여 이상을 실현하고 요청을 받은 후 하나의 스레드를 사용하여 응답 처리
  • MixIN, Mixin 문맹퇴치반

    Server 클래스의 상속 관계

            +------------+
            | BaseServer |
            +------------+
                  |
                  v
            +-----------+        +------------------+
            | TCPServer |------->| UnixStreamServer |
            +-----------+        +------------------+
                  |
                  v
            +-----------+        +--------------------+
            | UDPServer |------->| UnixDatagramServer |
            +-----------+        +--------------------+
    

    UDPSERver는 TCPServer를 계승하고, TCPServer는 BaseServer를 계승하며, UnixStreamServer는 TCPServer를 계승하고, UnixDatagramServer는 UDPSERver를 계승한다.
    if hasattr(socket, 'AF_UNIX'):
    
        class UnixStreamServer(TCPServer):
            address_family = socket.AF_UNIX
    
        class UnixDatagramServer(UDPServer):
            address_family = socket.AF_UNIX
    

    원본 코드를 보면 Unix Stream Server와 TCPServer, Unix Datagram Server와 UDPSERver는 단지 바뀌었다address_family.
    소켓에 관하여.AF_UNIX는 주로 같은 기기의 프로세스 간 통신에 사용됩니다.AF_INET 도메인 및 AF_UNIX 도메인 소켓 통신 원리 비교
    if hasattr(os, "fork"):
        class ForkingUDPServer(ForkingMixIn, UDPServer): pass
        class ForkingTCPServer(ForkingMixIn, TCPServer): pass
    
    class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
    class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
    

    다중 프로세스 서버와 다중 스레드 서버는 TCPServer, UDPSERver와 Forking MixIn, Threading MixIn을 조합한 것일 뿐이다.
    class BaseRequestHandler:
        def __init__(self, request, client_address, server):
            self.request = request
            self.client_address = client_address
            self.server = server
            self.setup()
            try:
                self.handle()
            finally:
                self.finish()
    
        def setup(self):
            pass
    
        def handle(self):
            pass
    
        def finish(self):
            pass
    

    RequestHandler 클래스의 상속 관계

              +--------------------+        +----------------------+
              | BaseRequestHandler |------->| StreamRequestHandler |
              +--------------------+        +----------------------+
                        |
                        v
            +------------------------+       
            | DatagramRequestHandler |
            +------------------------+ 
    

    Stream Request Handler와 Datagram Request Handler는 Base Request Handler를 계승하는 것으로 setup과finish를 다시 썼고 본질적으로 캐시로 플러그인에 대한 서비스를 제공합니다.

    호출 프로세스


    예를 들어 호출 절차를 보다
    import socketserver
    
    class MyTCPHandler(socketserver.BaseRequestHandler):
    
        def handle(self):
            # self.request is the TCP socket connected to the client
            self.data = self.request.recv(1024).strip()
            print("{} wrote:".format(self.client_address[0]))
            print(self.data)
            # just send back the same data, but upper-cased
            self.request.sendall(self.data.upper())
    
    if __name__ == "__main__":
        HOST, PORT = "localhost", 9999
    
        with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
            server.serve_forever()
    

    여기에는 BaseRequestHandler로부터 계승된 MyTCPHandler를 정의하고 Handle 방법을 다시 쓴 다음 호스트, 포트와 함께 TCPServer로 전송합니다.
    class BaseServer:
        def __init__(self, server_address, RequestHandlerClass):
            """Constructor.  May be extended, do not override."""
            self.server_address = server_address
            self.RequestHandlerClass = RequestHandlerClass
            self.__is_shut_down = threading.Event()
            self.__shutdown_request = False
    
    class TCPServer(BaseServer):
        def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
            """Constructor.  May be extended, do not override."""
            BaseServer.__init__(self, server_address, RequestHandlerClass)
            self.socket = socket.socket(self.address_family,
                                        self.socket_type)
            if bind_and_activate:
                try:
                    self.server_bind()
                    self.server_activate()
                except:
                    self.server_close()
                    raise
    

    1. TCPServer 실례화는 자체server_bind() 방법을 호출하여 전송된 server_address 파라미터를 귀속시켰고, server_activate() 방법은 BaseServer 클래스에서 다시 쓰는 것으로 주로 요청 대기열 크기의 설정입니다.
    2. 서비스를 시작하고 BaseServer류의 serve_forever() 방법을 호출하여 요청을 끊임없이 감청하고 요청이 있을 때 _handle_request_noblock() 방법으로 요청 처리를 완성한 다음에 service_actions() 처리 후의 조작을 호출한다.이 방법은 ForkingMixIn 클래스에서 다시 작성되어 요청 처리를 완료하는 프로세스를 해결하는 데 사용됩니다.
        def _handle_request_noblock(self):
            try:
                request, client_address = self.get_request()
            except OSError:
                return
            if self.verify_request(request, client_address):
                try:
                    self.process_request(request, client_address)
                except Exception:
                    self.handle_error(request, client_address)
                    self.shutdown_request(request)
                except:
                    self.shutdown_request(request)
                    raise
            else:
                self.shutdown_request(request)
    
    _handle_request_noblock() 메서드가 요청을 처리할 때 다음 메서드가 호출됩니다.
  • get_request()방법은 TCPServer류에서 정의, 호출socket.accept()방법, 반환request파라미터와 client_address파라미터
  • verify_request(request, client_address) 방법은 BaseServer 클래스에서 정의되고 하위 클래스에서 다시 써서 검증 처리를 할 수 있다
  • process_request(request, client_address)방법 요청 처리 함수, 호출finish_request()방법, ForkingMixIn류와 ThreadingMixIn류에서 다시 쓰기, 프로세스나 루틴으로 처리
  • finish_request(request, client_address) 방법의 구체적인 요청 처리 과정, 여기는 사용자 정의MyTCPHandler 클래스의 실례화입니다. 이 클래스는 BaseRequestHandler 클래스를 계승하고 그 정의를 따르며 자신이 실례화될 때 handle() 방법
  • 을 호출합니다.
  • shutdown_request(request)방법은 TCPServer류에서 정의되고 먼저 request.shutdown(socket.SHUT_WR), 오류가 발생하면 close_request(request)방법으로 request.close()한다. UDPServer류에서 이 함수는 다시 쓴다pass떨어진다
  • 3. 서비스를 종료하고 서비스를 종료하려면 수동으로 BaseServer 클래스의 shutdown() 방법을 호출할 수 있습니다. 예를 들어 직접 사용Ctrl-C
        def shutdown(self):
            self.__shutdown_request = True
            self.__is_shut_down.wait()
    

    주로 __shutdown_request 표지 교체, 대기 차단serve_forever() 라인 복귀
    이것이 예제 TCPServer 의 라이프 사이클입니다.

    약간의 문제

    request.shutdowm(socket.SHUT_WR)request.close()차이
    1. shutdown을 호출하면 지정한 링크를 닫고,close는 설명자의 인용 계수기가 0일 때까지 기다려야 링크를 닫습니다. 2.close는 두 개의 링크를 동시에 닫고, shutdown 값은 지정한 링크를 닫습니다.close 후 파일 설명자는 더 이상 사용할 수 없습니다. (인용 기수는 0이고 자원 방출), shutdown 후 파일 설명자는 사용할 수 있습니다.
    구체적이다
  • socketshutdown과close 함수의 차이
  • TCP 세 번의 악수와 네 번의 손 흔들기 과정
  • poll, epoll 선택
    if hasattr(selectors, 'PollSelector'):
        _ServerSelector = selectors.PollSelector
    else:
        _ServerSelector = selectors.SelectSelector
    
    ##  Lib/selectors.py
    
    # Choose the best implementation, roughly:
    #    epoll|kqueue|devpoll > poll > select.
    # select() also can't accept a FD > FD_SETSIZE (usually around 1024)
    if 'KqueueSelector' in globals():
        DefaultSelector = KqueueSelector
    elif 'EpollSelector' in globals():
        DefaultSelector = EpollSelector
    elif 'DevpollSelector' in globals():
        DefaultSelector = DevpollSelector
    elif 'PollSelector' in globals():
        DefaultSelector = PollSelector
    else:
        DefaultSelector = SelectSelector
    

    cketserver 모듈에서 수동으로 poll 라이브러리를 선택했습니다. 모듈이 제공하는 Default Selector를 사용하여 선택하는 것이 아닙니다.

    좋은 웹페이지 즐겨찾기