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

时间:2018-02-16 09:00:35

标签: c++ c++11 winapi c++14

我知道之前已经问过这个问题,但我找不到答案,所以我发帖求助。

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

客户端,它只是连接到管道并接收数据并使用这些数据进行处理。

我的问题是,我希望能够发送多种类型的数据,例如float,int,strings等。如何将数据重建为正确的数据(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);
}

我的问题是,我有一堆数据我想在1 go中发送给客户端,其中可能包含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()));
    }
}

我对如何将所有数据发送到客户端并正确获取原始价值感到迷茫?

3 个答案:

答案 0 :(得分:4)

您必须使用一个库,将您的数据转换为可以通过套接字发送的内容。这叫做序列化器!您也可以使用序列化程序来序列化数据流,也可以使用GUI或其他任何方式。接收数据只需要“反序列化”。

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

http://www.boost.org/doc/libs/1_66_0/libs/serialization/doc/index.html

https://uscilab.github.io/cereal/

还有更多!

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

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;
 }   

正如您所看到的,您无需为了解“数据类型的序列化”而编写自己的内容。类/结构必须提供序列化方法,以便它们也可以拆分为原生数据类型。

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

Is it possible to send this data from my DLL to the EXE through named pipe instead of saving the file?

对于从文档中获取的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与套接字连接,例如这里: How can I create an 'ostream' from a socket?

但是,这项工作也非常简单。您只需为套接字编写自己的缓冲区类并将其连接到ofstream。我相信不超过10行代码!

因此,您现在可以将变量和对象作为xml,json或其他任何内容传输到套接字上。

编辑:从评论:是的,使用管道而不是套接字也将适应iostream,技术完全相同,并基于streambuf周围的实现。 c++ connect output stream to input stream

我希望在Windows上它可以以相同的方式工作,并且istream也可以像ostream一样适应。

更详细地了解一个完整的解决方案将不再符合我认为的Q&amp; A风格。因此,如果还有关于将某些内容与iostream相关联的问题,请开始提出新问题!

答案 1 :(得分:1)

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

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

回答您的问题&#34; //如何将所有数据放入缓冲区以发送给客户?&#34;

以下是一些代码段:

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, ...

请查看以下示例,了解一些代码提示:Named Pipe Client

答案 2 :(得分:0)

首先,我们需要了解这里的管道是什么。管道和队列是多个进程之间通信的媒介。您提交/放入队列或管道中的每条消息都是唯一的一组消息,当您阅读它时,一次只能读取一条消息。如果您将1020.45“Hellow”放入管道并从客户端读取,则会将10作为第一条消息20.45读取为第二条消息{ {1}}作为第三条消息。通常它们将是一个字符串值,您需要将其转换为适当的类型。您应该有自己的逻辑来检查值是“Hellow”还是int还是普通字符串。首先,只需阅读数据并打印出来,然后考虑如何应对它们。