TCPServerの仕組み
具体的にサーバを動作させる場合は、BaseServerを継承したサーバを用います。ここではTCPServerを使ってみます。
TCPServerの中身
ここを参照すると、以下の例のようにBaseServerでインタフェースのみであった部分を継承して、tcp用のソケットプログラミングの実装がなされています。
def server_activate(self): """Called by constructor to activate the server. May be overridden. """ self.socket.listen(self.request_queue_size)
具体的にどのメソッドが何をやっているかは以下の通りです。なおソケットの対してのread/writeに関する操作は、ユーザ定義のハンドラーで実装します。
- bind -> server_bind
- listen -> server_activate
- accept ->get_request
- close -> shutdown_request
TCPServerの使い方
以下がサンプルコードです。クライアントが入力したデータをそのままサーバ上に表示するだけです。
import SocketServer class MyTCPHandler(SocketServer.BaseRequestHandler): def handle(self): self.data = self.request.recv(1024).strip() print "%s wrote:" % self.client_address[0] print self.data if __name__ == "__main__": HOST, PORT = "localhost", 9999 server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler) server.serve_forever()
クライアント側はtelnetでサーバに接続します。以前はクライアント側のコードも書いていましたが、実はtelnetでも通信できます(当然か)。
# telnet localhost 9999 Trying ::1... telnet: connect to address ::1: Connection refused Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. test TESTConnection closed by foreign host.
サーバ側で確かに動作しているのを確認できました。なぜかリモートホストからだと失敗します(笑)。
# python tcpserver.py 127.0.0.1 wrote: test
StreamRequestHandlerの中身
今回は用いませんでしたが、ハンドラーのクラスとして便利なStreamRequestHandlerがあります。StreamRequestHandlerは、以下のようにBaseRequestHandlerにrwileおよびwfileを実装したクラスです(コメントは削除して実装のみ掲載してます)。
class StreamRequestHandler(BaseRequestHandler): rbufsize = -1 wbufsize = 0 timeout = None disable_nagle_algorithm = False 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) def finish(self): if not self.wfile.closed: try: self.wfile.flush() except socket.error: # An final socket error may have occurred here, such as # the local error ECONNABORTED. pass self.wfile.close() self.rfile.close()
実際に読み書きするパケットのデータ部分は、ソケットオブジェクトのmakefileメソッドを呼び出してファイルオブジェクトを用いて処理しているところに注目です。ファイルオブジェクトとしてプロトコルを扱える場合はとても便利です。クライアントにレスポンスを返すのはfinish関数中のflushメソッドを利用しています。