如何使用 Python Socket Server 打开和关闭线程?

问题描述 投票:0回答:1

我有一个简单的Python套接字服务器,我希望我的客户端能够在多个连接期间打开/关闭线程进程(即我不想在请求之间保持客户端-服务器连接打开)。服务器的简化版本如下所示:

_bottle_thread = None # I have tried this and a class variable

class CalibrationServer(socketserver.BaseRequestHandler):

    allow_reuse_address = True
    request_params = None
    response = None

    bottle_thread = None

    def handle(self):
        self.data = self.request.recv(1024).strip()
        self.request_params = str(self.data.decode('utf-8')).split(" ")
        method = self.request_params[0]

        if method == "start": self.handle_start()
        elif method == "stop": self.handle_stop()
        else: self.response = "ERROR: Unknown request"

        self.request.sendall(self.response.encode('utf-8'))


    def handle_start(self):
        try:
            bottle = self.request_params[1]
            _bottle_thread = threading.Thread(target=start, args=(bottle,))
            _bottle_thread.start()
            self.response = "Ran successfully"
            print(_bottle_thread.is_alive(), _bottle_thread)
        except Exception as e:
            self.response = f"ERROR: Failed to unwrap: {e}"

    def handle_stop(self):
        print(_bottle_thread)
        if _bottle_thread and _bottle_thread.is_alive():
            _bottle_thread.join()  # Wait for the thread to finish
            self.response = "Thread stopped successfully"
        else:
            self.response = "No active thread to stop"



if __name__ == "__main__":
    HOST, PORT = LOCAL_IP, CAL_PORT

    with socketserver.TCPServer((HOST, PORT), CalibrationServer) as server:
        server.serve_forever()

该过程开始正常,但是当我与服务器的连接关闭时,我无法再访问

_bottle_thread
。它只是重新初始化为
None
。我只有一台主计算机与服务器通信,并且只需要运行一个
start
实例。我尝试过使用类变量来保存它并使用全局变量。我还尝试过使用 Threading 和 Forking TCP 服务器。我如何访问该线程以将其关闭?我需要更改它以使连接始终打开吗?还有其他方法可以解决这个问题吗?我想使用服务器,这样我就可以更轻松地控制这个过程,因为它将同时在 8 台不同的计算机上运行,但我完全接受其他想法(我尝试过 Ansible,但它对我不起作用)。谢谢!

编辑:

这是我的客户代码:

HOST, PORT = LOCAL_IP, CAL_PORT
data = " ".join(sys.argv[1:])

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    sock.connect((HOST, PORT))
    sock.sendall(bytes(data + "\n", "utf-8"))
    received = str(sock.recv(1024), "utf-8")

print("Sent:     {}".format(data))
print("Received: {}".format(received))

它通过一些简单的参数打开、发送和关闭与服务器的连接。

这是我的客户端控制台输出:

(venv) path$ python cal_client.py start argument
Sent:     start argument
Received: Ran successfully
(venv) path$ python cal_client.py stop
Sent:     stop
Received: No active thread to stop

和我的服务器:

Received from 127.0.0.1:
['start', 'argument']
Initializing
True <Thread(Thread-1, started 8236592650235098)>
Received from 127.0.0.1:
['stop']
None # the thread is showing None
python multithreading sockets tcp socketserver
1个回答
0
投票

啊。我一开始就错过了发生了什么......

问题是,当您分配给

_bottle_thread
时,您并没有分配给它的全局版本(甚至分配给它的类版本)。

运行这个简短的示例将会很有启发:

_bottle_thread = "Global"

class C:
    _bottle_thread = "Class"

    def meth(self):
        _bottle_thread = "Method"
        print(f"Method {_bottle_thread}")    

c = C()
c.meth()

print(f"Global: {_bottle_thread}")
print(f"Class: {c._bottle_thread}")

输出为:

Method Method
Global: Global
Class: Class

发生的事情是,由于 python 作用域规则,您最终会得到

_bottle_thread
的三个不同版本:通过在所有其他作用域之外分配给它而获得的全局版本,通过在类中分配给它而获得的类版本定义,以及通过在方法内分配给它而获得的本地定义。 (请注意,不可能从方法外部打印在
_bottle_thread
内部分配的本地
meth
,也就是说,因为一旦方法结束,它的引用计数就会变为 0,然后它将被销毁。)

您需要在您希望全局可见的每个范围内使用

global
关键字:

def meth(self):
    global _bottle_thread
    _bottle_thread = "Method"

更好的解决方案是将

_bottle_thread
变量保留为实例变量:

class CalibrationServer(socketserver.BaseRequestHandler):
    ...
    def __init__(self):
        # (Not strictly necessary but considered best practice to 
        # initialize instance variables in the constructor)
        self._bottle_thread = None 
        

    def handle_start(self):
        ...
        self._bottle_thread = threading.Thread(target=start, args=(bottle,))
        ...
    def handle_stop(self):
        if self._bottle_thread and self._bottle_thread.is_alive():
             ...
© www.soinside.com 2019 - 2024. All rights reserved.