PythonでTCPの双方向通信をしてみた

表題の通り。Pythonでソケット通信をする場合はsocket.socketを使用する。TCP通信を行う場合は、一方をLISTENにして、もう一方からconnectで接続してから通信を行う。(UDPの場合は接続なしでデータを送受信すればいい。)作ったクラスは以下の通り。

from socket import *
import threading


class TCPboth():
    def __init__(self, port, ip="127.0.0.1", timeout=3):
        SrcIP = ip                             # 受信元IP
        SrcPort = port                                 # 受信元ポート番号
        self.SrcAddr = (SrcIP, SrcPort)                 # アドレスをtupleに格納
        self.BUFSIZE = 4096                             # バッファサイズ指定
        self.conn = None
        self.flg = True
        self.timeout = timeout
    
    def start_server(self):
        # サーバを起動する
        self.th = threading.Thread(target=self._server_th)
        self.th.start()
        
    def _server_th(self):
        if self.conn is None:
            self.server = create_server(self.SrcAddr)
            self.server.listen()
            # 接続を待機
            self.conn, self.addr = self.server.accept()
            # タイムアウトを設定
        self.conn.settimeout(self.timeout)
        while self.flg:
            try:
                s = self.conn.recv(self.BUFSIZE)
                self.OnRecv(s)
            except timeout:
                pass
            except:
                # タイムアウト以外の例外なら接続を閉じる
                self.flg = False
                self.conn.close()
                self.conn = None
                print("server closed")
        
    def create_connection(self, port, ip="127.0.0.1"):
        # 接続する
        if self.conn is not None:
            self.conn.close()
            self.conn = None
        self.conn = socket(AF_INET, SOCK_STREAM)
        self.conn.bind(self.SrcAddr)
        self.conn.connect((ip, port))
        
    def OnRecv(self, s):
        # データ受信時の処理
        print(s)
        
    def send(self, s):
        # データの送信
        if self.conn is not None:
            self.conn.send(s.encode("UTF-8"))
        else:
            print("No connection!!")
            
    def close(self):
        self.flg = False
        if self.conn is not None:
            self.conn.close()
        self.th.join() 

使用方法は以下のような感じ。まずコンソールを開き、接続を待機するサーバーを以下のように立てる。">>"のプロンプトが表示されるが、まだ接続していないのでデータは送信できない。

print("start")
s1 = TCPboth(9000)
s1.start_server()
st = ""
while st != "end":
    st = input(">>")
    s1.send(st)
    
s1.close()
print("OK")
del s1 

次に別のコンソールを起動し、以下のように別のサーバーを立てる。先ほどと異なる点としては、create_connectionで先ほど立てた9000番のサーバーに接続している。start_serverではすでに接続完了しているので、データの受信待ちに移行する。

print("start")
s2 = TCPboth(9001)
s2.create_connection(9000)
s2.start_server()
st = ""
while st != "end":
    st = input(">>")
    s2.send(st)
    
s2.close()
print("OK")
del s2 

どちらのプロンプトも開けたら、好きなほうに文字を入力してエンターを押すと、もう一方に文字が出力される。データ受信時に出力だけでなくもっといろんな処理をしたい場合は、OnRecvに独自の処理を実装すればOK。