59497


ソケット通信を停止させる時にエラーが出ます。  
エラーを再現するために色々試して見た結果、以下のようにsleepを挟んでデータを送っていると、受信側でソケット通信を停止させる時にエラーになるようです。

**testSend.py**

[code]

    import time
    from socket import socket, AF_INET, SOCK_DGRAM
    
    HOST = ''
    PORT = 5000
    ADDRESS = "127.0.0.1"
    
    s = socket(AF_INET, SOCK_DGRAM)
    
    while True:
        s.sendto(str(time.time()).encode(), (ADDRESS, PORT))
        time.sleep(0.1)
    
    s.close()
    
[/code]

受信側のコードは、以下のようになっています。  
GUIを立ち上げて[start]ボタンを押すとデータを受信して、200秒に1度ずつ時間をprintし  
[stop]ボタンを押すと受信とprintを停止するプログラムになっています。

**testRecieve.py**

[code]

    import tkinter as tk
    import threading
    import time
    from socket import socket, AF_INET, SOCK_DGRAM
    
    class threadingGUI():
        def __init__(self):
            self.stop_flag = True
            self.s = None
            self.thread1 = None
            self.thread2 = None
    
        def worker(self):
            #文字列を受け取る
            messages, address = self.s.recvfrom(8192)
            print(messages)
    
        def schedule(self,interval, f, wait=True):
            base_time = time.time()
            next_time = 0
            while not self.stop_flag:#接続を切るボタンが押されるまでループ
                self.thread2 = threading.Thread(target=f)
                self.thread2.start()
                if wait:
                    self.thread2.join()
                next_time = ((base_time - time.time()) % interval) or interval
                time.sleep(next_time)
    
        def start(self):
            if not self.thread1:
                HOST = ''
                PORT = 5000
    
                #受信
                ADDRESS = "127.0.0.1"
                self.s = socket(AF_INET, SOCK_DGRAM)
                self.s.bind((HOST, PORT))
    
                self.thread1 = threading.Thread(target=self.schedule,args=(1.00/200.00, self.worker, False))
                self.stop_flag=False
                self.thread1.start()
    
    
        def stop(self):
            if self.thread1:
                self.stop_flag=True
                self.thread1.join()
                self.s.close()
                self.thread1=None
    
        def GUI_start(self):
            root=tk.Tk()
            Button001=tk.Button(root,text="Start",command=self.start)
            Button001.pack()
            Button002=tk.Button(root,text="Stop",command=self.stop)
            Button002.pack()
            root.mainloop()
    
    t = threadingGUI()
    t.GUI_start()
    
[/code]

[stop]ボタンを押すと

[code]

    line 15, in worker
        messages, address = self.s.recvfrom(8192)
    OSError: [WinError 10038] ソケット以外のものに対して操作を実行しようとしました。
    
[/code]

というエラーメッセージが繰り返し表示されます。  
testSend.pyからtime.sleep(0.1)を消すとこのエラーはなくなるのですが。  
このsleepを残したままエラーを出さない方法はないでしょうか？


CC BY-SA 4.0
2019-10-05T13:00:52.697
59485
2019-10-06T00:27:34.503
2019-10-05T15:16:54.643
34471
34471
question
python network socket
socket通信を停止させる時にエラー：OSError: [WinError 10038] ソケット以外のものに対して操作を実行しようとしました。


一般的にソケットは同期IOです。つまり

[code]

        def worker(self):
            #文字列を受け取る
            messages, address = self.s.recvfrom(8192)
            print(messages)
    
[/code]

は受信できるまで待ち続けます。そしてこれを起動する

[code]

        def schedule(self,interval, f, wait=True):
            base_time = time.time()
            next_time = 0
            while not self.stop_flag:#接続を切るボタンが押されるまでループ
                self.thread2 = threading.Thread(target=f)
                self.thread2.start()
                if wait:
                    self.thread2.join()
                next_time = ((base_time - time.time()) % interval) or interval
                time.sleep(next_time)
    
[/code]

は受信待ちスレッドを作成し続けます。仮にループが200周すれば、受信待ちスレッドが200個作成されます。これらは送信側から200回受信しなければ完了しません。

[stop]ボタンを押すと、ソケットがクローズされるため、大量に作成された受信待ちスレッドがエラーを発生させます。

* * *

と状況は説明できますが、どのような処理にしたいのかがわからないので、修正案は示すことができません。


CC BY-SA 4.0
2019-10-05T23:43:50.060
59496
2019-10-05T23:43:50.060
4236
59485
answer


@sayuriさん回答とほとんど被りますが、  
動作内容・原因とエラーの状況は以下になるでしょう。

  * 作成したsocketが無限待ちのブロッキング型である
  * scheduleスレッドの中で5ms毎にworkerスレッドを作成し、かつ終了を待っていない
  * testSend.pyスクリプトは100ms毎に1回送信
  * 上記により、startボタンクリック後は100ms毎に19個の受信待ちworkerスレッドが積み上がり、時間が経つごとに増えていく
  * stopボタンクリックしてソケットclose&None設定後に、積み上がった受信待ちworkerスレッドが全て例外で終了する

気付かれていないもう一つの異常として、testSend.pyスクリプトを動作させずにtestRecieve.pyスクリプトだけを起動・startボタンクリック・stopボタンクリックすると、無応答状態になります。

対処としては以下の2つを両方組み込むことになるでしょう。

  * socketにtimeout値(0.01～0.1秒程度?)を設定する  
あるいはノンブロッキング型に設定する(以下の例はtimeout設定)

* * *
[code]

    #受信
    ADDRESS = "127.0.0.1"
    self.s = socket(AF_INET, SOCK_DGRAM)
    self.s.settimeout(0.1)    # タイムアウト値設定を追加する
    self.s.bind((HOST, PORT))
    
[/code]

* * *

  * workerスレッドの受信&print処理を例外処理で囲む  
(正常動作中でも常にtimeout等の例外が発生するので)

* * *
[code]

    def worker(self):
        try:    # 例外処理を組み込む
            #文字列を受け取る
            messages, address = self.s.recvfrom(8192)
            print(messages)
        # 必要であれば以下に発生した例外種類に応じた対処を組み込む
        except:
            pass    # 例としては全てにおいて特に何もしない
    
[/code]


CC BY-SA 4.0
2019-10-06T00:17:52.750
59497
2019-10-06T00:27:34.503
2019-10-06T00:27:34.503
26370
26370
59485
answer