【问题标题】:Close listening socket in python thread在python线程中关闭监听套接字
【发布时间】:2013-05-20 00:42:11
【问题描述】:

我在尝试了解用于网络通信的套接字时遇到问题。我制作了一个简单的线程来监听连接并为连接客户端创建进程,但我的问题是我无法让线程正确加入,因为我还没有找到取消 socket.accept() 调用的方法当我想退出程序时。

我的代码是这样的;

class ServerThread( threading.Thread ):

    def __init__(self, queue, host, port):
        threading.Thread.__init__(self)
        self.queue = queue
        self.running = True
        self.hostname = host
        self.port = port

    def run(self):
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.bind((self.hostname, self.port))
        self.socket.listen(1)
        while self.running:
            try:
                conn, address = self.socket.accept()
                process = Process(target=server_slave, args=(conn, address, self.queue))
                process.daemon = True
                process.start()
            except socket.timeout:
                pass

    def stop(self):
        self.running = False
        self.socket.close()

我已经设法通过设置self.setDaemon(True) 并退出主程序来关闭程序,将所有内容交给伟大的垃圾收集器——但这似乎是一个糟糕的解决方案。我也尝试为套接字设置超时,但这会导致得到[Errno 35] Resource temporarily unavailable(不管实际超时,即使我将它设置为年......)。

我做错了什么?我是否以愚蠢的方式设计了线程,或者我错过了接受连接的一些内容?

【问题讨论】:

  • '但这似乎是一个糟糕的解决方案' - 它有效吗?如果您不喜欢让操作系统终止所有线程,您可以尝试从主线程或其他线程关闭侦听套接字。这通常会导致 accept() 'early' 返回错误。适用于大多数操作系统/语言,但未尝试使用 Python,因此不是答案。
  • 它有效 - 但我觉得这样做可能不会很好地对待潜在客户。我发布了一个看起来更好的解决方案,因为它允许代码完成,但它仍然看起来有点奇怪,因为它以创建一个我必须立即加入的新进程结束。

标签: python multithreading sockets


【解决方案1】:

最好的方法是使用一个与连接线程无关的监听线程,并给它一个合理的超时时间。超时时,检查该线程是否应该关闭,如果没有,则再次循环并返回侦听。

    def tcp_listen_handle(self, port=23, connects=5, timeout=2):
        """This is running in its own thread."""
        sock = socket.socket()
        sock.settimeout(timeout)
        sock.bind(('', port))
        sock.listen(connects)  # We accept more than one connection.
        while self.keep_running_the_listening_thread():
            connection = None
            addr = None
            try:
                connection, addr = sock.accept()
                print("Socket Connected: %s" % str(addr))
                # makes a thread deals with that stuff. We only do listening.
                self.handle_tcp_connection_in_another_thread(connection, addr)
            except socket.timeout:
                pass
            except OSError:
                # Some other error.
                print("Socket was killed: %s" % str(addr))
                if connection is not None:
                    connection.close()
        sock.close()

唯一要做的就是监听,超时,检查它是否应该在超时期间死掉,然后返回监听。一般的经验法则是线程应该检查它们是否应该死亡并尽可能快地尝试自己完成。如果您不想在线程解除阻塞和检查之前等待 2 秒超时等待。您可以自己连接。

【讨论】:

    【解决方案2】:

    允许退出程序的肮脏解决方案是使用os._exit(0)

    def stop(self):
        self.socket.close()
        os._exit(0)
    

    请注意,sys.exit 在尝试干净退出/释放资源时不起作用/阻塞。但是os._exit 是最低级别的方法,它可以正常工作,而其他任何方法都不起作用。

    操作系统本身会释放资源(在任何现代系统上),就像在 C 程序中执行 exit 时一样。

    【讨论】:

      【解决方案3】:

      部分测试解决方案

      1. self.socket.settimeout(0.1) 放在while 之前
      2. conn.settimeout(None) 放在accept 之后

      【讨论】:

      • 听从这个建议是迄今为止我一整天做出的最糟糕的决定
      • 我发现 settimeout() 为我工作,请参阅 stackoverflow.com/a/41643863/143931
      • 悖论,你能解释一下(我知道,3.5 年后)为什么这是一个错误的决定吗?我可以想象它以各种有趣的方式失败,但我希望避免自己学习这些。
      【解决方案4】:

      在大多数情况下,一旦连接被接受,您将打开一个新线程或进程。要关闭连接,请中断 while 循环。垃圾收集将删除线程或进程,但加入将确保没有留下任何东西。

      持久性套接字在用户关闭它们或它们超时时关闭。非持久性(例如静态网页)会在发送信息后关闭。

      这是 Python 中持久套接字服务器的一个很好的示例。它使用多处理,这意味着它可以跨多个内核运行以执行受 CPU 限制的任务。更常见的是多线程。

      import socket
      import multiprocessing
      
      def run():
          host = '000.000.000.000'
          port = 1212
          sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
          sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
          sock.bind(('', port))
          sock.listen(3)
          while True:
              p = multiprocessing.Process(target=worker, args=sock.accept()).start()
      def worker(conn, addr):
          while True:
              if data == '':
                  #remote connection closed
                  break
               if len(dataList) > 2:
                  # do stuff
                  print 'This code is untested'
      
      run()
      

      【讨论】:

        【解决方案5】:

        让线程关闭的一种方法似乎是与套接字建立连接,从而使线程继续完成。

        def stop(self):
            self.running = False
            socket.socket(socket.AF_INET, 
                          socket.SOCK_STREAM).connect( (self.hostname, self.port))
            self.socket.close()
        

        这可行,但仍然感觉它可能不是最佳的......

        【讨论】:

        • 是的——“人为地”满足等待条件,(如这里——打开一个临时的本地连接),也在“停止阻塞线程”的技巧包中。
        • 你好像知道这些东西! ^^ 谢谢你的帮助!我还可以尝试其他技巧吗?
        • 目前正试图理解这个问题/代码 - 我也会这样做,但我无法理解“socket,.socket(socket.AF_INET...) 行 - 它的目的是什么?
        • Kev1n91,socket.socket( [...] ).connect( [...] ) 行基本上是创建一个临时套接字对象,其中 AF_INET 和 SOCK_STREAM 是地址族和套接字类型。这用于创建与正在等待连接的进程的连接,以便可以告诉它停止等待!只是告诉它关闭是行不通的,因为它会在接受新指令之前永远等待连接。
        猜你喜欢
        • 2011-11-09
        • 2015-11-30
        • 2013-03-29
        • 1970-01-01
        • 2011-11-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多