如何在指定时间后结束线程?

发布于 2025-01-12 20:15:52 字数 923 浏览 0 评论 0原文

我有这个 Python 脚本,它只是启动线程来侦听本地主机上的某些端口。

#!/usr/bin/python3

import socket
import threading
import concurrent.futures

threads = []

def listen_on_port(port):
    HOST = ''                 # Symbolic name meaning all available interfaces
    PORT = int(port)          # Arbitrary non-privileged port
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind((HOST, PORT))
        s.listen(1)
        conn, addr = s.accept()
        print(addr,conn)


def main(ports):
    with concurrent.futures.ThreadPoolExecutor() as executor:
        for port in ports:
            executor.submit(listen_on_port,port)

if __name__ == '__main__':
    main(ports=[50010,50020,50030])

当我运行此脚本时,它会按预期侦听端口。我使用此脚本来测试防火墙规则是否允许这些端口上的连接。在另一个系统上,我执行 nc -zv my-host 50010 ,并且线程正在侦听端口 50010 终止,这就是我想要的。

但是,我想做出两项改进:1)让线程在 X 秒后死亡,2)像守护进程一样在后台运行线程,以便我的脚本将退出并保持线程运行。

我怎样才能实现这个目标?

I have this Python script that just starts up threads to listen on some ports on the localhost.

#!/usr/bin/python3

import socket
import threading
import concurrent.futures

threads = []

def listen_on_port(port):
    HOST = ''                 # Symbolic name meaning all available interfaces
    PORT = int(port)          # Arbitrary non-privileged port
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind((HOST, PORT))
        s.listen(1)
        conn, addr = s.accept()
        print(addr,conn)


def main(ports):
    with concurrent.futures.ThreadPoolExecutor() as executor:
        for port in ports:
            executor.submit(listen_on_port,port)

if __name__ == '__main__':
    main(ports=[50010,50020,50030])

When I run this script it listens on the ports as expected. I use this script to test that the firewall rules are allowing connections on those ports. And on another system I execute nc -zv my-host 50010 and the thread is listening on port 50010 terminates, which is what I want.

But, I would like to make two improvements: 1) have the threads die after X seconds, 2) Run the threads in the back ground like daemons so that my script will exit and leave the threads running.

How can I achieve this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

唐婉 2025-01-19 20:15:52

但是,我想做出两项改进:1)让线程在 X 秒后终止,2)像守护进程一样在后台运行线程,以便我的脚本将退出并保持线程运行。

我建议您实际上并不想做(2)。您可以将后台事务留给 shell,而不必担心代码中的情况。这意味着您可以像这样编写代码:

#!/usr/bin/python3

import argparse
import concurrent.futures
import socket


class Listener:
    def __init__(self, port: int, timeout: float = 10):
        self.port = port
        self.timeout = timeout

    def run(self):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(self.timeout)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            s.bind(("", self.port))
            s.listen(1)
            try:
                print(f"Waiting for connection on port {self.port}")
                conn, addr = s.accept()
                print(addr, conn)
            except TimeoutError:
                print(f"Listener on {self.port} timed out.")


def main():
    p = argparse.ArgumentParser()
    p.add_argument("--timeout", "-t", type=float, default=10)
    p.add_argument("ports", nargs="+", type=int)
    args = p.parse_args()

    pool = concurrent.futures.ThreadPoolExecutor()
    tasks = []

    for port in args.ports:
        l = Listener(port, timeout=args.timeout)
        tasks.append(pool.submit(l.run))

    concurrent.futures.wait(tasks)


if __name__ == "__main__":
    main()

然后像这样运行它:

python listener.py 100 200 300 &

如果您确实想处理代码中的后台,那么最好使用简单的 os.fork 而不是 daemonize 模块,因为您的目标并不是真正编写守护进程;你想要一些你希望写入你的终端的东西。这可能看起来像这样:

#!/usr/bin/python3

import socket
import os


class Listener:
    def __init__(self, port: int, timeout: float = 10):
        self.port = port
        self.timeout = timeout

    def run(self):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(self.timeout)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            s.bind(("", self.port))
            s.listen(1)
            try:
                print(f"Waiting for connection on port {self.port}")
                conn, addr = s.accept()
                print(addr, conn)
            except TimeoutError:
                print(f"Listener on {self.port} timed out.")

def main():
    p = argparse.ArgumentParser()
    p.add_argument("--timeout", "-t", type=float, default=10)
    p.add_argument("ports", nargs="+", type=int)
    args = p.parse_args()

    for port in args.ports:
        listener = Listener(port, timeout=args.timeout)
        pid = os.fork()
        if pid == 0:
            listener.run()
            return


if __name__ == "__main__":
    main()

运行该代码(并从另一个终端连接到侦听端口)看起来像这样:

$ python listener.py 2001 2002 2003
Waiting for connection on port 2001
Waiting for connection on port 2002
Waiting for connection on port 2003
$ ('127.0.0.1', 51466) <socket.socket fd=4, family=2, type=1, proto=0, laddr=('127.0.0.1', 2001), raddr=('127.0.0.1', 51466)>
('127.0.0.1', 44602) <socket.socket fd=4, family=2, type=1, proto=0, laddr=('127.0.0.1', 2002), raddr=('127.0.0.1', 44602)>
('127.0.0.1', 38672) <socket.socket fd=4, family=2, type=1, proto=0, laddr=('127.0.0.1', 2003), raddr=('127.0.0.1', 38672)>

如果让侦听器超时,它看起来像:

$ py listener.py 2001 2002 2003
Waiting for connection on port 2001
Waiting for connection on port 2002
Waiting for connection on port 2003
$ Listener on 2003 timed out.
Listener on 2001 timed out.
Listener on 2002 timed out.

But, I would like to make two improvements: 1) have the threads die after X seconds, 2) Run the threads in the back ground like daemons so that my script will exit and leave the threads running.

I would like to suggest that you don't really want to do (2). You can leave backgrounding things to your shell, and not worry about that in your code. That means you can write your code like this:

#!/usr/bin/python3

import argparse
import concurrent.futures
import socket


class Listener:
    def __init__(self, port: int, timeout: float = 10):
        self.port = port
        self.timeout = timeout

    def run(self):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(self.timeout)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            s.bind(("", self.port))
            s.listen(1)
            try:
                print(f"Waiting for connection on port {self.port}")
                conn, addr = s.accept()
                print(addr, conn)
            except TimeoutError:
                print(f"Listener on {self.port} timed out.")


def main():
    p = argparse.ArgumentParser()
    p.add_argument("--timeout", "-t", type=float, default=10)
    p.add_argument("ports", nargs="+", type=int)
    args = p.parse_args()

    pool = concurrent.futures.ThreadPoolExecutor()
    tasks = []

    for port in args.ports:
        l = Listener(port, timeout=args.timeout)
        tasks.append(pool.submit(l.run))

    concurrent.futures.wait(tasks)


if __name__ == "__main__":
    main()

And then run it like:

python listener.py 100 200 300 &

If you really want to handle backgrounding in your code, you're probably better off with simple os.fork instead of the daemonize module, since your goal isn't really to write a daemon; you want something that you expect to write to your terminal. That might look like this:

#!/usr/bin/python3

import socket
import os


class Listener:
    def __init__(self, port: int, timeout: float = 10):
        self.port = port
        self.timeout = timeout

    def run(self):
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.settimeout(self.timeout)
            s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            s.bind(("", self.port))
            s.listen(1)
            try:
                print(f"Waiting for connection on port {self.port}")
                conn, addr = s.accept()
                print(addr, conn)
            except TimeoutError:
                print(f"Listener on {self.port} timed out.")

def main():
    p = argparse.ArgumentParser()
    p.add_argument("--timeout", "-t", type=float, default=10)
    p.add_argument("ports", nargs="+", type=int)
    args = p.parse_args()

    for port in args.ports:
        listener = Listener(port, timeout=args.timeout)
        pid = os.fork()
        if pid == 0:
            listener.run()
            return


if __name__ == "__main__":
    main()

Running that code (and connecting to the listening ports from another terminal) looks like this:

$ python listener.py 2001 2002 2003
Waiting for connection on port 2001
Waiting for connection on port 2002
Waiting for connection on port 2003
$ ('127.0.0.1', 51466) <socket.socket fd=4, family=2, type=1, proto=0, laddr=('127.0.0.1', 2001), raddr=('127.0.0.1', 51466)>
('127.0.0.1', 44602) <socket.socket fd=4, family=2, type=1, proto=0, laddr=('127.0.0.1', 2002), raddr=('127.0.0.1', 44602)>
('127.0.0.1', 38672) <socket.socket fd=4, family=2, type=1, proto=0, laddr=('127.0.0.1', 2003), raddr=('127.0.0.1', 38672)>

If you let the listeners time out, it looks like:

$ py listener.py 2001 2002 2003
Waiting for connection on port 2001
Waiting for connection on port 2002
Waiting for connection on port 2003
$ Listener on 2003 timed out.
Listener on 2001 timed out.
Listener on 2002 timed out.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文