前回の質問(Python3 Tkinter チャットを作成したい)
にて、Tkinterとソケット通信でチャットのようなアプリを作る際にサーバとクライアントの両方を使うことができるような回答を頂いたので、とりあえず作ろうと思いました。
(サーバは受信専用、クライアントは送信専用な使い方をしようとしています。)
まずは、TkinterのUI表示とサーバの立ち上げをしたいのですが、早速わからないことがいっぱいです。
Tkinter使いながらソケット通信する上で下記要件を満たす必要がある(?)と思いました。
- ソケット通信の開始と終了を任意の位置でできるようにする。
(理由は、勝手に通信を開始したり終了されては困るから。)
とりあえず、開始はTkinterの画面が出る前、終了はTkinterの画面が閉じたときとします。
2. ソケット通信が切れたときの復帰、復帰もできなかった際の処理。
(Tkinter閉じてしまえばいい?)
3. サブスレッドで受け取ったデータをメインスレッドに渡す。
(TkinterのLabelなどで表示したいから。)
3については、過去にスレッドセーフなグローバル変数等で渡すやり方を教わったので、キューを使うことにしました。
2は現状できなくてもいいです。(そもそも、それ以外が完成しないと実装が難しいかもしれないので。)
1についてがわからない部分です。
ソケット通信(サーバ)を開始〜立ち上げ状態については、サブスレッドスタートさせて繰り返しループさせればいいのかなって思っています。メッセージを常に受信できるようにしたいため。
終了するときの方法がわからず、受信で繰り返しループしているので、そのループフラグを落とせばいいのかと思ってやってみたのですが、できませんでした。(閉じるボタンを押したときにエラー。単純に変数にアクセスできない?)
コードは下記に記載しますが、そもそもソケット通信(サーバ)の開始終了についてはこの方法でできますか。
もし、ソケット通信の開始終了でフラグを落とすだけではなく他に処理が必要な場合は教えて下さい。
回答よろしくおねがいします。
該当のソースコード
Python:
1from tkinter import ttk 2import queue 3import socket 4import threading 5import tkinter as tk 6 7class Controller: 8 def __init__(self): 9 self.rcv_data = queue.Queue() 10 self.main() 11 12 def main(self): 13 self.thread = threading.Thread(target=self.loop) 14 self.thread.start() 15 self.win = tk.Tk() 16 self.view = View(self.win) 17 self.win.protocol("WM_DELETE_WINDOW", self.close) 18 self.win.mainloop() 19 20 def loop(self): 21 self.svr = Server(self.rcv_data) 22 23 def close(self): 24 # サーバ切断したい 25 self.svr.loop_flag = False 26 27 self.thread.join() 28 self.win.destroy() 29 30class View: 31 def __init__(self, win): 32 self.win = win 33 self.win.geometry("400x300") 34 35# ソケット通信参考 36# https://techacademy.jp/magazine/19147 37class Server: 38 def __init__(self, rcv_data): 39 self.loop_flag = True 40 self.ip_adr = "127.0.0.1" 41 self.port = 50000 42 self.buffer_size = 1024 43 self.start(rcv_data) 44 45 def start(self, rcv_data): 46 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: 47 s.bind((self.ip_adr, self.port)) 48 s.listen(1) # <--待ち状態に入る(これも接続終了時に対応する必要あり?) 49 while True: 50 con, adr = s.accept() 51 print(con, adr) 52 while self.loop_flag: 53 data = con.recv(1024) 54 if not data: 55 break 56 print("recv") 57 rcv_data.put() 58 59 60if __name__ == "__main__": 61 ctrlr = Controller()
閉じるボタンを押したときに表示されたエラー
Exception in Tkinter callback Traceback (most recent call last): File "C:\Users\user01\AppData\Local\Programs\Python\Python38\lib\tkinter\__init__.py", line 1883, in __call__ return self.func(*args) File "c:/Users/user01/Desktop/test.py", line 32, in close self.svr.loop_flag = False AttributeError: 'Controller' object has no attribute 'svr'
追記
try-except追加、daemon化、二重ループに再変更
Python
1from tkinter import ttk 2import queue 3import socket 4import threading 5import tkinter as tk 6 7loop_flag = True 8 9 10class Controller: 11 def __init__(self): 12 self.rcv_data = queue.Queue() 13 self.main() 14 15 def main(self): 16 self.thread = threading.Thread(target=self.loop, daemon=True) 17 self.thread.start() 18 self.win = tk.Tk() 19 self.view = View(self.win) 20 self.win.protocol("WM_DELETE_WINDOW", self.close) 21 self.win.mainloop() 22 23 def loop(self): 24 self.svr = Server(self.rcv_data) 25 self.svr.start(self.rcv_data) 26 27 def close(self): 28 global loop_flag 29 # サーバ切断したい 30 loop_flag = False 31 32 self.svr.s.close() 33 #self.thread.join() 34 self.win.destroy() 35 36 37class View: 38 def __init__(self, win): 39 self.win = win 40 self.win.geometry("400x300") 41 42# ソケット通信参考 43# https://techacademy.jp/magazine/19147 44 45 46class Server: 47 def __init__(self, rcv_data): 48 self.ip_adr = "127.0.0.1" 49 self.port = 50000 50 self.buffer_size = 1024 51 52 def start(self, rcv_data): 53 global loop_flag 54 self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 55 self.s.bind((self.ip_adr, self.port)) 56 self.s.listen(1) 57 58 while loop_flag: 59 try: 60 con, adr = self.s.accept() 61 print(con, adr) 62 while loop_flag: 63 data = con.recv(1024) 64 print(data) 65 if not data: 66 break 67 print("recv") 68 rcv_data.put() 69 except Exception as e: 70 print(type(e)) 71 print(e) 72 73 74if __name__ == "__main__": 75 ctrlr = Controller() 76
回答1件
あなたの回答
tips
プレビュー