Home > Software engineering >  Boost::asio::windows::stream_handle client side don't get a data
Boost::asio::windows::stream_handle client side don't get a data

Time:09-04

I try to use windows pipes with asio::windows::stream_handle but I get an ununderstood behaviour for me: client side don't catch transfered data, but server side don't launch a handler of async_write. My code is below. Client and Server are two differrent processes, their binaries are in the same directory.

Server:

    class MessageProcessor
    {
    private:
        HANDLE m_WindowsPipeHandle;
        OVERLAPPED m_OverlapWin;
        std::unique_ptr<boost::asio::windows::stream_handle> m_Pipe;
    public:
        MessageProcessor(boost::asio::io_context& asio_ctx, const std::string& pipe_name)
        {
            if (std::filesystem::exists(pipe_name))
            {
                std::filesystem::remove(pipe_name);
            }

            m_WindowsPipeHandle = CreateNamedPipe(
              pipe_name.c_str(),
              PIPE_ACCESS_OUTBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED,
              PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
              PIPE_UNLIMITED_INSTANCES,
              1,
              1,
              INFINITE,
              NULL);

           if (m_WindowsPipeHandle == INVALID_HANDLE_VALUE)
           {
               assert(false && "Pipe");
               exit(EXIT_FAILURE);
           }
           if (ConnectNamedPipe(m_WindowsPipeHandle, &m_OverlapWin) != 0)
           {
               assert(false && "Connect Pipe");
               exit(EXIT_FAILURE);
           }

           std::this_thread::sleep_for(std::chrono::seconds(1));
           m_Pipe = std::make_unique<boost::asio::windows::stream_handle>(asio_ctx, m_WindowsPipeHandle);
        }

        void AsyncWrite(const std::string& msg)
        {
            log("Launch async transfer", msg);
            boost::asio::async_write(
              *m_Pipe,
              boost::asio::buffer(msg),
              [](const boost::system::error_code& error, size_t bytes_transferred)
              {
                  if (error)
                  {
                      log("Pipe transfer error", error);
                  }
                  else
                  {
                      log("Pipe transfer ok", bytes_transferred);
                  }
              });
         }
    };

AsynWrite is called defered by asio::post via timer

Client:

class Pipe
{
private:
    HANDLE win_handle;
    std::unique_ptr<boost::asio::windows::stream_handle> pipe;
    std::string read_buf;

    std::fstream fs;

public:
    Pipe()
    {
        fs.open("tester.txt", std::ios_base::out);
        win_handle = CreateFile(
            cfg::PipeName.c_str(),
            GENERIC_READ,
            0,
            NULL,
            OPEN_EXISTING,
            FILE_FLAG_OVERLAPPED,
            NULL);

        if (win_handle == INVALID_HANDLE_VALUE)
        {
            assert(false && "Pipe Client");
            exit(EXIT_FAILURE);
        }

        pipe = std::make_unique<boost::asio::windows::stream_handle>(ctx, win_handle);
    }

    void Run()
    {
        fs << "Run launch" << std::endl;
        boost::asio::async_read(
            *pipe,
            boost::asio::buffer(read_buf),
            [this](const boost::system::error_code& error, size_t bytes)
            {
                if (error)
                {
                    fs << error << std::endl;
                }
                if (!read_buf.empty())
                {
                    fs << "Readed str: " << read_buf << std::endl;
                }
                else
                {
                    fs << "Empty buf!" << std::endl;
                }

                boost::asio::post(ctx, boost::bind(&Pipe::Run, this));
            });
    }
};

It writes 'Empty buf' always :(

May be who knows, why my server doesn't real transfer data via win pipe?

CodePudding user response:

boost::asio::buffer(read_buf)

According to the docs

Create a new modifiable buffer that represents the given string.

template<
    typename Elem,
    typename Traits,
    typename Allocator>
mutable_buffer buffer(
    std::basic_string< Elem, Traits, Allocator > & data);

Return Value mutable_buffer(data.size() ? &data[0] : 0, data.size() * sizeof(Elem)).

Remarks The buffer is invalidated by any non-const operation called on the given string object.

So you're explicitly creating a buffer with length zero. There's no space in the buffer, which is a completion condition for async_read.

You can pre-size the buffer:

std::string buf;
std::cout << boost::asio::buffer(buf).size() << std::endl;

buf.assign("Hello world!");
std::cout << boost::asio::buffer(buf).size() << std::endl;

buf.resize(1024);
std::cout << boost::asio::buffer(buf).size() << std::endl;

Prints

0
12
1024

If you want a growing buffer you need to use a DynamicBuffer model. E.g. streambuf or dynamic_string_buffer etc. A creative example:

Live On Coliru

#include <boost/asio.hpp>
#include <iostream>
using namespace std::string_view_literals;
namespace asio = boost::asio;

namespace {
    auto poem = R"(
        I went to send a missive today
        As I have done so oft before
        But I forgot to employ that scrap of advice
        "Use Signal. Use Tor."

    )"sv;

    float constexpr crypto[]{
        1.3563156e-19, 1.3563156e-19, 1.692641e 22,  1.7563698e 25,
        1.6611423e 22, 6.7120163e 22, 4.2902826e 24, 4.524393e 30,
        8.398558e-33,  1.3563156e-19, 1.3563156e-19, -2.0796537e-38,
        1.2414338e 28, 1.271254e 31,  7.270847e 31,  1.1258676e 24,
        2.028251e-19,  7.3782448e 28, 1.172262e-19,  1.3563156e-19,
        10.007843,     7.316806e 34,  1.8466762e 20, 7.316806e 34,
        1.8466762e 20, 7.316806e 34,  1.8466762e 20, 7.316806e 34,
        1.3555985e-19, 1.3563156e-19, 1.6885541e 31, 1.595227e 25,
        1.0901187e 27, 1.6885558e 31, 6.5649985e 28, 9.35016e-40};

    auto signature = R"(
                 Manish Goregaokar 2017
    )"sv;
} // namespace

template <typename DynBuf> void foo(DynBuf&& buf) {
    std::array src{
        asio::buffer(poem),
        asio::buffer(crypto),
        asio::buffer(signature),
    };
    for (auto in : src) {
        for (; auto n = asio::buffer_copy(buf.prepare(90), in); in  = n) {
            buf.commit(n);
            std::cout << "Read " << n << " bytes\n";
        }
    }
}

int main() {
    std::string s;

    foo(asio::dynamic_buffer(s));

    asio::streambuf sb;
    foo(sb);

    std::cout << s.length() << " == " << sb.in_avail() << "\n"
              << s << "\n-----\n"
              << &sb;
}

Prints

Read 90 bytes
Read 75 bytes
Read 90 bytes
Read 54 bytes
Read 45 bytes
Read 90 bytes
Read 75 bytes
Read 90 bytes
Read 54 bytes
Read 45 bytes
444 == 354

        I went to send a missive today
        As I have done so oft before
        But I forgot to employ that scrap of advice
        "Use Signal. Use Tor."

            Heed this advice, children.
        It’s not something to ignore
        Always, always, always, always
        Use Signal. Use Tor.
                 Manish Goregaokar 2017
    
-----

        I went to send a missive today
        As I have done so oft before
        But I forgot to employ that scrap of advice
        "Use Signal. Use Tor."

            Heed this advice, children.
        It’s not something to ignore
        Always, always, always, always
        Use Signal. Use Tor.
                 Manish Goregaokar 2017
    
        

Full poem here: https://manishearth.github.io/blog/2017/03/12/use-signal-use-tor/

  • Related