21.21. 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()and- server_activate(). The other parameters are passed to the- BaseServerbase 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) を使えば、非同期的な動作をサポートできます。
Creating a server requires several steps.  First, you must create a request
handler class by subclassing the BaseRequestHandler class and
overriding its handle() method;
this method will process incoming
requests.  Second, you must instantiate one of the server classes, passing it
the server's address and the request handler class.  Then call the
handle_request() or
serve_forever() method of the server object to
process one or many requests.  Finally, call server_close()
to close the socket.
ThreadingMixIn から継承してスレッドを利用した接続を行う場合、突発的な通信切断時の処理を明示的に指定する必要があります。 ThreadingMixIn クラスには daemon_threads 属性があり、サーバがスレッドの終了を待ち合わせるかどうかを指定する事ができます。スレッドが独自の処理を行う場合は、このフラグを明示的に指定します。デフォルトは False で、Pythonは ThreadingMixIn クラスが起動した全てのスレッドが終了するまで実行し続けます。
サーバクラス群は使用するネットワークプロトコルに関わらず、同じ外部メソッドおよび属性を持ちます。
21.21.1. サーバ生成に関するノート¶
継承図にある五つのクラスのうち四つは四種類の同期サーバを表わしています:
+------------+
| BaseServer |
+------------+
      |
      v
+-----------+        +------------------+
| TCPServer |------->| UnixStreamServer |
+-----------+        +------------------+
      |
      v
+-----------+        +--------------------+
| UDPServer |------->| UnixDatagramServer |
+-----------+        +--------------------+
UnixDatagramServer は UDPServer から派生していて、 UnixStreamServer からではないことに注意してください --- IP と Unix ストリームサーバの唯一の違いはアドレスファミリーでそれは両方の Unix サーバクラスで単純に繰り返されています。
- 
class socketserver.ForkingMixIn¶
- 
class socketserver.ThreadingMixIn¶
- Forking and threading versions of each type of server can be created using these mix-in classes. For instance, - ThreadingUDPServeris created as follows:- class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass - The mix-in class comes first, since it overrides a method defined in - UDPServer. Setting the various attributes also changes the behavior of the underlying server mechanism.
- 
class socketserver.ForkingTCPServer¶
- 
class socketserver.ForkingUDPServer¶
- 
class socketserver.ThreadingTCPServer¶
- 
class socketserver.ThreadingUDPServer¶
- These classes are pre-defined using the mix-in classes. 
To implement a service, you must derive a class from BaseRequestHandler
and redefine its handle() method.
You can then run various versions of
the service by combining one of the server classes with your request handler
class.  The request handler class must be different for datagram or stream
services.  This can be hidden by using the handler subclasses
StreamRequestHandler or DatagramRequestHandler.
もちろん、まだ頭を使わなければなりません! たとえば、サービスがリクエストによっては書き換えられるようなメモリ上の状態を使うならば、フォークするサーバを使うのは馬鹿げています。というのも子プロセスでの書き換えは親プロセスで保存されている初期状態にも親プロセスから分配される各子プロセスの状態にも届かないからです。この場合、スレッド実行するサーバを使うことはできますが、共有データの一貫性を保つためにロックを使わなければならなくなるでしょう。
一方、全てのデータが外部に(たとえばファイルシステムに)保存される HTTP サーバを作っているのだとすると、同期クラスではどうしても一つの要求が処理されている間サービスが「耳の聞こえない」状態を呈することになります --- この状態はもしクライアントが要求した全てのデータをゆっくり受け取るととても長い時間続きかねません。こういう場合にはサーバをスレッド実行したりフォークすることが適切です。
In some cases, it may be appropriate to process part of a request synchronously,
but to finish processing in a forked child depending on the request data.  This
can be implemented by using a synchronous server and doing an explicit fork in
the request handler class handle() method.
Another approach to handling multiple simultaneous requests in an environment
that supports neither threads nor fork() (or where these are too
expensive or inappropriate for the service) is to maintain an explicit table of
partially finished requests and to use selectors to decide which
request to work on next (or whether to handle a new incoming request).  This is
particularly important for stream services where each client can potentially be
connected for a long time (if threads or subprocesses cannot be used).  See
asyncore for another way to manage this.
21.21.2. 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_addressand- RequestHandlerClassattributes.- 
fileno()¶
- Return an integer file descriptor for the socket on which the server is listening. This function is most commonly passed to - selectors, to allow monitoring multiple servers in the same process.
 - 
handle_request()¶
- Process a single request. This function calls the following methods in order: - get_request(),- verify_request(), and- process_request(). If the user-provided- handle()method of the handler class raises an exception, the server's- handle_error()method will be called. If no request is received within- timeoutseconds,- handle_timeout()will be called and- handle_request()will return.
 - 
serve_forever(poll_interval=0.5)¶
- Handle requests until an explicit - shutdown()request. Poll for shutdown every poll_interval seconds. Ignores the- timeoutattribute. It also calls- service_actions(), which may be used by a subclass or mixin to provide actions specific to a given service. For example, the- ForkingMixInclass uses- service_actions()to clean up zombie child processes.- バージョン 3.3 で変更: Added - service_actionscall to the- serve_forevermethod.
 - 
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()¶
- serve_forever()ループに停止するように指示し、停止されるまで待ちます。
 - 
server_close()¶
- サーバをクリーンアップします。上書き出来ます。 
 - 
address_family¶
- サーバのソケットが属しているプロトコルファミリです。一般的な値は - socket.AF_INETおよび- socket.AF_UNIXです。
 - 
RequestHandlerClass¶
- ユーザが提供する要求処理クラスです; 要求ごとにこのクラスのインスタンスが生成されます。 
 - 
server_address¶
- The address on which the server is listening. The format of addresses varies depending on the protocol family; see the documentation for the - socketmodule for details. For Internet protocols, this is a tuple containing a string giving the address, and an integer port number:- ('127.0.0.1', 80), for example.
 - 
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()¶
- Actually processes the request by instantiating - RequestHandlerClassand calling its- handle()method.
 - 
get_request()¶
- ソケットから要求を受理して、クライアントとの通信に使われる 新しい ソケットオブジェクト、およびクライアントのアドレスからなる、2 要素のタプルを返します。 
 - 
handle_error(request, client_address)¶
- This function is called if the - handle()method of a- RequestHandlerClassinstance raises an exception. The default action is to print the traceback to standard output and continue handling further requests.
 - 
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()¶
- サーバのコンストラクタによって呼び出され、適切なアドレスにソケットをバインドします。このメソッドは上書きできます。 
 
- 
21.21.3. 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.requestis different for datagram or stream services. For stream services,- self.requestis a socket object; for datagram services,- self.requestis a pair of string and socket.
 
- 
- 
class socketserver.StreamRequestHandler¶
- 
class socketserver.DatagramRequestHandler¶
- These - BaseRequestHandlersubclasses override the- setup()and- finish()methods, and provide- self.rfileand- self.wfileattributes. The- self.rfileand- self.wfileattributes can be read or written, respectively, to get the request data or return data to the client.
21.21.4. 使用例¶
21.21.4.1. 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
    server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
    # 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))
この例の出力は次のようになります:
Server:
$ python TCPServer.py
127.0.0.1 wrote:
b'hello world with TCP'
127.0.0.1 wrote:
b'python is nice'
Client:
$ 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
21.21.4.2. 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
    server = socketserver.UDPServer((HOST, PORT), MyUDPHandler)
    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サーバーの例と全く同じようになります。
21.21.4.3. 非同期処理の 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)
    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()
    server.server_close()
この例の出力は次のようになります:
$ 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
The ForkingMixIn class is used in the same way, except that the server
will spawn a new process for each request.
