Python在Linux上的进程间通信

7

有很多关于如何完成以下操作的示例:

1)在同一程序中的不同进程之间进行通信。

2)在网络上的客户端/服务器之间进行通信。

但是,在我查找过的任何地方都没有一个好的例子来回答以下问题:

  • 从Python程序A发送字符串到程序B的规范方法是什么?程序B需要阻止并处理该字符串,然后在循环中等待接收下一个字符串。

我觉得我多次接近答案,但从未成功创建一个可工作的示例。

附加隐含要求:

  • 实际上是两个不同的程序:这个示例实际上需要两个不同的程序(即两个文件progA.py,progB.py,可以分别在同一台机器上的两个屏幕上从命令行分别运行),而不是使用任何类型的forking或multiprocess来创建客户端和服务器。
  • 请建议一种允许发送可变长度的分隔符字符串的方法,而不必精确计算数据大小的字节数的方法(后者在实现时更容易出错)。
  • 最好不使用localhost互联网连接。

例如,当读者使用:

pipein = open(pipe_name, 'r')
while program.KeepRunning:
    action = pipein.readline()[:-1]
    program.processLine(line)
    time.sleep(1)

作者使用:

command = "enable"
pipeout = os.open(pipe_name, os.O_WRONLY)
os.write(pipeout, command)
os.write(pipeout, "\n")

如建议所述http://www.python-course.eu/pipes.php,读者在读取空字符串时会陷入无限循环。
有趣的是,在program.processLine函数中添加if(action == 'enable'): longFunction()将导致longFunction中的部分代码在永久阻塞读取空行之前被执行。
另一方面,所有使用更现代且不那么低级的subprocess模块的示例都涉及多线程应用程序,而不是多个应用程序。其他实现涉及套接字和网络。
虽然我尝试过利用套接字,但这会导致常见的“出了些问题”的错误,其中有许多可能的原因,例如Error 111: "connection refused"。由于python代码的一部分在接收特定命令时会修改网络配置(例如,它使用各种参数调用iptciptables等命令),因此使用与localhost的网络连接可能需要避免,这会导致难以调试且通常令人讨厌的问题。除非第二个程序在同一台机器上运行,否则不应该使用网络接口进行进程间通信。

你尝试过 multiprocessing.Queue 吗? - Pynchia
1个回答

3
这是预期的行为。查看此答案以获取类似问题和FIFO行为概述。与您的问题相关的部分如下:

当没有更多的写入者时(...)读取器通过read()返回EOF被通知。

file.readline() 文档说,''(空字符串)表示到达了EOF

如果f.readline()返回一个空字符串,则已到达文件末尾,而空行由'\n'表示,这是仅包含单个换行符的字符串。

就是这样。在每次尝试读取时,您会得到一个空字符串的无限循环,这意味着没有更多的写入者连接。
没有任何阻止您使用命名管道来解决您的任务。最简单的方法只是在没有写入者时休眠一段时间。以下是可行的示例:
# server.py

import os
import time

pipe_name = 'pipe_test'

if not os.path.exists(pipe_name):
    os.mkfifo(pipe_name)

with open(pipe_name, 'r') as pipe:
    print("Listening for actions...")
    while True:
        action = pipe.readline()[:-1]
        if action == '':
            print("No clients. Sleeping...")
            time.sleep(1)
        else:
            print("Action received:", repr(action))

# client.py

import os

pipe_name = 'pipe_test'

if not os.path.exists(pipe_name):
    os.mkfifo(pipe_name)

print("Waiting for server to start...")
with open(pipe_name, 'w') as pipe:
    action = input("Enter action to send: ")
    pipe.write(action + '\n')

注意:

  • 使用底层函数os.open()没有意义,因为你可以使用open()与命名管道进行交互。
  • 打开用于读取的命名管道会阻塞,直到第一个写入者连接。因此,在第一个客户端连接到管道之前,输出中不会出现Listening for actions...。写入者在没有读取器的情况下也是如此。
  • 你要求“处理该字符串并在循环中等待另一个字符串”。除非在单独的线程中处理该字符串,否则它不会尝试读取下一个字符串,直到当前字符串被处理。

很好的回答。有一个问题:为什么要使用[:-1]?如果我理解正确,如果在服务器休眠时客户端添加了多行到管道中,那么只有这些行中的最后一行会被读取。 - aphid
f.readline() 返回带有尾随的 \n 的行,因此使用 [:-1] 来从字符串末尾剥离这个 \n。不,当进程唤醒时,所有在睡眠期间添加的行都将被处理。您可以通过增加睡眠时间并在睡眠时间内发送多行来进行测试。管道在服务器休眠时存储所有行。 - Yaroslav Admin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接