【问题标题】:Named pipe messages corrupted (Win32,C)命名管道消息损坏 (Win32,C)
【发布时间】:2016-02-04 18:56:33
【问题描述】:

我有一些管道通信代码 - 接收到的字节与发送的字节不匹配。 有一个循环调用“CallNamedPipe”将消息发送到服务器。 现在只有第一条消息完好无损地接收到,其余的所有消息都被部分填充为 0xCD 字节。 看来,当我在发送后释放内存时 - 服务器线程仍在读取它。 MSDN 说 CallNamedPipe() 是一个完整的消息序列:打开管道、发送字节并关闭管道。 所以,这对我来说似乎很奇怪。我必须提到,这段代码是由 VC++ 6.0 构建的——一个非常古老的编译器。代码在 Windows 7 上运行,也许我需要使用兼容模式?客户端和服务器可执行文件都在同一个物理系统上运行,而不是远程运行。客户端在启动时使用 CreateProcess() 来启动服务器。消息是在很久以后发送的,所以我希望比赛条件不重要。

感谢您的建议。

============ Client side (pseudocode): ============
for (iPiece=0; iPiece < nPieces; ++iPiece)
{
    buffer = malloc (2048);

    // copy some data bytes into buffer (1..2048 bytes)
    // log 1st 32 DWORDS from message about to be sent

    if (! CallNamedPipe (name, buffer, nBytes, ..., 1000))
    {
        // diagnostics: call to GetLastError(), etc.
    }
    free (buffer);
}

============ Server side (pseudocode): ============
DWORD __stdcall ServerThreadProc (PVOID p)
{
    UINT    cbMaxMsg = 0x10000; // 64K for a pipe message
    PVOID   buffer = malloc (cbMaxMsg);
    HANDLE  hPipe;
    BOOL    fAbort = 0;

    hPipe = CreateNamedPipe (name, PIPE_ACCESS_DUPLEX,
        PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE,
        PIPE_UNLIMITED_INSTANCES, cbMaxMsg, cbMaxMsg, 1000, NULL);

    while (fAbort == 0) // one of pipe messages sets fAbort=1, so thread can return.
    {
        if (ConnectNamedPipe (hPipe, NULL))
        {
            DWORD   bytesLoaded = 0;

            ReadFile (hPipe, buffer, cbMaxMsg, &bytesLoaded, NULL);
            if (bytesLoaded)
            {
                // log 1st 32 DWORDS from received message
                // process the pipe message (switch/case)
                // data may be written back to client after processing

                FlushFileBuffers (hPipe);
            }
            DisconnectNamedPipe (hPipe);
        }
        else
        {
            // diagnostics, GLE(), etc.
        }
    }

    free (buffer);
    CloseHandle (hPipe);
    return 0;
}

【问题讨论】:

  • 请显示实际填充缓冲区以进行发送和处理读取的字节的代码。在客户端,您每次都分配一个新的缓冲区,但您是否检查分配错误? nBytes 每次都是相同的值吗?在服务器端,您为每个客户端连接的每次读取重复使用一个缓冲区。不同大小的数据包不会使用缓冲区中的所有相同字节。如果ReadFile() 成功,则在处理缓冲区时必须考虑bytesLoaded 的值。你这样做吗?
  • 请提供一个Minimal, Complete, and Verifiable example 来演示错误操作。
  • 1) 刷新管道的读取端不是一个好主意。这将破坏您实际想要保留的字节。 2)只连接一次管道,只断开一次。 3)管道不知道“记录”它只是一个字节流。因此,管道的每次读取都可以获得 0 字节或 1000 字节或任何其他字节数。这意味着管道的读取可能包含任何数据组合。代码需要实现某种协议,因此代码可以确定来自客户端的“写入”何时开始/结束(续)
  • (cont) 建议一个标志和计数是客户端发送的每个缓冲区的第一部分,建议客户端只发送实际包含数据的字节数,而不是整个 malloc 的缓冲区。如果没有数据包含-1,那可能是标志,服务器需要实现为:读取int大小,如果结果==标志,然后读取计数,然后循环,调用读取计数...计数- 以前读取等,将字节累积到缓冲区中,直到读取整个记录。然后处理该记录。然后阅读,再次查看标志。
  • CallNamedPipe() 的 MSDN 也表示 If the message written to the pipe by the server process is longer than nOutBufferSize, CallNamedPipe returns FALSE, and GetLastError returns ERROR_MORE_DATA. The remainder of the message is discarded, because CallNamedPipe closes the handle to the pipe before returning.。您确定消息没有超出缓冲区吗?您检查ERROR_MORE_DATA. 的错误吗?即使您没有从服务器端写入任何消息,这也是如此。

标签: c winapi named-pipes


【解决方案1】:

使用PIPE_TYPE_MESSAGE 使管道成为消息管道。它将向服务器提供类似行为的消息。如果您尝试阅读超过标准邮件大小的内容,它将为您提供完整的邮件。

0xCD 是标准填充,我认为是堆,但不确定。

到目前为止,我们正在读取 64k 的数据,并写入 2k 的数据。看起来 CallNamedPipe 在数据被接受之前不会返回(因为有超时 - 设置为 1000 毫秒)。这些系统的行为是,一旦内核拥有数据缓冲区,则不允许客户端代码更改内存。

我会说最有可能的情况,就是缓冲区没有被正确填充,并且服务器中的数据量与管道中的消息一致。

您没有提供足够的数据来验证这一点。

【讨论】:

  • 我将创建一个工作示例。我想知道我可以发布多少代码。我将创建两个程序,就像在产品中一样。我只发布了伪代码,检查了所有分配,没有记录任何失败,没有 ERROR_MORE_DATA 或任何其他问题。我记录发送和接收的字节-它们仅在第一个 2K 字节块上匹配。接收字节数与发送字节数相同。用较大缓冲区中的 memcpy() 填充的缓冲区。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-08
  • 2010-10-28
  • 2011-03-19
  • 1970-01-01
相关资源
最近更新 更多