命名管道上的 WriteFile 有时会返回 ERROR_NO_DATA

Posted

技术标签:

【中文标题】命名管道上的 WriteFile 有时会返回 ERROR_NO_DATA【英文标题】:WriteFile on a named pipe sometimes returns ERROR_NO_DATA 【发布时间】:2011-05-24 02:01:42 【问题描述】:

我有一个 C++ 程序,它正在创建一个命名管道来写入数据。有客户反映客户端连接命名管道,但服务端写入数据失败(ERROR_NO_DATA)。

在我能找到的任何 MSDN 页面中都没有真正解释此错误代码;有人对如何解决这个问题有任何想法吗?或者是什么原因?

打开代码:

ostringstream pipeName;
pipeName << "\\\\.\\pipe\\unique-named-pipe-" << GetCurrentProcessId();

pipeHandle = CreateNamedPipeA(
    pipeName.str().c_str(),              // pipe name
    PIPE_ACCESS_DUPLEX,                  // open mode
    PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, // pipe mode
    PIPE_UNLIMITED_INSTANCES,            // max instances
    512,                                 // output buffer size
    512,                                 // input buffer size
    0,                                   // use default timeouts
    NULL);                               // security attributes

if (INVALID_HANDLE_VALUE == pipeHandle)

    THROW("Failed to create named pipe", GetLastError());


cout << "Pipe ready" << endl;

// Wait for a client to connect to the pipe        
BOOL status = ConnectNamedPipe(pipeHandle, NULL);

if (!status)

    DWORD lastError = GetLastError();

    if (ERROR_PIPE_CONNECTED != lastError)
    
        THROW("Failed to wait for client to open pipe", lastError);
    
    else
    
        // Ignore, see MSDN docs for ConnectNamedPipe() for details.
    

编写代码:

// response is a std::string
int writeOffset = 0;
int length = response.length();

while ((int) response.length() > writeOffset)

    DWORD bytesWritten;

    BOOL status = WriteFile(
        pipeHandle,
        response.c_str() + writeOffset,
        length - writeOffset,
        &bytesWritten,
        NULL);

    if (!status)
    
        // This sometimes fails with ERROR_NO_DATA, why??
        THROW("Failed to send via named pipe", GetLastError());
    

    writeOffset += bytesWritten;

抛出宏

#define THROW(message, errorCode) \
 \
    fprintf(stderr, "%s: line: %d file: %s error:0x%x\n", \
            message, __LINE__, __FILE__, errorCode); \
    fflush(stderr); \
    throw message; \
 \

谢谢!

【问题讨论】:

您是否有可能在 response.c_str() + writeOffset 中移出 char 数组 @rerun 我不这么认为,我已经在写作循环中添加了这样你就可以看到发生了什么。 看不到 THROW 宏的作用。 语句之前调用 GetLastError()。 @Hans 我也添加了 THROW 宏的代码。 Hmya,您假设检索 stderr 不会改变线程的最后一个错误状态。也许不是,当您使用 /MD 构建时,它会变得非常混乱。像这样的宏是邪恶的,内联函数不是。 【参考方案1】:

查看 WinError.h,这是定义此错误代码和其他错误代码的位置:

//
// MessageId: ERROR_NO_DATA
//
// MessageText:
//
// The pipe is being closed.
//
#define ERROR_NO_DATA                    232L

听起来客户端已经关闭了管道的末端——也许客户端代码认为它已经获得了完整的字符串,关闭了它们的末端,而上面的代码继续尝试编写?

【讨论】:

客户端肯定期待更多的数据并得到一个意外的 EOF 类型错误。据我所知,客户端并没有先关闭管道。 这个page 提到ERROR_NO_DATA 是一种可能性,如果客户端在服务器尝试读取管道时关闭管道,但它没有解释写入情况。 MSDN 上没有明确指出,但有一些网页表明此错误也适用于 WriteFile - 这是 MSDN 中使用它的一些示例代码:support.microsoft.com/kb/190351 你可能想要发布客户端代码。客户如何知道何时关闭管道或何时有完整的字符串?这是否会发生在循环中的第一次写入时,或者仅发生在第二次或以后的调用中? 在客户端调用CloseHandle 之前,我在服务器的第一个WriteFile 上看到了ERROR_NO_DATA。但是,在这种情况下,客户端首先使用了WriteFile,发送了一个可变长度的字符串,而服务器未能匹配到ReadFileGetLastError=109, ERROR_BROKEN_PIPE)。

以上是关于命名管道上的 WriteFile 有时会返回 ERROR_NO_DATA的主要内容,如果未能解决你的问题,请参考以下文章

命名管道问题

WriteFile() 块(通过命名管道从 C++ 客户端写入 C# 服务器)

fprintf vs WriteFile 写入管道:无法从所有管道读取

命名管道中的 C# 死锁

为啥线程在进程间通信期间会破坏命名管道?

打破 ReadFile() 阻塞 - 命名管道 (Windows API)