在 C++ 程序之间发送数据的基本命名管道?

问题描述 投票:0回答:3

我知道以前有人问过这个问题,但我找不到答案,所以我发帖寻求帮助。

我有一个 DLL,一旦注入进程就会创建一个命名管道。管道将等待客户端连接,并将数据发送到客户端,直到客户端断开连接。

客户端,它只会连接到管道并接收数据并利用这些数据执行操作。

我的问题是,我希望能够发送超过 1 种类型的数据,例如 float、int、string 等。如何将数据重建为正确的数据(float、int strings 等)?

这是我为客户提供的代码:

HANDLE hPipe;
DWORD dwWritten;
char Buffer[1024];

hPipe = CreateFile(TEXT("\\\\.\\pipe\\Pipe"),
    GENERIC_READ | GENERIC_WRITE,
    0,
    NULL,
    OPEN_EXISTING,
    0,
    NULL);
if (hPipe != INVALID_HANDLE_VALUE)
{
    WriteFile(hPipe,
        Buffer, //How do I put all the data into a buffer to send over to the client?
        sizeof(Buffer),   // = length of string + terminating '\0' !!!
        &dwWritten,
        NULL);


    CloseHandle(hPipe);
}

服务器:

wcout << "Creating Pipe..." << endl;

HANDLE hPipe;
char buffer[1024];
DWORD dwRead;


hPipe = CreateNamedPipe(TEXT("\\\\.\\pipe\\Pipe"),
    PIPE_ACCESS_DUPLEX,
    PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,   // FILE_FLAG_FIRST_PIPE_INSTANCE is not needed but forces CreateNamedPipe(..) to fail if the pipe already exists...
    1,
    1024 * 16,
    1024 * 16,
    NMPWAIT_USE_DEFAULT_WAIT,
    NULL);
while (hPipe != INVALID_HANDLE_VALUE)
{
    if (ConnectNamedPipe(hPipe, NULL) != FALSE)   // wait for someone to connect to the pipe
    {
        while (ReadFile(hPipe, buffer, sizeof(buffer) - 1, &dwRead, NULL) != FALSE)
        {
            /* add terminating zero */
            buffer[dwRead] = '\0';

            /* do something with data in buffer */
            printf("%s", buffer);
        }
    }

    DisconnectNamedPipe(hPipe);
}

我的问题是,我有一堆数据想一次性发送到客户端,其中可能包含 float、int、double 等。一旦我从服务器收集了所有数据,我想将其发送给客户端并让客户端通过拆分数据来解析它,如下所示:

void split(const string& s, char c,
    vector<string>& v) {
    string::size_type i = 0;
    string::size_type j = s.find(c);

    while (j != string::npos) {
        v.push_back(s.substr(i, j - i));
        i = ++j;
        j = s.find(c, j);

        if (j == string::npos)
            v.push_back(s.substr(i, s.length()));
    }
}

我有点迷失如何将所有数据发送给客户端并正确获取原始值?

c++ c++11 winapi c++14
3个回答
5
投票

您必须使用一个库将您的数据转换为可以通过套接字发送的数据。这就是所谓的序列化器!您还可以使用序列化程序来序列化为数据流,或者也可以在 GUI 或其他方式中序列化。要接收数据,您只需“反序列化”即可。

您可以找到很多序列化器库,例如:

自定义代码可以看起来像(伪代码!):

class Check
{
    int i;
    float f;

    template<SERIALIZER_TYPE>
    void Serialize( SERIALIZER_TYPE& ser )
    {
         ser & i & f;
    }
 };

 int main()
 {
     Check c;
     std::string s;

     Socket socket( ip, port );

     WriteSerializer   ser(socket);
     ser & c & s;
 }   

如您所见,您无需自己编写任何内容来“了解”数据类型如何序列化。类/结构必须提供序列化器方法,因此它们也可以拆分为其本机数据类型。

编辑:添加了评论中的问题:

是否可以通过[a]命名管道将这些数据从我的DLL发送到EXE,而不是保存文件?

对于

cereal
取自文档:

cereal 具有出色的标准库支持以及二进制、XML 和 JSON 序列化器。如果您需要其他东西,谷物被编写为可以轻松扩展以添加自定义序列化存档或类型。

因此,一种选择是根据您的需求编写新界面。

但是看一下示例代码:

void x()
{
    std::ofstream file( "out.xml" );
    cereal::XMLOutputArchive archive( file ); // depending on the archive type, data may be
                                        // output to the stream as it is serialized, or
                                        // only on destruction
    archive( some_data, more_data, data_galore );
}

如您所见,使用

std::ofstream
作为输出。所以你可以简单地使用为套接字打开的
ofstream

如何使用套接字连接 std::ostream 已得到解答,例如这里: 如何从套接字创建“ostream”?

但是手工完成这项工作也很简单。您必须为套接字编写自己的缓冲区类并将其连接到

ofstream
。我相信不超过十行代码!

因此,您现在可以通过套接字以 XML、Json 或其他形式传输变量和对象。

编辑:来自评论:是的,使用管道而不是套接字也适用于

iostream
,并且技术完全相同,并且基于围绕
streambuf
实现某些内容。 c++ 将输出流连接到输入流

我希望在 Windows 上它也能以同样的方式工作,并且

istream
也具有
ostream
的适应性。

我认为,这里不适合以问答方式详细介绍完整解决方案。因此,如果对将某些内容连接到

iostream
还有其他疑问,请开始一个新问题!


1
投票

如果您使用WriteFile API写入管道,那么您可以发送字节缓冲区。我喜欢 @Klaus 关于序列化来打包所有数据的想法,如果我需要通过管道从客户端发送对象到服务器,这将是我选择的实现。

但是,如果您只需要发送一些数据对(例如“abc 1.12345”),我认为它太过分了。我只需将它们放入带有已知分隔符的缓冲区中,然后从服务器发送到客户端,然后在客户端上解析字符串。

回答你的问题“//如何将所有数据放入缓冲区以发送给客户端?”

这是一些代码片段:

std::string strToSend;
// ... some initialization
std::wstring wstr = std::wstring(strToSend.begin(), strToSend.end());
LPTSTR pchStr = wstr.c_str();
LPCTSTR pchSend;
StringCchCopy( pchSend, 1024, pchStr );

pchSend
通话中使用
WriteFile(hPipe, pchSend, ...

另请查看以下示例以了解一些代码思路:命名管道客户端


0
投票

首先我们需要了解这里的管道是什么。 Pipe和Queue是多进程之间通信的媒介。您在队列或管道中提交/删除的每条消息作为一组消息都是唯一的,当您阅读它时,一次只会读取一条消息。如果您将

10
20.45
“Hellow”
放入管道中并从客户端读取它们,它将读取
10
作为第一条消息,
20.45
作为第二条消息,
“Hellow”
作为第三条消息。通常它们将是一个字符串值,您需要将其转换为适当的类型。您应该有自己的逻辑来检查某个值是
int
float
还是普通字符串。首先读取数据并将其全部打印出来,然后思考如何应对它们。

© www.soinside.com 2019 - 2024. All rights reserved.