命名管道:ConnectNamedPipe 后的 ReadFile 返回 ERROR_BROKEN_PIPE

Posted

技术标签:

【中文标题】命名管道:ConnectNamedPipe 后的 ReadFile 返回 ERROR_BROKEN_PIPE【英文标题】:Named pipe: ReadFile after ConnectNamedPipe return ERROR_BROKEN_PIPE 【发布时间】:2016-07-02 10:35:50 【问题描述】:

我重新激活了几个月前可以使用的代码。它让我发疯,但它不再。我在其他问题中找不到答案。

在服务器端,我使用

创建了一个管道
#define MAX_MESSAGE_LENGTH 1024
SECURITY_ATTRIBUTES sa;
SECURITY_DESCRIPTOR sd;
InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);
SetSecurityDescriptorDacl(&sd, TRUE, static_cast<PACL>(0), FALSE);

sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = FALSE;

auto pipe_name = _T("\\\\.\\pipe\\") + _serviceName;

HANDLE pipe = CreateNamedPipe(
    pipe_name.c_str(),
    PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
    1, 
    MAX_MESSAGE_LENGTH, MAX_MESSAGE_LENGTH, // buffer lengths (advisory)
    0, // default timeout of 50ms when WaitNamedPipe uses NMPWAIT_USE_DEFAULT_WAIT
    &sa));

然后一个线程等待带有ConnectNamedPipe 的传入客户端。 ConnectNamedPipe 阻塞,直到客户端连接

HANDLE pipe = CreateFile(
    pipe_name.c_str(),   // pipe name 
    GENERIC_READ |  // read and write access 
    GENERIC_WRITE,
    0,              // no sharing 
    NULL,           // default security attributes
    OPEN_EXISTING,  // opens existing pipe 
    FILE_ATTRIBUTE_NORMAL, // default attributes 
    NULL);         // no template file 

服务器上的ConnectNamedPipe 然后返回TRUEGetLastError == 0。但是当它尝试调用ReadFile 来读取管道上的传入数据时,ReadFile 立即返回FALSEGetLastError==ERROR_BROKEN_PIPE。 在客户端,CreateFile 已返回 GetLastError==231,“所有管道实例都忙”。虽然它是唯一的客户!对 WaitNamedPipe(pipe, 2000) 的调用返回错误代码 121,“信号量超时期限已过期”。 增加 CreateNamedPipe 中允许的客户端数量不会改变任何事情。

在客户端尝试连接的那一刻,管道似乎完全断开了。但为什么?客户端和服务器都以相同的用户甚至相同的会话在同一台机器上运行。 对ConnectNamedPipe 的另一次调用失败,GLE=232:“管道正在关闭”。

我还有其他SECURITY_ATTRIBUTES 用于CreateNamedPipe,这将允许非提升用户连接,但这没有区别。

我还尝试在客户端上使用CallNamedPipe,结果相同。

【问题讨论】:

请提供Minimal, Complete, and Verifiable example,说明您是如何在两端使用管道的。 我猜你有一些流氓程序连接到你的管道然后关闭。您可以尝试更改管道的名称,看看是否有任何变化。如果失败,我建议使用Process Monitor 查找原因。 如果@rodrigo 的猜测是正确的(而且看起来不错),那么在 ConnectNamedPipe() 成功后立即在服务器中尝试 GetNamedPipeClientProcessId() 也是值得的。 我更改了管道名称但没有成功,GetNamedPipeClientProcessId() 返回正确的客户端 pid。我将创建一个完整的示例(抱歉,我过于关注 Minmal... :() 【参考方案1】:

PathFileExists 是管道杀手!经过数小时的尝试,我终于找到了破坏管道的原因:对管道名称的 PathFileExists 的简单调用!这是最近在客户端添加的,用于检查管道是否已创建。我查看了代码更改,但我完全错过了。 PathFileExists 正确返回 true 或 false,但似乎弄乱了管道(正如我所说的那样,允许多个客户端连接没有帮助)。啊!!!

【讨论】:

令人惊讶的是,构建 MCVE 可以让问题的原因变得显而易见。 :-) 感谢您回帖。 有趣的是,它看起来像PathFileExists 打开和关闭文件,或者类似的东西。所以我上面的猜测部分正确。顺便说一句,你应该接受这个答案,即使它是你自己的。

以上是关于命名管道:ConnectNamedPipe 后的 ReadFile 返回 ERROR_BROKEN_PIPE的主要内容,如果未能解决你的问题,请参考以下文章

未发出信号的非阻塞 ConnectNamedPipe 事件

命名管道 232 管道正在关闭

C/S模型之命名管道

C++ 使用命名管道

Windows 命名管道无法正确响应并发请求

C中的命名管道读取奇怪的结果