ここではsocketserverを使ってシンプルなエコーサーバー通信のプログラムを作ってみます。
クライアント側TCP/IPで接続して、そのクライアントから送ったものを送り元に戻すというだけのエコーサーバーです。どうさの確認方法としてはtelnetで接続して、telnetからのキー入力をエコーバックします。
#
# File: echoserv.py
# オリジナルは
# https://docs.python.org/ja/3/library/socketserver.html
#
import socketserver
import sys
import os
import signal
##
## 実装されるハンドラ
##
class LocalTCPHandler(socketserver.BaseRequestHandler):
def handle(self):
# 接続されるハンドラ
print(self.client_address[0]) # 接続元IPアドレス
print(os.getpid()) # 自分のPID
while True:
try:
self.data = self.request.recv(1024)
print(self.data.decode('UTF-8'),end='')
self.request.sendall(self.data)
except:
return
def finish(self):
print("**Finish**")
print(os.getpid())
return socketserver.BaseRequestHandler.finish(self)
def finish_all(signal,frame): # プログラムを終了
sys.exit(0)
# Control-C シグナルを拾った時の処理
signal.signal(signal.SIGINT, finish_all)
if __name__ == "__main__":
HOST, PORT = "0.0.0.0" , 54321
#
# TCPサーバーを作る / フォークする
#
with socketserver.ForkingTCPServer((HOST, PORT), LocalTCPHandler) as server:
server.serve_forever()LocalTCPHandlerクラスを実装することで機能を実現するかたちになります。メソッドhandleが接続したときの動作です。finishは通信が終了した際の後処理になります。
handleのなかでは単純に入力をうけつけ、それをprintし、そして送り元にそのままお繰り返しています。try: except: のエラー処理をつけ通信がエラーになった時はexceptでhandleを抜けるようにしています。exceptになる場合ですが、大半の場合はクライアント側からの接続断です。いずれにしてもここでは、TCPでの接続が維持できない/接続が切れるという場合は、そのまま抜けるということをしています。
finishは終了であることをprintとし、さらに下位のハンドラを呼び出して後処理をさせて終了させるかたちになっています。
関数finish_allはプログラムがcontrol-Cなどで割り込みで強制的に終了した際に、後処理を行うためのものです。関連する後処理はシステムのexitを呼び出して、そちらにまかせています。もし、この処理を行っていない場合、このプログラムが終了しても通信に利用しているポートが開きっぱなしになったままになります。
関数signal.signal()は、SIGINT (割り込みシグナル)をキャッチした時、finish_allが実行されます。この場合は、プログラム全体が正常に後処理を行ったうえで終了します。
名前の通りにTCPのサーバーを立ち上げ、接続したらフォークして受信・送信の処理をします。インスタンスは変数serverに格納されます。あとはserver.serve_forever()でループで処理されることになります。接続したら子プロセスをフォークして処理し、接続が終了すると子プロセスが終了します。接続毎に新しい子プロセスで処理されるので、非同期に行われることになります。