【问题标题】:RuntimeError: received 0 items of ancdata when sharing sockets between processesRuntimeError: 在进程之间共享套接字时收到 0 项 ancdata
【发布时间】:2016-08-08 01:48:11
【问题描述】:

这是我能想象的最少的代码,没有错误处理、优雅退出等。

#!/usr/bin/env python3.5
import multiprocessing, socket, traceback
from multiprocessing import reduction

def loop(pipe):
    while True:
        try:
            c = socket.fromfd(reduction.recv_handle(pipe), socket.AF_INET, socket.SOCK_STREAM)
            c.sendall('HTTP/1.1 200 OK\r\nContent-Length: 6\r\nContent-Type: text/plain\r\n\r\nhello\n'.encode())
            c.shutdown(socket.SHUT_WR)
            c.close()
        except: print(traceback.format_exc())

if __name__ == '__main__':
    pipe_recv, pipe_send = multiprocessing.Pipe()

    proc = multiprocessing.Process(target=loop, args=(pipe_recv,))
    proc.start()

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(('localhost', 9000))
    s.listen()

    while True:
        conn, addr = s.accept()
        reduction.send_handle(pipe_send, conn.fileno(), proc.pid)

我可以curl它:

curl -v http://localhost:9000/
*   Trying 127.0.0.1...
* Connected to localhost (127.0.0.1) port 9000 (#0)
> GET / HTTP/1.1
> Host: localhost:9000
> User-Agent: curl/7.47.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Length: 6
< Content-Type: text/plain
<
hello
* Connection #0 to host localhost left intact

当我有一个或几个循环的 curl-s 时它工作得很好,但是如果我创建了很多“客户端”或者只是使用 apache 基准测试,例如,脚本会崩溃:

Traceback (most recent call last):
  File "/tmp/this-is-my-script", line 8, in loop
    c = socket.fromfd(reduction.recv_handle(pipe), socket.AF_INET, socket.SOCK_STREAM)
  File "/usr/lib/python3.5/multiprocessing/reduction.py", line 181, in recv_handle
    return recvfds(s, 1)[0]
  File "/usr/lib/python3.5/multiprocessing/reduction.py", line 160, in recvfds
    len(ancdata))
RuntimeError: received 0 items of ancdata

我花了很多时间试图理解这一点,但没有运气。所以我放弃了,问了这个问题。 reduction 模块甚至没有记录在 3.5 中。这是否意味着我不应该使用它?

如果没有 - 我看不到在进程之间共享套接字的任何其他可能性,有吗?

如果是 - 我想念什么?

【问题讨论】:

  • 根据docs.python.org/2/library/…socket.shutdown(socket.SHUT_WR) 只关闭了一半的连接。这可能与您的挂起有关。
  • 是的,但是另一半必须被客户端关闭,否则socket会在服务器上保持TIME_WAIT状态。

标签: python sockets multiprocessing


【解决方案1】:

终于!

  1. 无需致电socket.fromfd()。我的代码基于this gist,不确定python2,但在3.5中它肯定会导致不必要的套接字重复创建,而这又会保留在TIME_WAIT中,如果直接调用socket.socket()可以避免。

fromfd() 来源:

def fromfd(fd, family, type, proto=0):
    nfd = dup(fd)
    return socket(family, type, proto, nfd)
  1. 根据this IBM article在发送方传输后必须关闭套接字

  2. 此外,在子进程中无条件关闭套接字会导致 apache 基准程序挂起。完全没有close() 就可以正常工作。

所以最终版本如下。它实际上不是多处理的,因此它会使用 -c 参数使 apache 基准测试失败。但这超出了问题的范围。

#!/usr/bin/env python3.5
import multiprocessing, socket, traceback
from multiprocessing import reduction

def loop(pipe_recv):
    while True:
        try:
            conn = socket.socket(fileno=reduction.recv_handle(pipe_recv))
            conn.sendall('HTTP/1.0 200 OK\r\nContent-Length: 6\r\nContent-Type: text/plain\r\n\r\nhello\n'.encode())
            conn.shutdown(socket.SHUT_WR)
        except: print(traceback.format_exc())

if __name__ == '__main__':
    pipe_recv, pipe_send = multiprocessing.Pipe()

    proc = multiprocessing.Process(target=loop, args=(pipe_recv,))
    proc.start()

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(('localhost', 9000))
    s.listen(100)

    while True:
        conn, addr = s.accept()
        reduction.send_handle(pipe_send, conn.fileno(), proc.pid)
        conn.close()

【讨论】:

  • “在发送方传输后必须关闭套接字”——不仅根据“IBM 文章”,stackoverflow.com/questions/17297810/… 也是如此。
  • @ivan_pozdeev:我刚刚意识到,在这种特定情况下,close() 可以省略,因为conn 会立即被垃圾收集。但你是对的,这是在 SO 上讨论过的,我错过了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-19
  • 2017-06-14
  • 2014-01-15
  • 1970-01-01
  • 2012-08-08
  • 1970-01-01
相关资源
最近更新 更多