開発を行った動機
AI で Web 上に検索結果を表示するシステム、index.html から client.php に検索要求を送信し、client.php が TCP/IP 通信で python のサーバー server.py に検索要求を伝えるというシステムを作っていました。検索サーバーは、検索の元ネタファイルを読み込んで、検索要求を待っています。元ネタファイルを読み込むのに5分以上かかるので、この index.html, client.php, server.py という配置にしました。どういうことかというと、server.py は一度元ネタファイルを5分かけて読み込んで、あとは通信で検索要求を待っていれば、タイムラグなく検索要求に答えられるからです。検索の元ネタのファイルは、別のプログラムで毎晩24時に更新されます。すると、通常は、検索要求を待っている server.py は、24時になったことを検出できなければなりません。通常の TCP/IP のネットワークサーバーでは、
socket→bind→listen →
while True → accept→ recv → send → close →
↑ ↓
←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←←
のようなループの accept で通信待ちでブロックされ、ほかのタスクを実行することはありません。これは困りました。毎晩24時になったことを検出し、検索元ネタファイルを読み込みなおさなければなりません。上のダイヤグラムだと、読み込みなおしの入る余地がありません。
解決策
ちゃんと、解決策が用意されていました。それは、accept について、timeout を設定して一定時間で例外を発生させ、例外が発生したら他のタスクを実行する。というものです。
サンプルプログラム
import socket
import datetime
def main():
# ss = SemanticSearch() 検索用クラスのインスタンスの初期化
# ss.load_corpus( filename ) 検索元ネタファイルを読み込む
# ソケットのパラメーター定義
server_ip = "127.0.0.1"
server_port = 10000
listen_num = 10
buffer_size = 8192
# 1.ソケットオブジェクトの作成
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.作成したソケットオブジェクトにIPアドレスとポートを紐づける
tcp_server.bind((server_ip, server_port))
tcp_server.settimeout( 1.0 )
# 3.作成したオブジェクトを接続可能状態にする
tcp_server.listen(listen_num)
# 4.ループして接続を待ち続ける
while True:
try:
# 5.クライアントと接続する
client,address = tcp_server.accept()
# 6.データを受信する
data = client.recv(buffer_size)
data = data.decode('UTF-8')
print("[*] Received Data : {}\n".format(data)) # data は検索文。
#検索実行
#send_result = ss.search( data )
# 7. 返事を返す。
client.send( send_result.encode( "UTF-8" ) )
# 8.接続を終了させる
client.close()
except socket.timeout as e:
t_now = datetime.datetime.now().time()
if '00:00' == str( t_now )[:5]:
tcp_server.close()
#ここで検索の元ネタファイルを読み直す。10分とかかかる。その間、通信はお断り。
# ss = SemanticSearch() 検索用クラスのインスタンスの初期化
# ss.load_corpus( filename ) 検索元ネタファイルを読み込む
# 1.ソケットオブジェクトの作成
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.作成したソケットオブジェクトにIPアドレスとポートを紐づける
tcp_server.bind((server_ip, server_port))
tcp_server.settimeout( 1.0 )
# 3.作成したオブジェクトを接続可能状態にする
tcp_server.listen(listen_num)
目標達成
目標は検索元ネタファイルの更新です。
#ここで検索の元ネタファイルを読み直す。10分とかかかる。その間、通信はお断り。
# ss = SemanticSearch() 検索用クラスのインスタンスの初期化
# ss.load_corpus( filename ) 検索元ネタファイルを読み込む
ここで更新しています。