取消接受并关闭Python处理/多处理侦听器连接的正确方法
(本例中我使用的是模块,但如果运行或使用 我目前有一个程序可以监听unix套接字(使用processing.connection.Listener)、接受连接并生成一个线程来处理请求。在某一点上,我想优雅地退出这个过程,但由于accept()-调用被阻塞,我看不到任何方法可以很好地取消它。我至少有一种方法可以在这里工作(OS X),设置一个信号处理程序并从另一个线程向进程发送信号,如下所示:取消接受并关闭Python处理/多处理侦听器连接的正确方法,python,sockets,multiprocessing,Python,Sockets,Multiprocessing,(本例中我使用的是模块,但如果运行或使用 我目前有一个程序可以监听unix套接字(使用processing.connection.Listener)、接受连接并生成一个线程来处理请求。在某一点上,我想优雅地退出这个过程,但由于accept()-调用被阻塞,我看不到任何方法可以很好地取消它。我至少有一种方法可以在这里工作(OS X),设置一个信号处理程序并从另一个线程向进程发送信号,如下所示: import processing from processing.connection import
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.\u listener.\u socket)并设置非阻塞选项……但这可能会有一些副作用,通常非常可怕
有没有人有更优雅(甚至可能是正确的!)的方法来实现这一点?它需要可移植到OSX、Linux和BSD,但不需要Windows可移植性等
澄清:
谢谢大家!和往常一样,我原来的问题中的含糊不清之处也暴露出来了:)
- 我需要在取消侦听后执行清理,我并不总是想实际退出该过程
- 我需要能够从不是从同一父进程派生的其他进程访问此进程,这会使队列变得笨拙
- 产生线程的原因是:
- 它们访问共享状态。实际上,它或多或少是一个常见的内存数据库,所以我想它可以做得不同
- 我必须能够在同一时间接受多个连接,但实际的线程在大多数情况下都会阻塞。每个接受的连接产生一个新线程;这是为了不阻塞I/O操作上的所有客户端
关于线程与进程,我使用线程使阻塞操作成为非阻塞,并使用进程来启用多处理。可能不理想,但您可以通过从信号处理程序或终止进程的线程向套接字发送一些数据来释放阻塞
编辑:实现这一点的另一种方法可能是使用连接,因为它们似乎支持超时(抱歉,我在第一次阅读时误读了您的代码)。我是多处理模块的新手,但在我看来,混合处理模块和线程模块是违反直觉的,他们不是针对解决同样的问题吗 无论如何,将侦听函数包装到进程本身如何?我不清楚这会如何影响代码的其余部分,但这可能是一个更干净的选择
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.'
这不是select的作用吗 仅当select指示套接字不会阻止时,才调用套接字上的accept select有一个超时,因此您可以偶尔中断以进行检查
如果是时候关机了……我想我可以避免它,但似乎我必须这样做:
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()
# ...
我知道这违反了德米特定律,引入了一些邪恶的猴子补丁,但这似乎是实现这一点最容易的方法。如果有人有更优雅的解决方案,我很乐意听到:)我遇到了同样的问题。我通过向侦听器发送一个“stop”命令来解决这个问题。在监听器的主线程(处理传入消息的线程)中,每次接收到新消息时,我都会检查它是否是“停止”命令并退出主线程 以下是我使用的代码:
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()
我正在使用身份验证密钥,通过从任意客户端发送stop命令来防止可能的黑客关闭我的服务
我的不是一个完美的解决方案。似乎更好的解决方案是修改
multiprocessing.connection.Listener
中的代码,并添加stop()
方法。但是,这需要通过流程将其发送给Python团队进行审批。由于它不是接收操作,我正在尝试取消它,因此我不能只将数据发送到连接(没有),而且那里还有竞争条件。第二,如果我真的在调查数据,那就行了,我不是;我在等一个新的连接。或者我误解了您的意思?既然侦听器正在接受指定套接字上的连接,那么不应该从另一个线程release accept()连接到它(使用multiprocessing.connection.Client)?对于我的回答第二部分中的歧义,我深表歉意,我会更正它。队列无法从外部工具等中发现。连接到套接字将释放accepy(),但我认为这会创建一个竞争。这或多或少是我所做的,但您发送的是SIGTERM而不是SIGPPIPE,并且您无法在run()的上下文中执行清理,而是在一个信号处理器中。谢谢你的澄清,现在我想不出什么,如果我想到什么,我会更新我的答案@HenrikGustafsson如果您将所有需要清理的内容保存到IVAR中,您不能在\uu del\uuu
中进行清理吗?这本身不是一个坏主意,但侦听器对象不会暴露底层套接字,我也不希望以如此大的方式违反demeters法。然而,事实证明,这正是我必须做的:)