在断开连接之前等待读取命名管道

Posted

技术标签:

【中文标题】在断开连接之前等待读取命名管道【英文标题】:Wait for Named Pipe to be read before Disconnecting 【发布时间】:2020-08-31 13:18:09 【问题描述】:

我有一个用 Python 编写的程序,我需要通过 C++ 程序联系它。我在 C++ 程序上创建管道服务器,客户端在 Python 中。每次我尝试不同的行为时,我都无法理解如何在两个程序之间正确读/写。请注意,我希望管道保持打开状态,以便将来进行多次读取/写入。

服务器 (C++)

#include <windows.h>
#include <iostream>
using namespace std;

#define FGPIPE TEXT("\\\\.\\pipe\\FGChroma")

int main()


    HANDLE hPipe;
    DWORD dwWritten;
    DWORD MAX_BUF_SIZE = 8;

    hPipe = CreateNamedPipe(FGPIPE,
                            PIPE_ACCESS_OUTBOUND,
                            PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
                            2,
                            MAX_BUF_SIZE*16,
                            0,
                            NMPWAIT_USE_DEFAULT_WAIT,
                            NULL
                            );

    cout<<"Pipe? "<<hPipe<<endl;

    cout<<"Awaiting Connection"<<endl;
    cout<<ConnectNamedPipe(hPipe, NULL)<<endl;
    cout<<"Connected"<<endl;
    WriteFile(hPipe, "MSG1\n", 5, &dwWritten, NULL);
    WriteFile(hPipe, "MSG2\n", 5, &dwWritten, NULL);
    FlushFileBuffers(hPipe);
    ///Need to Wait for read here
    cout<<"Disconnecting"<<endl;
    DisconnectNamedPipe(hPipe);


客户端(Python)

f = open(r"\\.\\pipe\\FGChroma", 'r', 0)
while True:
    value = f.read()
    print(value)

当我尝试在 Python 中执行 f.read() 时,我得到一个错误 OSError 22 Invalid Argument,这是公平的,因为我已经断开了管道。但是,如果我不断开管道,那么 Python 代码永远不会完成读取并一直等到管道关闭或断开连接。我觉得解决方案很简单,我只是错过了一些让我头疼的小东西。我阅读了命名管道和 Win API 的文档,我还尝试了 win32py 和其他替代方案,但我遇到了同样的问题;我不明白如何在 2 个实例之间保持连接并允许读取而不必断开连接或等待读取。

【问题讨论】:

【参考方案1】:

我认为 Python f.read() 和 Windows 管道存在问题。也许我错过了一些东西,但是当你像你一样读取管道时似乎没有 EOF,并且 python read() 将读取结束并出错,即使它之前正确读取了所有内容。

要解决此问题,您可以使用缓冲管道 (open(r"pipe", 'r')),然后一个接一个地读取字符 (f.read(1)) 直到出现错误,或者使用 os.read(f.fileno(), 1024),这是较低级别且有效在这种情况下。

【讨论】:

免责声明:我没有使用无缓冲管道进行测试,因为它在 python 3 中显然不受支持。 绝对了不起的答案。我怀疑这是 EOF 问题(因此为什么人们使用 win32py.ReadFile),但是您使用 f.read(1) 的解决方案效果很好!我将 1 替换为我的缓冲区大小,现在一切都稳定了。【参考方案2】:

你可以看这里Python and Windows Named Pipes

在 windows 上通过多个程序打开一个文件并不像在 unix 上那样。如果您无法避免它,请使用 win32pipe 但这会使您的代码不独立于平台

【讨论】:

【参考方案3】:

根据上面ElderBug 的回答,这是一个不使用缓冲区,而是使用行尾终止符的工作解决方案。您可能需要相应地调整您的代码。

def read_line(file_handle):
    EOF = False
    data = ""
    while True:
        try:
            c = file_handle.read(1).decode("utf-8")
        except OSError: # EOF
            EOF = True
            break
        if c == '\n':
            break
        if not c:
            EOF = True
            break
        data += c
    return data, EOF

print("Connecting to Pipe")
while True:
    try:
        f = open(r"\\.\\pipe\\FGChroma", 'rb', 0)
        break
    except FileNotFoundError:
        continue

print("Connected")

print("Reading Data")

while True:
    data, EOF = read_line(f)
    if EOF: break
    print(data)

您可能还需要将阅读模式从'rb' 切换到'r'。仅将b 用于二进制模式,但您必须通过将缓冲区模式从0 设置为1 来启用缓冲。

【讨论】:

以上是关于在断开连接之前等待读取命名管道的主要内容,如果未能解决你的问题,请参考以下文章

检测命名管道与 I/O 完成断开连接

当客户端除了快速连接-断开之外啥都不做时,命名管道会失败

C#命名管道,如何检测客户端断开连接

命名管道:服务器端如何知道客户端已断开连接?

客户端断开连接后如何使命名管道不忙?

Android调试器不断断开连接