由于一些不寻常的要求,我希望能够从 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;
}
}
替代客户端代码(如下所示)也可以工作。
#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;
}
}
所有版本都使用
cl.exe /Zi /EHsc /Fe: <file>.exe <file>.cpp
在 Visual Studio Code 下按预期编译和运行。
使用
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;
}
主要是绝望,没有太多思考。我尝试使用
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 中执行
mkfifo
等效操作的真实示例(使用 istream 从命名管道读取)。
下面的服务器/客户端代码可以与我之前尝试过的代码进行对比,这是对我有用的解决方案,本着@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 . . .