取消接受和关闭Python处理/多处理侦听器连接的正确方法

时间:2008-12-10 21:25:18

标签: python sockets multiprocessing

(我在这个示例中使用的是pyprocessing模块,但如果您运行python 2.6或使用multiprocessing backport

,则可能会使用多处理替换处理

我目前有一个程序可以监听unix套接字(使用processing.connection.Listener),接受连接并生成处理请求的线程。在某一点上,我想优雅地退出该过程,但由于accept() - 调用是阻塞的,我认为没有办法以一种很好的方式取消它。我有一种方法在这里工作(OS X)至少,设置一个信号处理程序并从另一个线程发信号通知过程如下:

import processing
from processing.connection import Listener
import threading
import time
import os
import signal
import socket
import errno

# This is actually called by the connection handler.
def closeme():
    time.sleep(1)
    print 'Closing socket...'
    listener.close()
    os.kill(processing.currentProcess().getPid(), signal.SIGPIPE)

oldsig = signal.signal(signal.SIGPIPE, lambda s, f: None)

listener = Listener('/tmp/asdf', 'AF_UNIX')
# This is a thread that handles one already accepted connection, left out for brevity
threading.Thread(target=closeme).start()
print 'Accepting...'
try:
    listener.accept()
except socket.error, e:
    if e.args[0] != errno.EINTR:
        raise
# Cleanup here...
print 'Done...'

我想到的唯一另一种方法是深入到连接(listener._listener._socket)并设置非阻塞选项......但这可能有一些副作用,而且通常非常可怕。

有没有人有更优雅(甚至可能是正确的!)的方式来实现这个目标?它需要可移植到OS X,Linux和BSD,但不需要Windows可移植性等。

澄清: 谢谢大家!像往常一样,我原来问题的模糊性被揭示:)

  • 我取消收听后需要进行清理工作,并不总是希望退出该流程。
  • 我需要能够从不是从同一个父级生成的其他进程访问此进程,这会使队列变得笨拙
  • 线程的原因是:
    • 他们访问共享状态。实际上或多或少是一个常见的内存数据库,所以我想它可以用不同的方式完成。
    • 我必须能够同时接受多个连接,但实际的线程在大多数情况下都会阻塞。每个接受的连接都会生成一个新线程;这是为了不阻止I / O操作上的所有客户端。

关于线程与进程,我使用线程使阻塞操作非阻塞,并使用进程启用多处理。

5 个答案:

答案 0 :(得分:3)

不是选择是什么?

如果select表示它不会阻止,则只在socket上调用accept ...

选择有一个超时,所以你偶尔偶尔会爆发检查 如果有时间关闭....

答案 1 :(得分:3)

我以为我可以避免它,但似乎我必须做这样的事情:

from processing import connection
connection.Listener.fileno = lambda self: self._listener._socket.fileno()

import select

l = connection.Listener('/tmp/x', 'AF_UNIX')
r, w, e = select.select((l, ), (), ())
if l in r:
  print "Accepting..."
  c = l.accept()
  # ...

我知道这打破了demeter的规律,并引入了一些邪恶的猴子修补,但似乎这将是最容易实现这一目的的端口方式。如果有人有更优雅的解决方案,我会很高兴听到它:)

答案 2 :(得分:1)

我是多处理模块的新手,但在我看来,混合处理模块和线程模块是违反直觉的,不是他们的目标是解决同样的问题吗?

无论如何,如何将listen功能包装到一个进程本身?我不清楚这会如何影响你的其余代码,但这可能是一个更清洁的选择。

from multiprocessing import Process
from multiprocessing.connection import Listener


class ListenForConn(Process):

    def run(self):
        listener = Listener('/tmp/asdf', 'AF_UNIX')
        listener.accept()

        # do your other handling here


listen_process = ListenForConn()
listen_process.start()

print listen_process.is_alive()

listen_process.terminate()
listen_process.join()

print listen_process.is_alive()
print 'No more listen process.'

答案 3 :(得分:0)

可能不理想,但你可以通过从信号处理程序或终止进程的线程向套接字发送一些数据来释放块。

编辑:另一种实现方法可能是使用 Connection Queues,因为它们似乎支持超时(道歉,我在第一次阅读时误读了你的代码)。

答案 4 :(得分:0)

我遇到了同样的问题。我通过发送"停止"解决了这个问题。命令听众。在收听者的主要线程(处理传入消息的主线程)中,每次收到新消息时,我只是检查它是否是"停止"命令并退出主线程。

以下是我使用的代码:

settings->permalink

我使用身份验证密钥来阻止黑客通过从任意客户端发送停止命令来关闭我的服务。

我的不是一个完美的解决方案。似乎更好的解决方案可能是修改def start(self): """ Start listening """ # set the command being executed self.command = self.COMMAND_RUN # startup the 'listener_main' method as a daemon thread self.listener = Listener(address=self.address, authkey=self.authkey) self._thread = threading.Thread(target=self.listener_main, daemon=True) self._thread.start() def listener_main(self): """ The main application loop """ while self.command == self.COMMAND_RUN: # block until a client connection is recieved with self.listener.accept() as conn: # receive the subscription request from the client message = conn.recv() # if it's a shut down command, return to stop this thread if isinstance(message, str) and message == self.COMMAND_STOP: return # process the message def stop(self): """ Stops the listening thread """ self.command = self.COMMAND_STOP client = Client(self.address, authkey=self.authkey) client.send(self.COMMAND_STOP) client.close() self._thread.join() 中的代码,并添加multiprocessing.connection.Listener方法。但是,这需要通过流程将其发送给Python团队批准。