Pythonのsocketモジュールのソースコードの中のいくつかの実現ポイント分析
BaseServerとBaseRequest Handler
Pythonはネットワークプログラミングのために、より高級なパッケージを改善しました。SocketServer.pyは多くのネットワークサービスの種類を提供しています。彼らのデザインはとても優雅です。Pythonはネットワークサービスを二つの主要なクラスに抽象化しています。一つはServer類で、接続に関するネットワーク操作を処理します。もう一つはRequest Handler類で、データ関連の操作を処理します。そして、Serverを拡張するための2つのMixInクラスを提供し、マルチプロセスまたはマルチスレッドを実現します。ネットワークサービスを構築する際には、ServerとRequest Handlerは分離されておらず、Request HandlerのインスタンスオブジェクトはServer内でServer作業に協力します。
モジュールの主要いくつかのServerの関係は以下の通りです。
BaseServer通過init_初期化して、外部にserveを提供します。foreverとhandler_request方法
init初期化:
serve_forever
serverオブジェクトを作成したら、serverオブジェクトを使って無限ループを開く必要があります。以下でserve gauを分析します。foreverのソースコードです。
select関数が返ってきたら、IO接続やデータがあるという意味で_を呼び出します。ハンドルrequest_noblockメソッドです。
BaseRequest Handler分析
すべてのrequest HandlerはBaserequest Handlerベースを継承します。
これでPython全体で提供されたServer方式を紹介しました。まとめて、ネットワークサービスを構築するには、ネットワークIOを処理するためにBaseServerが必要であり、同時に内部でrequest Handlerオブジェクトを作成し、すべての具体的な要求に対して処理を行う。
BaseServer-BaseRequest Handler
TCPServer
TCPServerはBaseServerを引き継ぎ、初期化時に、socketソケットの作成を行いました。
ゲットするrequest
このような一番重要な方法はget_です。requestこの方法は、socketオブジェクトに戻る要求接続を行う。
また、TCPServerは、ファイル記述子を返すためにベースクラスのselect呼び出しに対してfileno方法を提供する。
Stream Request Handler
TCPServerはtcpソケットを使用したネットワークサービスを実現し、Handlerは対応するStreamRequest Handlerである。これはBaseRequest Handlerを継承します。ベースクラスのsetup方法とfinish方法は、接続によってキャッシュファイルの読み書き操作を実現するために書き換えられます。
セットアップ方法:
TCPサービスを実現するにはTCPServerとStreamRequest Handlerを用いて共同で連携する必要がある。大まかな関数呼び出しの流れは以下の通りです。関数呼び出しは括弧で表し、割り当ては括弧ではなく、クラスプレフィックスの表示はシステム呼び出しではありません。
TCPServer-Stream Request Handler
ThreadingMixIn
ThreadingMixIn類はマルチスレッド方式を実現しました。それは二つの方法しかなく、それぞれprocess_です。requestとprocess_request_thread方法多プロセスの方式はForking MixInで、しばらく省略します。
使用する場合は、マルチ引き継ぎでインターフェースを呼び出すことにより、例えば:
Pythonはネットワークプログラミングのために、より高級なパッケージを改善しました。SocketServer.pyは多くのネットワークサービスの種類を提供しています。彼らのデザインはとても優雅です。Pythonはネットワークサービスを二つの主要なクラスに抽象化しています。一つはServer類で、接続に関するネットワーク操作を処理します。もう一つはRequest Handler類で、データ関連の操作を処理します。そして、Serverを拡張するための2つのMixInクラスを提供し、マルチプロセスまたはマルチスレッドを実現します。ネットワークサービスを構築する際には、ServerとRequest Handlerは分離されておらず、Request HandlerのインスタンスオブジェクトはServer内でServer作業に協力します。
モジュールの主要いくつかのServerの関係は以下の通りです。
+------------+
| BaseServer |
+------------+
|
v
+-----------+ +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+ +------------------+
|
v
+-----------+ +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+ +--------------------+
BaseServer分析BaseServer通過init_初期化して、外部にserveを提供します。foreverとhandler_request方法
init初期化:
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
同前init_ソースは簡単です。主な役割はserverオブジェクトを作成し、serverアドレスと処理要求のクラスを初期化することです。socketのプログラミングを熟知しているので、よく知っているはずです。server_。addressはホストとポートを含むタプルです。serve_forever
serverオブジェクトを作成したら、serverオブジェクトを使って無限ループを開く必要があります。以下でserve gauを分析します。foreverのソースコードです。
def serve_forever(self, poll_interval=0.5):
self.__is_shut_down.clear()
try:
while not self.__shutdown_request:
r, w, e = _eintr_retry(select.select, [self], [], [],
poll_interval)
if self in r:
self._handle_request_noblock()
finally:
self.__shutdown_request = False
self.__is_shut_down.set()
serve_foreverはパラメータを一つ受けます。インターバルは、セレクトポーリングの時間を表します。そして無限ループに入り、select方式を呼び出してネットワークIOの傍受を行う。select関数が返ってきたら、IO接続やデータがあるという意味で_を呼び出します。ハンドルrequest_noblockメソッドです。
_handle_request_noblock
def _handle_request_noblock(self):
try:
request, client_address = self.get_request()
except socket.error:
return
if self.verify_request(request, client_address):
try:
self.process_request(request, client_address)
except:
self.handle_error(request, client_address)
self.shutdown_request(request)
_。ハンドルrequest_noblock方法は、要求の処理を開始し、非ブロッキングである。この方法はget_によりrequest法は接続を取得し,そのサブクラスで具体的に実現した。接続ができたら、verify_を呼び出します。request法検証要求。検証が通ると、すぐにプロcess_を呼び出します。request処理要求。途中でエラーが発生したら、handle_を呼び出します。error処理エラーとshutdown_requestは接続を終了します。
verify_request
def verify_request(self, request, client_address):
return True
この方法はrequestを検証し、通常は布団類を書き換えます。簡単にTrueに戻ればいいです。そしてprocess_に入ります。request方法は要求を処理します。
process_request
def process_request(self, request, client_address):
self.finish_request(request, client_address)
self.shutdown_request(request)
process.request法はmixinの入口であり、MixInサブクラスはこの方法を書き換えることによってマルチスレッドまたはマルチプロセスの構成を行う。finish_を呼び出しますrequest完了要求の処理は、shutdown_を呼び出します。request終了要求。
finish_request
def finish_request(self, request, client_address):
self.RequestHandlerClass(request, client_address, self)
finishrequest方法は要求を処理します。request Handlerオブジェクトを作成し、request Handlerを通じて具体的な処理を行います。BaseRequest Handler分析
すべてのrequest HandlerはBaserequest Handlerベースを継承します。
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()
このクラスは各要求を処理します。対象を初期化する場合は、requestオブジェクトを要求します。そしてsetupメソッドを呼び出して、サブクラスはこの方法を書き換えて、socket接続を処理するために使用します。次はhandlerとfinishの方法です。要求に対するすべての処理は、ハンドル方法を書き換えることができます。これでPython全体で提供されたServer方式を紹介しました。まとめて、ネットワークサービスを構築するには、ネットワークIOを処理するためにBaseServerが必要であり、同時に内部でrequest Handlerオブジェクトを作成し、すべての具体的な要求に対して処理を行う。
BaseServer-BaseRequest Handler
__init__(server_address, RequestHandlerClass):
BaseServer.server_address
BaseServer.RequestHandlerClass
serve_forever():
select()
BaseServer._handle_request_noblock()
BaseServer.get_request() -> request, client_addres
BaseServer.verify_request()
BaseServer.process_request()
BaseServer.process_request()
BaseServer.finish_request()
BaseServer.RequestHandlerClass()
BaseRequestHandler.__init__(request)
BaseRequestHandler.request
BaseRequestHandler.client_address = client_address
BaseRequestHandler.setup()
BaseRequestHandler.handle()
BaseServer.shutdown_request()
BaseServer.close_request()
BaseServer.shutdown_request()
BaseServer.close_request()
BaseServerとBaseRequest Handlerはネットワーク処理の2つの基質である。実際のアプリケーションでは、ネットワーク動作は、TCPまたはHTTPプロトコルを使用することが多い。SocketServer.pyもより高度なTCP、UDPパッケージを提供しています。次に、TCPに関するネットワークモジュール(UDPとTCPのコード組織の違いは特に大きくないので、しばらく無視してください。)を見てみます。TCPServer
TCPServerはBaseServerを引き継ぎ、初期化時に、socketソケットの作成を行いました。
def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
BaseServer.__init__(self, server_address, RequestHandlerClass)
self.socket = socket.socket(self.address_family,
self.socket_type)
if bind_and_activate:
self.server_bind()
self.server_activate()
同前init_方法はsocketモジュールでsocketオブジェクトを作成し、server_を呼び出します。bindとserver_activate
server_bind
def server_bind(self):
if self.allow_reuse_address:
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind(self.server_address)
self.server_address = self.socket.getsockname()
server_bind方法は、socketオブジェクトのbind動作を行い、ネットワークアドレスの多重化などのsocket関連属性を設定する。
server_activate
def server_activate(self):
self.socket.listen(self.request_queue_size)
server_activate方法も比較的簡単で、socketオブジェクトのlistenを追加します。ゲットするrequest
このような一番重要な方法はget_です。requestこの方法は、socketオブジェクトに戻る要求接続を行う。
def get_request(self):
"""Get the request and client address from the socket.
"""
return self.socket.accept()
ゲットするrequest方法はBaserver基類の中の_です。ハンドルrequest_noblockで呼び出し、そこからソケットオブジェクトが取得した接続情報が入ってきます。UDPServerなら、ここで取得したのはUDP接続です。また、TCPServerは、ファイル記述子を返すためにベースクラスのselect呼び出しに対してfileno方法を提供する。
Stream Request Handler
TCPServerはtcpソケットを使用したネットワークサービスを実現し、Handlerは対応するStreamRequest Handlerである。これはBaseRequest Handlerを継承します。ベースクラスのsetup方法とfinish方法は、接続によってキャッシュファイルの読み書き操作を実現するために書き換えられます。
セットアップ方法:
def setup(self):
self.connection = self.request
if self.timeout is not None:
self.connection.settimeout(self.timeout)
if self.disable_nagle_algorithm:
self.connection.setsockopt(socket.IPPROTO_TCP,
socket.TCP_NODELAY, True)
self.rfile = self.connection.makefile('rb', self.rbufsize)
self.wfile = self.connection.makefile('wb', self.wbufsize)
setupはnagleアルゴリズムを使うかどうかを判断しました。その後、対応する接続属性を設定します。最も重要なのは、読み取り可能なファイルと書き込み可能なファイルを作成しました。彼らは実際にファイルを作成したのではなく、データを読み込み、データを送信する操作をカプセル化しています。抽象的にファイルに対する操作になります。self.rfileはクライアントデータを読み出す対象であると理解でき、データを読み取る方法がある。self.wfileは、クライアントにデータを送信するためのオブジェクトです。後の操作では、クライアントデータが到来するとバッファ領域に書き込まれて読み取り可能で、クライアントにデータを送信する必要がある場合は、書き込み可能なファイルの中にwriteデータだけが必要です。TCPサービスを実現するにはTCPServerとStreamRequest Handlerを用いて共同で連携する必要がある。大まかな関数呼び出しの流れは以下の通りです。関数呼び出しは括弧で表し、割り当ては括弧ではなく、クラスプレフィックスの表示はシステム呼び出しではありません。
TCPServer-Stream Request Handler
__init__(server_address, RequestHandlerClass):
BaseServer.server_address
BaseServer.RequestHandlerClass
TCPServer.socket = socket.socket(self.address_family, self.socket_type)
TCPServer.server_bind()
TCPServer.server_activate()
serve_forever():
select()
BaseServer._handle_request_noblock()
TCPServer.get_request() -> request, client_addres
socket.accept()
BaseServer.verify_request()
BaseServer.process_request()
BaseServer.process_request()
BaseServer.finish_request(request, client_address)
BaseServer.RequestHandlerClass()
BaseRequestHandler.__init__(request)
BaseRequestHandler.request
BaseRequestHandler.client_address = client_address
StreamRequestHandler.setup()
StreamRequestHandler.connection = StreamRequestHandler.request
StreamRequestHandler.rfile
StreamRequestHandler.wfile
BaseRequestHandler.handle()
StreamRequestHandler.finsih()
StreamRequestHandler.wfile.close()
StreamRequestHandler.rfile.close()
BaseServer.shutdown_request(request)
TCPServer.shutdown()
request.shutdown()
TCPServer.close_request(request)
request.close()
TCPServer.shutdown_request(request)
TCPServer.shutdown(request)
request.shutdown()
TCPServer.close_request(request)
request.close()
最初にBaseServerについて紹介したとき、pythonがBaseServerに対して設計していることを知っていたとき、Mixinにマルチスレッドまたはマルチプロセスを拡張できるインターフェースを残しました。mixinは父親タイプのパーシャを複写します。request方法が実現されます。ThreadingMixIn
ThreadingMixIn類はマルチスレッド方式を実現しました。それは二つの方法しかなく、それぞれprocess_です。requestとprocess_request_thread方法多プロセスの方式はForking MixInで、しばらく省略します。
process_request
def process_request(self, request, client_address):
t = threading.Thread(target = self.process_request_thread,
args = (request, client_address))
t.daemon = self.daemon_threads
t.start()
process.request法は父親のこの方法を複写した。これをインターフェースの入口として、各要求に対して、Threadを呼び出して新しいスレッドを開く。スレッドごとにprocessをバインドします。request_thread方法
process_request_thread
def process_request_thread(self, request, client_address):
try:
self.finish_request(request, client_address)
self.shutdown_request(request)
except:
self.handle_error(request, client_address)
self.shutdown_request(request)
process.request_thread方法とBaseServerの中のparse_requestはほとんど同じです。マルチスレッドの方式でのみ呼び出します。使用する場合は、マルチ引き継ぎでインターフェースを呼び出すことにより、例えば:
class ThreadingTCPServer(ThreadingMixIn, TCPServer):
pass
具体的なコールプロセスは以下の通りです。
ThreadingMixIn -- TCPServer - StreamRequestHandler
__init__(server_address, RequestHandlerClass):
BaseServer.server_address
BaseServer.RequestHandlerClass
TCPServer.socket = socket.socket(self.address_family, self.socket_type)
TCPServer.server_bind()
TCPServer.server_activate()
serve_forever():
select()
BaseServer._handle_request_noblock()
TCPServer.get_request() -> request, client_addres
socket.accept()
BaseServer.verify_request()
BaseServer.process_request()
ThreadingMixIn.process_request()
t = threading.Thread(target = ThreadingMixIn.process_request_thread)
ThreadingMixIn.process_request_thread
BaseServer.finish_request(request, client_address)
BaseServer.RequestHandlerClass()
BaseRequestHandler.__init__(request)
BaseRequestHandler.request
BaseRequestHandler.client_address = client_address
StreamRequestHandler.setup()
StreamRequestHandler.connection = StreamRequestHandler.request
StreamRequestHandler.rfile
StreamRequestHandler.wfile
BaseRequestHandler.handle()
StreamRequestHandler.finsih()
StreamRequestHandler.wfile.close()
StreamRequestHandler.rfile.close()
BaseServer.shutdown_request(request)
TCPServer.shutdown()
request.shutdown()
TCPServer.close_request(request)
request.close()
TCPServer.shutdown_request(request)
TCPServer.shutdown(request)
request.shutdown()
TCPServer.close_request(request)
request.close()