🔚

【Python】Socket ServerをCtrl+Cで停止できない問題

2024/06/08に公開

Server側でCtrl+Cが効かなくなる

Socket Serverを稼働して接続待機している間、Ctrl+Cを入力しても停止できなくなるという不具合に遭遇しました。しかしその後接続されると、途端に停止するようです。なんと致命的なことでしょう。

有名な不具合

調べてみれば、先人も同じ不具合に対峙していた記録がみられました。どうやら、既に知れた不具合だったようですね。

https://stackoverflow.com/questions/74730025/how-to-stop-server-socket-using-ctrlc-in-python

上の内容に倣いsignalによって停める方法を試してみるも、停めることはできず。按ずるに、Windowsでは無効のようです。そこで次の記事を読んでみたものの、何をされているのか理解が及びませんでした。

https://eel3.hatenablog.com/entry/2022/07/18/221219

本題

唯に動いているものを停めるのみだと言うのに、徒に複雑な仕組みを設けては、作業量が割に合いません。希わくは、単純明快な仕組みで停めたいものです。
そこでThreadを使ってみたところ、意図した通り停めることができました。

server.py
from socket import socket
from threading import Thread
import time

# 無限ループ
def loop():
    # 例外が起こるまで何もせず待機
    try:
        while True:
            pass
    # 例外が起こると終了する
    except:
        exit(0)

# Socket Server
def main():
    ADDRESS = ("127.0.0.1", 12345)
    BUFFER_SIZE = 1024

    with socket() as server:
        server.bind(ADDRESS)
        server.listen()
        print(f"Server: {ADDRESS[0]}:{ADDRESS[1]}\n")

        while True:
            clientSocket, clientAddress = server.accept()
            print(f"{clientAddress} connected")
            with clientSocket:
                # 受信するだけで返信はしていない
                clientData = clientSocket.recv(BUFFER_SIZE).decode()
                print(clientData)
            time.sleep(1)

# main()を並行処理として分離する
serverThread = Thread(
    target = main,
    daemon = True
)
serverThread.start()

# loop()を主たる処理として実行する
loop()

これで、Ctrl+Cの入力をloop()が感知してくれるのでしょう。
なお、Client側は特に細工を要しませんので、本記事では扱いません。

psで確認

本当に停止しているのか、実は秘密裏に動いているのではないかと気になったので、psで確認してみることにしました。
先ずはPythonの停止していることを確かめます。(何故かVSCodeを閉じないといけませんでした)

特に何も動かしていない時
PS C:\> ps

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
# 中略
     87    45.46     121.44       1.52    4060   2 pwsh
     79    33.24     101.05       1.19   16052   2 pwsh
     15    10.07       8.53       0.00    7836   0 RAPS
# 以下略

ここで、PowerShellps用とpython用に二つ開いています。次に、先のプログラムを実行します。

server.pyが動いている時
PS C:\> ps

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
# 中略
     88    46.77     122.59       1.41    4060   2 pwsh
     80    35.03     101.20       1.09   16052   2 pwsh
     13     8.53      14.61      38.14    4152   2 python
     15    10.07       8.53       0.00    7836   0 RAPS
# 以下略

確かにpythonとあります。最後に、プログラムを停止します。

停止後
PS C:\> ps

 NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
 ------    -----      -----     ------      --  -- -----------
# 中略
     87    44.28     120.52       1.61    4060   2 pwsh
     79    33.80     101.31       1.27   16052   2 pwsh
     15    10.07       8.52       0.00    7836   0 RAPS
# 以下略

pythonが消えたことを確かめました。

手段としては比較的素直ですから、理解も実装も容易ではないでしょうか。しかしsignalの方が実装は簡潔ですから、動作するのであればそちらに及くはなしと信じます。

Discussion