命名管道,如何知道在读取端读取的确切字节数。 C++, 视窗
Posted
技术标签:
【中文标题】命名管道,如何知道在读取端读取的确切字节数。 C++, 视窗【英文标题】:Named Pipes, How to know the exact number of bytes to read on Reading side. C++, Windows 【发布时间】:2012-09-22 10:44:00 【问题描述】:我正在使用配置为将数据作为单字节流发送的命名管道,以在两个应用程序之间发送序列化数据结构。序列化数据的大小变化很大。在发送端,这不是问题,我可以调整发送的字节数。
如何将接收(读取)端的缓冲区设置为要读取的确切字节数?有没有办法知道发送(写入)端的数据有多大?
我查看了 PeekNamedPipe,但该函数似乎对字节类型的命名管道无用?
lpBytesLeftThisMessage [输出,可选] 指向变量的指针,该变量接收此消息中剩余的字节数。对于字节类型的命名管道或匿名管道,此参数将为零。如果不读取数据,该参数可以为NULL。
http://msdn.microsoft.com/en-us/library/windows/desktop/aa365779(v=vs.85).aspx
如果您无法确定所需的确切缓冲区大小,如何最好地处理这种情况?
发送代码
string strData;
strData = "ShortLittleString";
DWORD numBytesWritten = 0;
result = WriteFile(
pipe, // handle to our outbound pipe
strData.c_str(), // data to send
strData.length(), // length of data to send (bytes)
&numBytesWritten, // will store actual amount of data sent
NULL // not using overlapped IO
);
阅读代码:
DWORD numBytesToRead0 = 0;
DWORD numBytesToRead1 = 0;
DWORD numBytesToRead2 = 0;
BOOL result = PeekNamedPipe(
pipe,
NULL,
42,
&numBytesToRead0,
&numBytesToRead1,
&numBytesToRead2
);
char * buffer ;
buffer = new char[numBytesToRead2];
char data[1024]; //1024 is way too big and numBytesToRead2 is always 0
DWORD _numBytesRead = 0;
BOOL result = ReadFile(
pipe,
data, // the data from the pipe will be put here
1024, // number of bytes allocated
&_numBytesRead, // this will store number of bytes actually read
NULL // not using overlapped IO
);
在上面的代码中,buffer 的大小始终为 0,因为 PeakNamedPipe 函数为所有 numBytesToRead 变量返回 0。有没有办法精确设置这个缓冲区大小?如果不是,那么处理这种情况的最佳方法是什么?感谢您的帮助!
【问题讨论】:
一个简单的方法是先写长度,再写数据。然后接收器可以简单地先读取长度并分配缓冲区。与管道消息模式的工作方式不同。 【参考方案1】:为什么你认为你不能使用lpTotalBytesAvail
来获取发送的数据大小?它总是在字节模式下对我有用。如果它总是为零,则可能您做错了什么。还建议使用std::vector
作为数据缓冲区,这比乱用原始指针和new
语句要安全得多。
lpTotalBytesAvail [out, optional]
一个指向变量的指针,该变量接收可从管道读取的总字节数。如果不读取数据,该参数可以为NULL。
示例代码:
// Get data size available from pipe
DWORD bytesAvail = 0;
BOOL isOK = PeekNamedPipe(hPipe, NULL, 0, NULL, &bytesAvail, NULL);
if(!isOK)
// Check GetLastError() code
// Allocate buffer and peek data from pipe
DWORD bytesRead = 0;
std::vector<char> buffer(bytesAvail);
isOK = PeekNamedPipe(hPipe, &buffer[0], bytesAvail, &bytesRead, NULL, NULL);
if(!isOK)
// Check GetLastError() code
【讨论】:
lpTotalBytesAvail 对我来说非常有用!这为我节省了很多麻烦和昂贵的缓冲区副本来添加 Hans Passant 建议的大小。【参考方案2】:好吧,您正在使用 ReadFile()。除其他事项外,该文档还说:
如果在消息模式下正在读取命名管道并且下一条消息较长 比 nNumberOfBytesToRead 参数指定的,ReadFile 返回 FALSE 和 GetLastError 返回 ERROR_MORE_DATA。可以阅读邮件的其余部分 通过随后调用 ReadFile 或 PeekNamedPipe 函数。
你试过了吗?我从来没有使用过这样的管道:-),只用它们来访问子进程的标准输入/输出句柄。
我假设上述内容可以根据需要经常重复,从而使“消息的剩余部分”的描述有些不准确:我认为如果“剩余部分”不适合您的缓冲区,您只会得到另一个ERROR_MORE_DATA,这样你就知道要得到余数的余数了。
或者,如果我完全误解了您并且您实际上并没有使用这种“消息模式”的东西:也许您只是以错误的方式阅读内容。您可以只使用固定大小的缓冲区将数据读入并将其附加到您的最终块,直到您到达数据的末尾。或者通过增加“固定”大小缓冲区的大小来优化这一点。
【讨论】:
不,我没有。这似乎有点骇人听闻,但是您是否建议我以非常小的增量阅读并循环直到不再收到 ERROR_MORE_DATA 错误? :) 这应该可行,但我试图避免将数据大小附加到消息中或最终将不相关的数据附加到序列化数据中 - 基本上是在阅读消息后任何基于字符/字符串的清理工作。在EOF之前有没有办法像阅读文本文件一样阅读?我可以在写入端插入自己的 EOF 吗? 我没有使用消息模式,我使用的是字节模式。 :) 但我想你只是给了我一个想法,我认为字节模式是在发送固定长度的字符时使用的,我会砍掉并将系统更改为消息模式管道并报告回来? 字节模式专门设计用于让 您 负责管理消息传递协议,或缺少该协议,以应对您正在实施的用例。换句话说,消息管道的一部分是消息长度的属性。除非你通过发送代表它的数据来管理它,否则字节模式中不存在这样的属性。换句话说,消息模式是一个人递给你一个盒子,上面有一个标签,上面写着它有多少。字节模式只是一个向你扔产品的人。除非你知道“那个人”在想什么,否则你没有足够的能力与之合作。以上是关于命名管道,如何知道在读取端读取的确切字节数。 C++, 视窗的主要内容,如果未能解决你的问题,请参考以下文章