我有一个TCP服务器和客户端。在服务器脚本的某个时刻,我启动一个进程,该进程需要能够获取每个新连接并向其发送数据。为了做到这一点,我有一个multiprocessing.Queue()
,我想从主进程中放入每个新连接,以便我打开的进程可以从中获取连接并将数据发送给它们。但是,您似乎无法将任何想要的内容传递给队列。当我尝试传递连接(套接字对象)时,我得到:
Traceback (most recent call last):
File "/usr/lib/python2.7/multiprocessing/queues.py", line 266, in _feed
send(obj)
TypeError: expected string or Unicode object, NoneType found
我可以使用其他替代方案吗?
答案 0 :(得分:6)
通过multiprocessing.Queue
发送套接字可以正常工作从python3.4开始,因为从该版本开始ForkingPickler
is used序列化要放入队列的对象,并且该pickler知道如何序列化套接字和其他包含文件句柄的对象。
multiprocessing.reduction.ForkingPickler
类已经存在于python2.7中并且可以腌制套接字,它只是multiprocessing.Queue
没有使用。
如果你不能切换到python3.4 +并且在python2.7中真的需要类似的功能,那么解决方法是创建一个使用ForkingPickler序列化对象的函数,例如:
from multiprocessing.reduction import ForkingPickler
import StringIO
def forking_dumps(obj):
buf = StringIO.StringIO()
ForkingPickler(buf).dump(obj)
return buf.getvalue()
而不是直接发送套接字,然后需要发送其pickle版本并在消费者中取消它。简单的例子:
from multiprocessing import Queue, Process
from socket import socket
import pickle
def handle(q):
sock = pickle.loads(q.get())
print 'rest:', sock.recv(2048)
if __name__ == '__main__':
sock = socket()
sock.connect(('httpbin.org', 80))
sock.send(b'GET /get\r\n')
# first bytes read in parent
print 'first part:', sock.recv(50)
q = Queue()
proc = Process(target=handle, args=(q,))
proc.start()
# use the function from above to serialize socket
q.put(forking_dumps(sock))
proc.join()
使插槽pickleable只在多处理的上下文中有意义,将它写入文件并稍后使用或尝试在不同的PC上使用或在原始进程结束后没有意义。因此,全局制作套接字可选不是一个好主意(例如,使用copyreg
机制)。