socketserver
--- ネットワークサーバのフレームワーク¶
ソースコード: Lib/socketserver.py
socketserver
モジュールはネットワークサーバを実装するタスクを単純化します。
基本的な具象サーバクラスが4つあります:
-
class
socketserver.
TCPServer
(server_address, RequestHandlerClass, bind_and_activate=True)¶ This uses the internet TCP protocol, which provides for continuous streams of data between the client and server. If bind_and_activate is true, the constructor automatically attempts to invoke
server_bind()
andserver_activate()
. The other parameters are passed to theBaseServer
base class.
-
class
socketserver.
UDPServer
(server_address, RequestHandlerClass, bind_and_activate=True)¶ This uses datagrams, which are discrete packets of information that may arrive out of order or be lost while in transit. The parameters are the same as for
TCPServer
.
-
class
socketserver.
UnixStreamServer
(server_address, RequestHandlerClass, bind_and_activate=True)¶ -
class
socketserver.
UnixDatagramServer
(server_address, RequestHandlerClass, bind_and_activate=True)¶ These more infrequently used classes are similar to the TCP and UDP classes, but use Unix domain sockets; they're not available on non-Unix platforms. The parameters are the same as for
TCPServer
.
これらの 4 つのクラスは要求を 同期的に (synchronously) 処理します; 各要求は次の要求を開始する前に完結していなければなりません。同期的な処理は、サーバで大量の計算を必要とする、あるいはクライアントが処理するには時間がかかりすぎるような大量のデータを返す、といった理由によってリクエストに長い時間がかかる状況には向いていません。こうした状況の解決方法は別のプロセスを生成するか、個々の要求を扱うスレッドを生成することです; ForkingMixIn
および ThreadingMixIn
配合クラス (mix-in classes) を使えば、非同期的な動作をサポートできます。
サーバの作成にはいくつかのステップが必要です。最初に、 BaseRequestHandler
クラスをサブクラス化して要求処理クラス (request hander class) を生成し、その handle()
メソッドをオーバーライドしなければなりません; このメソッドは入力される要求を処理します。次に、サーバクラスのうち一つを、サーバのアドレスと要求処理クラスを渡してインスタンス化しなければなりません。サーバを with
文中で使うことが推奨されます。そして、handle_request()
または serve_forever()
メソッドを呼び出して、一つのまたは多数の要求を処理します。最後に、(with
文を使っていなかったなら) server_close()
を呼び出してソケットを閉じます。
ThreadingMixIn
から継承してスレッドを利用した接続を行う場合、突発的な通信切断時の処理を明示的に指定する必要があります。 ThreadingMixIn
クラスには daemon_threads 属性があり、サーバがスレッドの終了を待ち合わせるかどうかを指定する事ができます。スレッドが独自の処理を行う場合は、このフラグを明示的に指定します。デフォルトは False
で、Pythonは ThreadingMixIn
クラスが起動した全てのスレッドが終了するまで実行し続けます。
サーバクラス群は使用するネットワークプロトコルに関わらず、同じ外部メソッドおよび属性を持ちます。
サーバ生成に関するノート¶
継承図にある五つのクラスのうち四つは四種類の同期サーバを表わしています:
+------------+
| BaseServer |
+------------+
|
v
+-----------+ +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+ +------------------+
|
v
+-----------+ +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+ +--------------------+
UnixDatagramServer
は UDPServer
から派生していて、 UnixStreamServer
からではないことに注意してください --- IP と Unix サーバの唯一の違いはアドレスファミリーです。
-
class
socketserver.
ForkingMixIn
¶ -
class
socketserver.
ThreadingMixIn
¶ それぞれのタイプのサーバのフォークしたりスレッド実行したりするバージョンはそれらのミクシン(mix-in)クラスを使って作ることができます。たとえば、
ThreadingUDPServer
は以下のようにして作られます:class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
ミクシンクラスは
UDPServer
で定義されるメソッドをオーバライドするために、最初に来ます。様々な属性を設定することで元になるサーバ機構の振る舞いを変えられます。ForkingMixIn
and the Forking classes mentioned below are only available on POSIX platforms that supportfork()
.socketserver.ForkingMixIn.server_close()
waits until all child processes complete, except ifsocketserver.ForkingMixIn.block_on_close
attribute is false.socketserver.ThreadingMixIn.server_close()
waits until all non-daemon threads complete, except ifsocketserver.ThreadingMixIn.block_on_close
attribute is false. Use daemonic threads by settingThreadingMixIn.daemon_threads
toTrue
to not wait until threads complete.バージョン 3.7 で変更:
socketserver.ForkingMixIn.server_close()
andsocketserver.ThreadingMixIn.server_close()
now waits until all child processes and non-daemonic threads complete. Add a newsocketserver.ForkingMixIn.block_on_close
class attribute to opt-in for the pre-3.7 behaviour.
-
class
socketserver.
ForkingTCPServer
¶ -
class
socketserver.
ForkingUDPServer
¶ -
class
socketserver.
ThreadingTCPServer
¶ -
class
socketserver.
ThreadingUDPServer
¶ These classes are pre-defined using the mix-in classes.
サービスの実装には、 BaseRequestHandler
からクラスを派生させてその handle()
メソッドを再定義しなければなりません。このようにすれば、サーバクラスと要求処理クラスを結合して様々なバージョンのサービスを実行することができます。要求処理クラスはデータグラムサービスかストリームサービスかで異なることでしょう。この違いは処理サブクラス StreamRequestHandler
または DatagramRequestHandler
を使うという形で隠蔽できます。
もちろん、まだ頭を使わなければなりません! たとえば、サービスがリクエストによっては書き換えられるようなメモリ上の状態を使うならば、フォークするサーバを使うのは馬鹿げています。というのも子プロセスでの書き換えは親プロセスで保存されている初期状態にも親プロセスから分配される各子プロセスの状態にも届かないからです。この場合、スレッド実行するサーバを使うことはできますが、共有データの一貫性を保つためにロックを使わなければならなくなるでしょう。
一方、全てのデータが外部に(たとえばファイルシステムに)保存される HTTP サーバを作っているのだとすると、同期クラスではどうしても一つの要求が処理されている間サービスが「耳の聞こえない」状態を呈することになります --- この状態はもしクライアントが要求した全てのデータをゆっくり受け取るととても長い時間続きかねません。こういう場合にはサーバをスレッド実行したりフォークすることが適切です。
ある場合には、要求の一部を同期的に処理する一方で、要求データに依って子プロセスをフォークして処理を終了させる、といった方法も適当かもしれません。こうした処理方法は同期サーバを使って要求処理クラスの handle()
メソッドの中で自分でフォークするようにして実装することができます。
スレッドも fork()
もサポートされない環境で (もしくはサービスにとってそれらがあまりに高価についたり不適切な場合に) 多数の同時要求を捌くもう一つのアプローチは、部分的に処理し終えた要求のテーブルを自分で管理し、次にどの要求に対処するか (または新しく入ってきた要求を扱うかどうか) を決めるのに selectors
を使う方法です。これは (もしスレッドやサブプロセスが使えなければ) 特にストリームサービスに対して重要で、そのようなサービスでは各クライアントが潜在的に長く接続し続けます。この問題を管理する別の方法について、 asyncore
モジュールを参照してください。
Serverオブジェクト¶
-
class
socketserver.
BaseServer
(server_address, RequestHandlerClass)¶ This is the superclass of all Server objects in the module. It defines the interface, given below, but does not implement most of the methods, which is done in subclasses. The two parameters are stored in the respective
server_address
andRequestHandlerClass
attributes.-
fileno
()¶ サーバが要求待ちを行っているソケットのファイル記述子を整数で返します。この関数は一般的に、同じプロセス中の複数のサーバを監視できるようにするために、
selectors
に渡されます。
-
handle_request
()¶ 単一の要求を処理します。この関数は以下のメソッド:
get_request()
、verify_request()
、およびprocess_request()
を順番に呼び出します。ハンドラ中でユーザによって提供されたhandle()
が例外を送出した場合、サーバのhandle_error()
メソッドが呼び出されます。timeout
秒以内にリクエストが来なかった場合、handle_timeout()
が呼ばれて、handle_request()
が終了します。
-
serve_forever
(poll_interval=0.5)¶ Handle requests until an explicit
shutdown()
request. Poll for shutdown every poll_interval seconds. Ignores thetimeout
attribute. It also callsservice_actions()
, which may be used by a subclass or mixin to provide actions specific to a given service. For example, theForkingMixIn
class usesservice_actions()
to clean up zombie child processes.バージョン 3.3 で変更: Added
service_actions
call to theserve_forever
method.
-
service_actions
()¶ This is called in the
serve_forever()
loop. This method can be overridden by subclasses or mixin classes to perform actions specific to a given service, such as cleanup actions.バージョン 3.3 で追加.
-
shutdown
()¶ Tell the
serve_forever()
loop to stop and wait until it does.shutdown()
must be called whileserve_forever()
is running in a different thread otherwise it will deadlock.
-
server_close
()¶ サーバをクリーンアップします。上書き出来ます。
-
address_family
¶ サーバのソケットが属しているプロトコルファミリです。一般的な値は
socket.AF_INET
およびsocket.AF_UNIX
です。
-
RequestHandlerClass
¶ ユーザが提供する要求処理クラスです; 要求ごとにこのクラスのインスタンスが生成されます。
-
server_address
¶ サーバが要求待ちを行うアドレスです。アドレスの形式はプロトコルファミリによって異なります。詳細は
socket
モジュールを参照してください。インターネットプロトコルでは、この値は例えば('127.0.0.1', 80)
のようにアドレスを与える文字列と整数のポート番号を含むタプルです。
-
socket
¶ サーバが入力の要求待ちを行うためのソケットオブジェクトです。
サーバクラスは以下のクラス変数をサポートします:
-
request_queue_size
¶ 要求待ち行列 (queue) のサイズです。単一の要求を処理するのに長時間かかる場合には、サーバが処理中に届いた要求は最大
request_queue_size
個まで待ち行列に置かれます。待ち行列が一杯になると、それ以降のクライアントからの要求は "接続拒否 (Connection denied)" エラーになります。標準の値は通常 5 ですが、この値はサブクラスで上書きすることができます。
-
socket_type
¶ サーバが使うソケットの型です; 一般的な2つの値は、
socket.SOCK_STREAM
とsocket.SOCK_DGRAM
です。
-
timeout
¶ タイムアウト時間(秒)、もしくは、タイムアウトを望まない場合に
None
。handle_request()
がこの時間内にリクエストを受信しない場合、handle_timeout()
メソッドが呼ばれます。
TCPServer
のような基底クラスのサブクラスで上書きできるサーバメソッドは多数あります; これらのメソッドはサーバオブジェクトの外部のユーザにとっては役にたたないものです。-
finish_request
(request, client_address)¶ RequestHandlerClass
をインスタンス化し、handle()
メソッドを呼び出して、実際に要求を処理します。
-
get_request
()¶ ソケットから要求を受理して、クライアントとの通信に使われる 新しい ソケットオブジェクト、およびクライアントのアドレスからなる、2 要素のタプルを返します。
-
handle_error
(request, client_address)¶ この関数は
RequestHandlerClass
インスタンスのhandle()
メソッドが例外を送出した際に呼び出されます。標準の動作では標準エラー出力へトレースバックを出力し、後続する要求を継続して処理します。バージョン 3.6 で変更: Now only called for exceptions derived from the
Exception
class.
-
handle_timeout
()¶ この関数は
timeout
属性がNone
以外に設定されて、リクエストがないままタイムアウト秒数が過ぎたときに呼ばれます。 fork型サーバーでのデフォルトの動作は、終了した子プロセスの情報を集めるようになっています。スレッド型サーバーではこのメソッドは何もしません。
-
process_request
(request, client_address)¶ finish_request()
を呼び出して、RequestHandlerClass
のインスタンスを生成します。必要なら、この関数から新たなプロセスかスレッドを生成して要求を処理することができます; その処理はForkingMixIn
またはThreadingMixIn
クラスが行います。
-
server_activate
()¶ Called by the server's constructor to activate the server. The default behavior for a TCP server just invokes
listen()
on the server's socket. May be overridden.
-
server_bind
()¶ サーバのコンストラクタによって呼び出され、適切なアドレスにソケットをバインドします。このメソッドは上書きできます。
-
verify_request
(request, client_address)¶ ブール値を返さなければなりません; 値が
True
の場合には要求が処理され、False
の場合には要求は拒否されます。サーバへのアクセス制御を実装するためにこの関数を上書きすることができます。標準の実装では常にTrue
を返します。
バージョン 3.6 で変更: context manager プロトコルのサポートが追加されました。コンテキストマネージャを終了することは、
server_close()
を呼ぶことと同一です。-
Request Handler Objects¶
-
class
socketserver.
BaseRequestHandler
¶ This is the superclass of all request handler objects. It defines the interface, given below. A concrete request handler subclass must define a new
handle()
method, and can override any of the other methods. A new instance of the subclass is created for each request.-
handle
()¶ この関数では、クライアントからの要求を実現するために必要な全ての作業を行わなければなりません。デフォルト実装では何もしません。この作業の上で、いくつかのインスタンス属性を利用することができます; クライアントからの要求は
self.request
です; クライアントのアドレスはself.client_address
です; そしてサーバごとの情報にアクセスする場合には、サーバインスタンスをself.server
で取得できます。The type of
self.request
is different for datagram or stream services. For stream services,self.request
is a socket object; for datagram services,self.request
is a pair of string and socket.
-
-
class
socketserver.
StreamRequestHandler
¶ -
class
socketserver.
DatagramRequestHandler
¶ These
BaseRequestHandler
subclasses override thesetup()
andfinish()
methods, and provideself.rfile
andself.wfile
attributes. Theself.rfile
andself.wfile
attributes can be read or written, respectively, to get the request data or return data to the client. Therfile
attributes support theio.BufferedIOBase
readable interface, andwfile
attributes support theio.BufferedIOBase
writable interface.バージョン 3.6 で変更:
StreamRequestHandler.wfile
also supports theio.BufferedIOBase
writable interface.
使用例¶
socketserver.TCPServer
の例¶
サーバサイドの例です:
import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
"""
The request handler class for our server.
It is instantiated once per connection to the server, and must
override the handle() method to implement communication to the
client.
"""
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
# Create the server, binding to localhost on port 9999
with socketserver.TCPServer((HOST, PORT), MyTCPHandler) as server:
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
server.serve_forever()
別の、ストリーム(標準のファイル型のインターフェースを利用して通信をシンプルにしたファイルライクオブジェクト)を使うリクエストハンドラクラスの例です:
class MyTCPHandler(socketserver.StreamRequestHandler):
def handle(self):
# self.rfile is a file-like object created by the handler;
# we can now use e.g. readline() instead of raw recv() calls
self.data = self.rfile.readline().strip()
print("{} wrote:".format(self.client_address[0]))
print(self.data)
# Likewise, self.wfile is a file-like object used to write back
# to the client
self.wfile.write(self.data.upper())
先ほどとの違いは、readline()
の呼び出しが、改行を受け取るまで recv()
を複数回呼び出すことです。1回の recv()
の呼び出しは、クライアントから1回の sendall()
呼び出しで送信された分しか受け取りません。
クライアントサイドの例:
import socket
import sys
HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])
# Create a socket (SOCK_STREAM means a TCP socket)
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
# Connect to server and send data
sock.connect((HOST, PORT))
sock.sendall(bytes(data + "\n", "utf-8"))
# Receive data from the server and shut down
received = str(sock.recv(1024), "utf-8")
print("Sent: {}".format(data))
print("Received: {}".format(received))
この例の出力は次のようになります:
サーバー:
$ python TCPServer.py
127.0.0.1 wrote:
b'hello world with TCP'
127.0.0.1 wrote:
b'python is nice'
クライアント:
$ python TCPClient.py hello world with TCP
Sent: hello world with TCP
Received: HELLO WORLD WITH TCP
$ python TCPClient.py python is nice
Sent: python is nice
Received: PYTHON IS NICE
socketserver.UDPServer
の例¶
サーバサイドの例です:
import socketserver
class MyUDPHandler(socketserver.BaseRequestHandler):
"""
This class works similar to the TCP handler class, except that
self.request consists of a pair of data and client socket, and since
there is no connection the client address must be given explicitly
when sending data back via sendto().
"""
def handle(self):
data = self.request[0].strip()
socket = self.request[1]
print("{} wrote:".format(self.client_address[0]))
print(data)
socket.sendto(data.upper(), self.client_address)
if __name__ == "__main__":
HOST, PORT = "localhost", 9999
with socketserver.UDPServer((HOST, PORT), MyUDPHandler) as server:
server.serve_forever()
クライアントサイドの例:
import socket
import sys
HOST, PORT = "localhost", 9999
data = " ".join(sys.argv[1:])
# SOCK_DGRAM is the socket type to use for UDP sockets
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# As you can see, there is no connect() call; UDP has no connections.
# Instead, data is directly sent to the recipient via sendto().
sock.sendto(bytes(data + "\n", "utf-8"), (HOST, PORT))
received = str(sock.recv(1024), "utf-8")
print("Sent: {}".format(data))
print("Received: {}".format(received))
この例の出力は、TCPサーバーの例と全く同じようになります。
非同期処理の Mix-in¶
複数の接続を非同期に処理するハンドラを作るには、 ThreadingMixIn
か ForkingMixIn
クラスを利用します。
ThreadingMixIn
クラスの利用例:
import socket
import threading
import socketserver
class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
def handle(self):
data = str(self.request.recv(1024), 'ascii')
cur_thread = threading.current_thread()
response = bytes("{}: {}".format(cur_thread.name, data), 'ascii')
self.request.sendall(response)
class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
pass
def client(ip, port, message):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
sock.connect((ip, port))
sock.sendall(bytes(message, 'ascii'))
response = str(sock.recv(1024), 'ascii')
print("Received: {}".format(response))
if __name__ == "__main__":
# Port 0 means to select an arbitrary unused port
HOST, PORT = "localhost", 0
server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
with server:
ip, port = server.server_address
# Start a thread with the server -- that thread will then start one
# more thread for each request
server_thread = threading.Thread(target=server.serve_forever)
# Exit the server thread when the main thread terminates
server_thread.daemon = True
server_thread.start()
print("Server loop running in thread:", server_thread.name)
client(ip, port, "Hello World 1")
client(ip, port, "Hello World 2")
client(ip, port, "Hello World 3")
server.shutdown()
この例の出力は次のようになります:
$ python ThreadedTCPServer.py
Server loop running in thread: Thread-1
Received: Thread-2: Hello World 1
Received: Thread-3: Hello World 2
Received: Thread-4: Hello World 3
ForkingMixIn
クラスは同じように利用することができます。この場合、サーバーはリクエスト毎に新しいプロセスを作成します。fork()
をサポートする POSIX プラットフォームでのみ利用可能です。