Python TkinterでUDPメッセージを受信するGUIアプリを作成する際に、recvfrom関数がブロックする問題について

このQ&Aのポイント
  • Python Tkinterを使用して、UDPメッセージを受信するGUIアプリを作成しています。
  • 受信開始ボタンを押すと、特定のポート番号からUDPでメッセージを受信し、テキストエリアに表示します。
  • recvfrom関数はメッセージが届くまでブロッキングするため、受信中に他のボタンが反応しない問題があります。
回答を見る
  • ベストアンサー

GUIアプリでブロックする関数使う(スレッド化?)

Python, Tkinterで以下のようなGUIアプリを作ろうとしています。 ・見た目はWindowsのメモ帳のような感じ ・メニューバーにある「受信開始」ボタンを押すと、特定のポート番号から  UDPで受信したメッセージを取得し、テキストエリアに延々と表示する  (テキストエリアとは、メモ帳で言えば、我々が書きたいテキストを書く部分です) ・普通のGUIアプリ通り、受信・表示中でも、×ボタンを押せば終了するし、  「受信停止」ボタンを押せば受信を停止するようにしたい。 ここで、UDPで受信する際の関数としてrecvfrom関数を使っているのですが、 この関数はメッセージがなにか届くまでブロッキングします。 この間、他の部分、つまり×ボタンだとか、他のボタンは押しても一切無反応になってしまいます。 具体的には、「受信開始」ボタンが押された際に呼ぶ関数として自作のrecvFromPort()という関数を読んでおり、それは以下のようにwhileで延々回すものです。 menubar.add_command(label = "受信開始", under = 0, command = lambda: recvFromPort(port.get())) def recvFromPort(myport): [tab] sock = socket(AF_INET, SOCK_DGRAM) [tab] sock.bind(("", myport)) [tab] while True: [tab][tab] data, addr = sock.recvfrom(BUFSIZE) [tab][tab] textarea.insert("end", data) これを、受信中でも他のボタンなどが聞くようにするには、 受信部分は別スレッドにすべきなのでしょうか? いろいろと調べては見たのですが、初歩的すぎるのか、ヒントがえられませんでした。 アドバイス頂きたく、よろしくお願い申し上げます。

  • R-gray
  • お礼率41% (1005/2413)

質問者が選んだベストアンサー

  • ベストアンサー
  • heburusu
  • ベストアンサー率85% (140/164)
回答No.1

GUIの入出力(表示)処理もrecvFromPortでブロックされてしまうので、 受信処理を別スレッドで動かす必要があるかと思います。 参考URLにTkinter+スレッドの参考になりそうなプログラムが載っております。

参考URL:
http://bit.ly/1PPfXmL
R-gray
質問者

お礼

ありがとうございます、ご紹介いただいたサイト参考になりました。 おかげさまで、無事完成させ、実働させることが出来ました。 遅くなって恐縮ですが、ありがとうございました。

関連するQ&A

  • パケット受信 recvfrom( )について

    お世話になります。 WinSockを用いてUDPパケット受信プログラムを作成しています。 環境はVC++です。ダイアログベースで、 STARTボタンを押すと受信開始します。 送信側よりパケットを1000個送信した場合、受信側では、 int num; SOCKET sock; char buf[1500]; //ソケットの設定は省略 while(1){ num = recvfrom(sock, buf, sizeof(buf), 0, NULL, NULL);  printf("%d",num ); if( num == SOCKET_ERROR){ break; } } recvfrom()で受信していますが、送信側で送信が完了しても while()を抜けないのか、応答なし(フリーズ)してしまいます。 printf()で表示してみると、 ちゃんと送信パケット個数回表示されています。 送信パケット数は変動するため、受信側のプログラムには 個数がわかりませんため、while()でループしています。 while()を抜ける条件はどうしたらいいのでしょうか? ちなみにrecvfrom()の戻り値として SOCKET_ERROR,0を指定してもエラーになります。 長くなりましたが、どうぞよろしくお願い致します。

  • recvfrom関数の戻り値がおかしいんですが…

    初めて投稿させていただきます。 現在、Cにてあるクライアントプログラムを作成している者です。 構成は以下の通り。 《構成》 サーバ:PLC(シーケンサと書けば大体の方はわかりますか?) クライアント:Linux Ubuntu9.10 通信方式:TCP/IP 《開発環境》 言語:C IDE:eclipse コンパイラ:gcc 4.x 《質問》 クライアントプログラムからASCIIデータをサーバへ送信し、サーバはクライアントから受信したASCIIデータに応答し、ASCIIデータ伝文を返信してきます。 このサーバからのASCIIデータ応答伝文をrecvfrom関数で受信し、応答伝文のASCIIデータを'buf'変数に格納し、標準出力関数で表示させるといったプログラムです。 このプログラムでは、ユーザ関数内でrecvfrom関数をコールしているのですが、ユーザ関数内でrecvfrom関数をコールした場合、受信データの先頭4byteしか受信できておらず、困っています。 ちなみに、main関数内でrecvfrom関数をコールすると、応答伝文全体のデータをきちんと受信できています。 ちなみに、サーバの応答ASCIIデータ伝文は、仕様通り(期待通り)のデータが返信されています。(ネットワークモニタ:wiresharkで確認済み) recvfrom関数をコールする場所によって、変数に格納するデータ量が変わるといったことがあるのでしょうか。 ソースの一部を添付します。 参照の上、アドバイス等いただけないでしょうか。 《ソース》 ◆mainソース #include ... :<-#include定義 int main{   :<-変数定義、変数初期値代入処理等 ユーザ関数1()コール<-ユーザ関数1内でソケット通信処理を動作させている。 ユーザ関数2()コール<-同上 } ◆ユーザ関数用ソース #include...   :<-#include定義 unsigned char ユーザ関数名1;<-ユーザ関数プロトタイプ宣言 unsigned char ユーザ関数名2:<-同上 char 変数1[5]; char s_buf[4096]; char buf[4096]; /*データ受信用バッファ*/ char 変数2[256]; size_t len; long l; int sock_fd; /*ソケット用ファイル記述子*/ struct sockaddr_in cl_addr; /*CPU用ソケットアドレス*/ socklen_t cl_len = 0; /*cl_addrのサイズ格納用*/ ssize_t n = 0; unsigned char ユーザ関数1(char *引数1,…char *引数5){   :送信データ生成処理 //* ソケット作成 *// if ((sock_fd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return 1;   } //* 接続先定義*// memset(&cl_addr, 0, sizeof cl_addr); cl_addr.sin_family  = AF_INET; /*プロトコル定義*/ cl_addr.sin_addr.s_addr = inet_addr("***.***.***.***"); /*IPアドレス定義*/ cl_addr.sin_port  = htons(****); /*Port定義*/ //* 接続 *// if (connect(sock_fd, (struct sockaddr *)&cl_addr, sizeof cl_addr) < 0) { perror("connect"); return 1;   } //* データ送信 *// if(send(sock_fd, s_buf, strlen(s_buf), 0) < 0){ fprintf(stderr, "could not send message : %s\n", s_buf); exit(EXIT_FAILURE); } printf("\nSendMessage\n%s\n",s_buf); //* データ受信 *// cl_len = sizeof cl_addr; //*↓↓このrecvfrom関数でbuf内にサーバ応答伝文の先頭4byteのみが格納されている。↓↓*// if (( n = recvfrom(sock_fd, buf, sizeof buf,0, (struct sockaddr *)&cl_addr, &cl_len)) < 0) { perror("recvfrom"); return 1; } fprintf(stderr ,"TCP from addr = %s, port = %d\n", inet_ntoa(cl_addr.sin_addr), ntohs(cl_addr.sin_port) );

  • 非同期関数とノンブロッキング関数について(winsock)

    こんばんわ。 VC++.NETにてコンソール上でソケットプログラミングをしています。 非同期モードとノンブロッキング関数について知りたいのですが、私は、今までTCPやUDPでsendtoやsend関数を使用してきました。しかし、非同期やノンブロッキング関数があることを知り、詳しく知りたいと思っています。 1.非同期やノンブロッキング関数はGUI作成を行う上では重要であるが、コンソール上でプログラミングを行っている場合は利用しなくてよいのでしょうか? 2.以下のWSAAsyncSelect関数の第2引数の設定がわかりません。サンプルでは、hwndに関連付けられたウィンドウがSM_EVENTメッセージを受け取りますと記述されていますが、ウィンドウを利用していないコンソールアプリでの記述方法はありますでしょうか? HWND hwnd; WSAAsyncSelect(Sock,hwnd,SM_EVENT,FD_WRITE); と記述し実行した場合、強制終了されてしまいます。 3.たとえば、送信側から送信したパケットを受信側で受信し、再度受信側から送信側へ送信する場合を考えると、 送信側で送信と受信が必要です。この場合、マルチスレッド処理が必要だと思うのですが、非同期のFD_WRITEとFD_READを使うことで、シングルスレッドで実現可能でしょうか? よろしくお願い致します。

  • TCPでの非同期型select関数について

    マッキントッシュOS-X上で動くTCP通信プログラムを作る場合について教えてください。 ウィンドウアプリを考えています。 ウィンドウ環境なので、アプリがフリーズしてしまわないために、 以下のような非同期処理が考えられます。 この場合、データが来ていなくても、while(1)がぐるぐる回るので 無駄にプロセスを食ってしまうと思うのですが、これはしかたのないことでしょうか。 もっとよい方法がるのでしょうか。 Winsockだとイベント応答関数での受信処理処理ができてエレガントなのですが、 バークレイソケットでは、どうすべきなのか疑問に思い質問させていただきました。 よろしくお願いします。 //ソケットを非同期モードにセット val = 1; ioctl(sock, FIONBIO, &val); while (1) {   memcpy(&fds, &readfds, sizeof(fd_set));   select(2, &fds, NULL, NULL, NULL);   // sockに読み込み可能データが届いている場合は、受信   if (FD_ISSET(sock, &fds)) {     memset(buf, 0, sizeof(buf));     recv(sock, buf, sizeof(buf), 0);   }   else{     //受信データがなかったときの処理   } }

  • Winsockを利用した単純なファイル送信プログラムについて

    こんばんは。 何度もこの掲示板を利用させていただいている者です。 WinsockのUDPを用いて簡単なファイル送信プログラムを作っています。UDPを使わずに、TCPを使用したほうが良いのでは?とのご指摘をいただきましたが、まずは、UDPを利用した単純なファイル送信プログラムを作ってみたいと思っています。 しかし、送信側から受信側へファイルがうまく受信できていません。もしかしたら、送信側自体がきちんと送信できていないのかもしれません。 以下にそのプログラムの概要と内容を示します。 [概要] 送信側→受信側にUDPを用いて、送信側にあるjpegまたはmpegファイルを送信し、受信側でファイルを開く。 [プログラム概要] ・送信側 ファイルポインタを用いてファイルオープン fread関数とsendto関数を用いて1024バイトずつ送信 ・受信側 ファイルポインタを用いてファイルオープン whileの無限ループ内に、recvfrom関数とfwrite関数を用いて送信側からのデータを受信 [プログラムの内容] ・送信側 printf("読み込み用ファイルを入力して下さい:"); scanf("%s",fname); if((fp = fopen(fname,"rb")) == NULL){ printf("入力ファイルをオープンできない。\n"); exit(1); } char send_buf[1025]; int n; while(n = fread(send_buf,1,1024,fp) != -1){ sendto(theSocket,send_buf,n,0,(LPSOCKADDR)&saServer,sizeof(struct sockaddr)); } ・受信側 char Recv_buf[1025]; char size; SOCKADDR_IN saClient; while(1){ size = recvfrom(theSocket,Recv_buf,1024,0,(LPSOCKADDR)&saClient,&nLen); fwrite(Recv_buf,size,1,fp); } ご指摘またはご教授をいただけたらと思います。 よろしくお願いします。

  • 親スレッドが子スレッドを監視する方法について(マルチスレッド)

    こんにちは。 私は、A端末から送信されたパケットをB端末で受信し、B端末で受信したそのパケットを再度、A端末へ送信するというプログラムを作成しました。 Phase1.A端末(送信側)→B端末(受信側) Phase2. B端末→A端末 ということです。 上記を実現するために、送信端末において、送信スレッド(親スレッド)と受信スレッド(子スレッド)を立てマルチスレッド処理を行っています。以下にプログラムの概要を示します。 main(int argc ,char *argv[]){ UDPSend(s_port,szServer); } static int UDPSend(unsigned short s_port,char *szServer){ hTh = (HANDLE)_beginthreadex(NULL, 0, UDPReceiveData, NULL, 0, &thID); while((n = fread(send_Buf,1,SEND_DATA_SIZE,fp)) != 0) { sendto(s1, send_Buf, n, 0, (LPSOCKADDR)&addrin1, sizeof(addrin1)); } } unsigned __stdcall UDPReceiveData(void *lpx){ while (1) { size = recvfrom(s2, recv_Buf, (int)sizeof(recv_Buf) - 1, 0, (SOCKADDR *)&from, &fromlen); return 0; } } UDPSend関数にて、パケットをB端末へ送信。UDPReceiveData関数にて、B端末からのパケットを受信しています。この場合、UDPSend関数(スレッド?)がUDPReceiveData関数より先に、終わってしまう場合が生じると思っているのですが。 UDPSend関数がUDPReceiveData関数を監視する方法があるのでしょうか? よろしくお願いします。

  • プログラムのGUI部分をブラウザで代用する手段

    rubyスクリプトを使用してデータ処理を行っております。 このプログラムのGUI部分をブラウザで代用することは可能でしょうか? もし可能であればどのような手段があるのかご教示いただきますと嬉しいです。 # ブラウザを使用せずに、rubyのGUIライブラリを使用する方法はここでは除外してください。 ブラウザ上で必要なパラメータを設定し、実行ボタン押下でこれをスクリプトに渡してスクリプト実行、処理結果(テキスト)をブラウザ上に表示させたいです(GUIとスクリプト間のデータの受け渡しは標準入出力利用? またはパラメータ用のデータファイル経由?) ブラウザ上で使用したいGUI部品は、ボタン、テキストエリア(入力用、表示用)、ラジオボタン、コンボボックス、(PC内の)ファイル選択ダイアログ等 実行環境 ・Windows XP以降(XPは事情によりサポートが切れても使用予定) ・ネットワークに接続せずPC単体で使用  htmlファイルをクリックしてGUIを起動するイメージ ・ブラウザはPCに標準で入っているインターネットエクスプローラ ・そのPCでhttpサーバー等は動かせません javascript等の使用で上記内容を実現可能でしょうか? よろしくお願いします。

  • socket通信でのフィルタリング

    linux環境にてC言語でUDPソケットのプログラムを作成しています。 Aの端末からUDPで受信してパケットヘッダの表示及び、宛先アドレスや宛先ポート番号 を書き換えてBの端末にUDPで送信します。 そこで下記の関数を使用して受信しているのですが、パケットキャプチャのように なんでも受信してしまいます。 socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) これを特定のポート番号だけ受信(フィルタ)するにはどのようにすれば良いのでしょうか?

  • テキスト入力後、エンターを押すことで関数を実行するには

     formのtextエリアに値を入力し、その隣に配置したボタンでその値を利用した 関数を実行することはできます。しかし、誤ってtextエリアでエンターを押してしまうと、 入力値が消えて何かが実行されているようですが、何も起きません。  参考書などを見て、onchangeかonBlurでも使えばよいのかと思い試してみましたが、だめです。  textエリアでクリックすることで、その値を利用した関数を実行できる=隣のボタンを クリックすることと同様の動作を実現するにはどうしたらよいのでしょうか。 《サンプル》 <input type = "text" name="text"> <button onClick ="xxxx()"> 実行 </button>  ※上のtextのvalueを元に実行するのが下のボタンの"xxxx()"

  • スレッド

    VC++2008ExpressEditionを使用してプログラムを作成しています。 Windowsフォームアプリケーションを作成し、そこに、TextBoxとButtonを放り込み、ボタンを押すと以下のようなコードが実行されるようにしました。 関数testfuncはhoge::Form1::testfuncという風な場所で定義されています。 logBoxは、Form1のコンストラクタのTODOの部分で作成しています。   public: static System::Windows::Forms::TextBox^ logBox;   void testfunc(){     int i;     for(i=0;i<200;i++){       int t=clock();       while(10>clock()-t);       logBox+=L"aaraeaewa"+i+L"\r\n";       logBox->SelectionStart = logBox->Text->Length;       logBox->ScrollToCaret();     }   }   System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) {     button1->Enabled=false;     button1->Text=L"実行中";     button1->Update();     ThreadStart^ trddel=gcnew ThreadStart(this,&hoge::Form1::testfunc);     Thread ^trd=gcnew Thread(trddel);     trd->Start();     button1->Enabled=true;     button1->Text=L"実行";   } この関数をボタンを押して実行すると、testfunc関数の logBox->SelectionStart = logBox->Text->Length; の部分で、 'System.InvalidOperationException' のハンドルされていない例外が System.Windows.Forms.dll で発生しました。 追加情報: 有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール 'logBox' がアクセスされました。 という風なエラーが出ます。 読んだ感じだと、元々のスレッドで作成したコントロールを新しく作ったスレッドからコントロールすることは出来ないって感じのことが書かれているのですが、どの様にすればこれは回避できるようになるのでしょうか?