提问人:prsm 提问时间:11/8/2023 更新时间:11/10/2023 访问量:125
如何在 Windows 中使用 c++ 创建命名管道,以便可以在给定管道名称的情况下从中读取数据,就好像它是常规文件一样?
How can I create a named pipe with c++ in Windows so that data can be read from it as if it were a regular file, given the pipe's name?
问:
问题
由于一些不寻常的要求,我希望能够从 Windows 中的 c++ 代码中从其名称打开命名管道,就好像它是常规的“文件路径”一样,而不是使用 .ReadFile
法典
我已经编写了服务器和客户端代码,以便在 Windows 中从命名管道中读取和写入。
服务器代码
#include <iostream>
#include <windows.h>
int main()
{
// Client will block if not in a thread.
const TCHAR *pipe_name = TEXT("\\\\.\\pipe\\Pipe");
HANDLE hPipe;
char buffer[1024];
DWORD dwRead;
hPipe = CreateNamedPipe(pipe_name,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1,
1024 * 16,
1024 * 16,
NMPWAIT_USE_DEFAULT_WAIT,
NULL);
if (hPipe == INVALID_HANDLE_VALUE)
{
std::cout << "CreateNamedPipe failed, GLE=" << GetLastError() << std::endl;
return -1;
}
// This will block if there's no client.
if (ConnectNamedPipe(hPipe, NULL) != FALSE)
{
// Read from the pipe
DWORD bytesRead = 0;
BOOL success = ReadFile(hPipe, buffer, sizeof(buffer) - 1, &bytesRead, NULL);
if (!success || bytesRead == 0)
{
std::cerr << "ReadFile failed, error: " << GetLastError() << std::endl;
}
else
{
buffer[bytesRead] = '\0'; // null terminate
std::cout << "Received: " << buffer << std::endl;
}
}
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
客户端代码
请注意,我已将管道名称用作“文件路径”,而不是更常见的,但它可以随心所欲地工作:fstream
WriteFile
#include <fstream>
#include <iostream>
#include <windows.h>
int main()
{
const TCHAR *pipe_name = TEXT("\\\\.\\pipe\\Pipe");
std::cout << "Running client. This uses fstream..." << std::endl;
while (true)
{
if (WaitNamedPipe(pipe_name, NMPWAIT_WAIT_FOREVER) != FALSE)
{
break;
}
else
{
std::cout << "Waiting for pipe server..." << std::endl;
Sleep(1000);
}
}
std::fstream fs;
fs.open(pipe_name, std::fstream::out); // Works!
if (fs.is_open())
{
fs << "Hello, Pipe Server!";
fs.close();
std::cout << "Client is done." << std::endl;
return 0;
}
else
{
std::cout << "Failed to open the pipe." << std::endl;
return 1;
}
}
Client Code, 相关应用
如下所示的替代客户端代码也有效。
#include <fstream>
#include <iostream>
#include <windows.h>
int main()
{
const TCHAR *pipe_name = TEXT("\\\\.\\pipe\\Pipe");
std::cout << "Running client. This uses WriteFile..." << std::endl;
while (true)
{
if (WaitNamedPipe(pipe_name, NMPWAIT_WAIT_FOREVER) != FALSE)
{
break;
}
else
{
std::cout << "Waiting for pipe server..." << std::endl;
Sleep(1000);
}
}
HANDLE hPipe;
DWORD dwWritten;
hPipe = CreateFile(pipe_name,
GENERIC_READ | GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
0,
NULL);
if (hPipe != INVALID_HANDLE_VALUE)
{
WriteFile(hPipe,
"Hello, Pipe Server!",
strlen("Hello, Pipe Server!") + 1,
&dwWritten,
NULL);
CloseHandle(hPipe);
std::cout << "Client is done." << std::endl;
return 0;
}
else
{
std::cout << "Failed to connect to pipe server, GLE=" << GetLastError() << std::endl;
return 1;
}
}
所有版本均在 Visual Studio Code 下按预期编译和运行,使用 .cl.exe /Zi /EHsc /Fe: <file>.exe <file>.cpp
输出
使用代码的客户端版本和“文件路径”,我获得以下输出:fstream
服务器优先
首先启动,在客户端启动之前不输出:
Received: Hello, Pipe Server!
Press any key to continue . . .
客户 下一页
接下来开始,立即输出:
Running client. This uses fstream...
Client is done.
Press any key to continue . . .
值得一提的是,颠倒执行顺序也会产生良好的结果。
客户至上
首先启动,立即输出等待消息,并在服务器启动后输出成功消息:
Running client. This uses fstream...
Waiting for pipe server...
Waiting for pipe server...
Waiting for pipe server...
Client is done.
Press any key to continue . . .
服务器 下一个
立即输出:
Received: Hello, Pipe Server!
Press any key to continue . . .
总结
问题是我想使用被视为常规文件路径的管道名称来读取数据,而不是传统的 .我能够使用这种方法将数据写入管道,但读取失败。具有讽刺意味的是(并认为将其描述为情境讽刺实际上可能是正确的),我并不真正关心数据如何写入管道 - 我很乐意使用它,但我真的很想使用管道的名称,就好像它是读取数据的常规文件路径一样。ReadFile
WriteFile
我试过了什么
我尝试了如下所示的服务器代码的变体,并结合了两个版本的客户端代码。
尝试的服务器代码
#include <iostream>
#include <fstream>
#include <string>
#include <windows.h>
int main()
{
// Client will block if not in a thread.
const TCHAR *pipe_name = TEXT("\\\\.\\pipe\\Pipe");
HANDLE hPipe;
char buffer[1024];
DWORD dwRead;
hPipe = CreateNamedPipe(pipe_name,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1,
1024 * 16,
1024 * 16,
NMPWAIT_USE_DEFAULT_WAIT,
NULL);
if (hPipe == INVALID_HANDLE_VALUE)
{
std::cout << "CreateNamedPipe failed, GLE=" << GetLastError() << std::endl;
return -1;
}
// This will block if there's no client.
if (ConnectNamedPipe(hPipe, NULL) != FALSE)
{
std::ifstream fs;
fs.open(pipe_name, std::fstream::in);
if (fs.is_open())
{
std::string line;
while (std::getline(fs, line))
{
std::cout << "Received: " << line << std::endl;
}
fs.close();
}
else
{
std::cout << "Server failed to open the pipe with ifstream." << std::endl;
}
}
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
return 0;
}
变种
主要是绝望,没有太多的思考。我尝试使用而不是只是 ,与将管道类型从 to 更改为 结合使用和不将其组合在一起,但无济于事。最后,我还尝试打开with和with。没有骰子。所有这些尝试都产生了以下输出。PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED
PIPE_ACCESS_DUPLEX
PYPE_TYPE_BYTE | PIPE_READMODE_BYTE
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE
fstream
std::fstream::in | std::fstrem::out
std::fstream::binary
新输出
在客户端,我的输出与以前相同。在服务器端,我尝试了一切,我得到了
Server failed to open the pipe with ifstream.
Press any key to continue . . .
事实上,与我想要的几乎完全相同的对应物可以写入数据,这让我希望可以使其用于读取数据,但是,当然,我完全意识到这并不能保证。
其他潜在的解决方案(以及为什么它们对我不起作用)
在 Read Named Pipe with _lread 中有人声称可以执行我正在寻找的操作,但我无法控制管道事务的执行方式,除了它将被视为常规文件以读取数据。_lopen
尽管有相反的说法(Fifo 文件 Windows 示例),但我找不到如何在 Windows 中执行 -equivalent 的真实示例(使用 istream 从命名管道读取)。mkfifo
答:
下面的服务器/客户端代码可以与我之前尝试的代码进行对比,本着 @user207421 建议的精神,它是对我有用的解决方案。
服务器
#include <iostream>
#include <fstream>
#include <string>
#include <windows.h>
int main()
{
const TCHAR *pipe_name = TEXT("\\\\.\\pipe\\Pipe");
HANDLE hPipe;
DWORD dwRead;
hPipe = CreateNamedPipe(pipe_name,
PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1,
1024,
1024,
NMPWAIT_USE_DEFAULT_WAIT,
NULL);
if (hPipe == INVALID_HANDLE_VALUE)
{
std::cout << "CreateNamedPipe failed, GLE=" << GetLastError() << std::endl;
return -1;
}
// This will block if there's no client.
if (ConnectNamedPipe(hPipe, NULL) != FALSE)
{
DWORD dwRead;
char buffer[20];
if (!ReadFile(hPipe, buffer, sizeof(buffer) - 1, &dwRead, NULL))
{
std::cout << "ReadFile failed, GLE=" << GetLastError() << std::endl;
}
else
{
buffer[dwRead] = '\0';
std::cout << "Received: " << buffer << std::endl;
}
DWORD dwWritten;
WriteFile(hPipe,
"Hello, Pipe Client!",
strlen("Hello, Pipe Client!") + 1,
&dwWritten,
NULL);
std::cout << "Server is done." << std::endl;
}
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
return 0;
}
客户
#include <fstream>
#include <iostream>
#include <string>
#include <windows.h>
int main()
{
const TCHAR *pipe_name = TEXT("\\\\.\\pipe\\Pipe");
while (true)
{
if (WaitNamedPipe(pipe_name, NMPWAIT_WAIT_FOREVER) != FALSE)
{
break;
}
else
{
std::cout << "Waiting for pipe server..." << std::endl;
Sleep(1000);
}
}
std::fstream fs;
fs.open(pipe_name, std::fstream::in);
if (fs.is_open())
{
fs << "Hello, Pipe Server!" << std::endl;
std::string line;
while (std::getline(fs, line))
{
std::cout << "Received: " << line << std::endl;
}
fs.close();
return 0;
}
else
{
std::cout << "Server failed to open the pipe with ifstream." << std::endl;
return 1;
}
}
输出
先启动服务器,再启动客户端
服务器
Received: Hello, Pipe Client!
Press any key to continue . . .
客户
Received: Hello, Pipe Server!
Server is done.
Press any key to continue . . .
先启动客户端,再启动服务器
客户
Waiting for pipe server...
Waiting for pipe server...
Received: Hello, Pipe Client!
Press any key to continue . . .
服务器
Received: Hello, Pipe Server!
Server is done.
Press any key to continue . . .
评论
server
fs.open(pipe_name, std::fstream::in);
在服务器中没有意义。你正在成为你自己的客户。服务器需要通过 执行其 I/O。hPipe
fs.is_open()