0

Python を使用してネットワーク プログラミングを開始し、基本的なピア ツー ピア チャット クライアント サーバー アプリケーションに取り組んでいます。コンソールで動作するようになりましたが、GUI の開発中に問題に直面しています。

これは、クライアント スクリプトのコードです。サーバーにデータを送信していますが、サーバーから送信されたデータを受信/表示できず、途方に暮れています。私のコードと解決策のエラーを表示してください。

    from socket import *
    from tkinter import *
    host="127.0.0.1"
    port=1420
    buffer=1024
    server=(host,port)
    clientsock=socket(AF_INET,SOCK_STREAM)
    clientsock.connect(server)
    class ipbcc(Frame):
        def __init__(self,master):
            Frame.__init__(self,master)
            self.grid()
            self.create()
            self.connect()
        def write(self,event):
            msg=self.e.get()
            clientsock.send(msg.encode())
        def create(self):

            self.pic=PhotoImage(file="logo.gif")
            self.label=Label(self,image=self.pic)
            self.label.grid(column=0)
            self.wall=Text(self,width=70,height=20,wrap=WORD)
            self.wall.grid(row = 0, column = 1, columnspan = 2, sticky = W)
            self.e=Entry(self,width=50)
            self.e.grid(row = 1, column = 1, sticky = W)
            self.e.bind('<Return>',self.write)
        def add(self,data):
            self.wall.insert(END,data)
        def connect(self):
            def xloop():
                while 1:
                    data=clientsock.recv(buffer).decode()
                    print(data)
                    self.add(data)

    root=Tk()
    root.title("IPBCC v0.1")
    app=ipbcc(root)
    root.mainloop()

PS: Python バージョン 3.3 で、サーバー スクリプトに問題はありません。

4

3 に答える 3

1

あなたのconnect関数は という関数を定義してxloopいますが、その関数を呼び出したり、返したり、他の誰かが呼び出せるように保存したりしません。何かを行うには、その関数を呼び出す必要があります。

もちろん、インラインで直接呼び出すだけでは、永久に実行されます。つまり、イベント ループに戻ることはなく、UI がフリーズしてユーザーへの応答が停止します。

これには、スレッド化またはポーリングの 2 つのオプションがあります。


これを行う明白な方法は、バックグラウンド スレッドを使用することです。基本的な考え方は非常に単純です。

def connect(self):
    def xloop():
        while 1:
            data=clientsock.recv(buffer).decode()
            print(data)
            self.add(data)
    self.t = threading.Thread(target=xloop)
    self.t.start()

ただし、これには 2 つの問題があります。


まず、バックグラウンド スレッドを停止する方法がありません。プログラムを終了しようとすると、バックグラウンド スレッドが停止するまで待機します。つまり、プログラムは永久に待機します。

これには簡単な解決策があります。それを「デーモン スレッド」にすると、メイン プログラムが終了するとすぐに強制終了されます。これは明らかに、途中で中断された場合に破損する可能性のある作業を行っているスレッドには適していませんが、あなたの場合は問題ではないようです。したがって、次の 1 行を変更するだけです。

self.t = threading.Thread(target=xloop, daemon=True)

次に、そのself.addメソッドは Tkinter ウィジェットを変更する必要があります。バックグラウンド スレッドからは実行できません。プラットフォームによっては、黙って失敗したり、例外を発生させたり、クラッシュすることさえあります。さらに悪いことに、99% の確率で動作し、1% の確率で失敗することもあります。

そのため、メイン スレッドにメッセージを送信して、ウィジェットの変更を依頼する何らかの方法が必要ですこれは少し複雑ですが、Tkinter and Threadsでその方法を説明しています。

または、バックグラウンド スレッドで Tkinter 呼び出しをインターセプトし、メイン スレッドに自動的に渡す を使用することもできるためmtTkinter、心配する必要はありません。


もう 1 つのオプションは、ブロッキングxloop関数を、データをポーリングする非ブロッキング関数に変更することです。問題は、Tkinter GUI イベントを待ちたいが、ソケットも待ちたいということです。

ソケットをメイン イベント ループに統合できれば、それは簡単です。着信する新しいメッセージは、他のイベントと同じように処理されます。Qt などのより強力な GUI フレームワークには、これを行う方法がありますが、Tkinter にはありません。Twistedのようなリアクター フレームワークは、それ自体を Tkinter に結び付けて追加することができます (または、少なくともうまく偽装できます)。ただし、基本的なデザインにこだわりたい場合は、自分で行う必要があります。

そのため、次の 2 つのオプションがあります。

  • Tkinter に完全な制御を与えます。たとえば、1/20 秒ごとに関数を呼び出すように依頼し、関数内でノンブロッキング チェックを実行します。または、読み取るものがなくなるまで、ノンブロッキング チェックをループすることもできます。
  • ソケット制御を与えます。Tkinter に機会があればいつでも関数を呼び出すように依頼し、Tkinter に戻る前に 1/20 秒間ブロックしてデータをチェックします。

もちろん、1/20 秒は適切な長さではない可能性があります。多くのアプリケーションでは、正解はありません。とにかく、ここに簡単な例があります:

def poll_socket(self):
    r, w, x = select.select([clientsock], [], [], 0)
    if r:
        data=clientsock.recv(buffer).decode()
        print(data)
        self.add(data)
    self.after(50, self.poll_socket)

def connect(self):
    self.after(50, self.poll_socket)
于 2013-09-09T19:01:56.827 に答える