SocketServerモジュールを使ったサーバプログラムの実装

Pythonでのネットワークプログラミングの方法をいろいろと検討している。今回は、「SocketServer」と「asyncore」というキーワードに関して調べてみた。

  • 参考サイト:

Python2.6 doc (SocketServer)
Python2.6 doc (asyncore)
SocketServer - ネットワークサービスを作成する
asyncore - 非同期I/Oハンドラ
一通りドキュメントを読んでみたが、すぐに全部を理解できるほど勉強が進んでいない。実用的なネットワークサービスプログラムを作成するとき、有用なモジュールであることは理解できた。今後も読み返して考える。

今回の検討項目

SocketServerモジュールで、早速、サーバプログラムを実装してみる。このモジュールを使えば、接続が複数ある場合にも処理が簡単になる(らしい)。その場合、ThreadingMixInクラスかForkingMixInクラスを利用する(らしい)。そこまで高度なことは今はやらずに、簡単なプログラムで試しにSocketServerを使ってみる。

コマンドpsを実行するプログラム

クライアント側からコマンドを送って、サーバ側でそのコマンドを実行し、結果をクライアントへ送信するプログラムを作ってみる。今の場合、サーバで実行させるコマンドはpsとする。SocketServerモジュールを使わない場合のサーバプログラムは以下の通り。bindとか、acceptとか、whileループを使っている。

# sv01.py
import socket, sys, os

def main():
    port = 9999
    ls = socket.socket(socket.AF_INET, socket.SOCK_STREAM);
    ls.bind(('', port))

    while 1:
        ls.listen(1)
        (conn, addr) = ls.accept()
        print 'client is at', addr  #クライアント側ホスト名

        rc = conn.recv(2)     #コマンドpsを受信
        ppn = os.popen(rc)     #コマンドの実行
        rl = ppn.read()      #コマンドの実行結果を読み込み
        conn.send(rl)
        conn.close()        #コマンドの実行結果を送信

if __name__ == '__main__':
    main()

一方で、クライアントプログラムは以下の通り。

# cl01.py
import socket, sys

def main():
    host = 'localhost'      #接続先ホスト名
    port = 9999          #ポート番号
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))

    cmd = 'ps'
    s.send(cmd)          #コマンドpsを送信
    data = s.recv(1024)      #1024バイト受信
    print data          #受信データを表示

if __name__ == '__main__':
    main()

プログラムの実行方法は次のようにする。

  • ターミナル画面を2つ開く。
  • 一方の画面(サーバ側画面)で、「python sv01.py」を実行する。この時点では何も起こらない。
  • もう一方の画面(クライアント側画面)で、「python cl01.py」を実行する。すると、サーバへ送られたpsコマンドがサーバ側で実行され実行結果がクライアント側画面に表示される。
  • サーバ側のプログラムは実行したままの状態で、クライアント側では

繰り返し「python cl01.py」を実行できる。下図に実行例を示す。

サーバプログラムでSocketServerモジュールを使う

上記のサーバプログラムsv01.pyにおいて、SocketServerモジュールを使うように修正する。修正後のサーバプログラムは以下の通り。

# sv02.py
import SocketServer
import os

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        print ">connect from:", self.client_address
        cmd = self.request.recv(2)
        rl = os.popen(cmd)
        data = rl.read()
        self.request.send(data)

if __name__ == '__main__':
    host, port = '', 9999
    server = SocketServer.TCPServer((host, port), MyTCPHandler)
#    server = SocketServer.ThreadingTCPServer((host, port), MyTCPHandler)
    server.serve_forever()

プログラムの実行方法や動作は、修正前と変わらない。
プログラムが単純すぎて、SocketServerモジュールの有難味がわからない...。