I've named pipe server which is written using boost asio. Server creates named pipe and calls ConnectNamedPipe passing asio overlapped ptr to it. The problem is that completion handler passed to asio overlapped is never called, that is calling CreateFile on client side won't trigger completion handler passed to ConnectNamedPipe. What am I doing wrong?
Here is full listing of both client and server:
#define _WIN32_WINNT 0x0501
#include <string>
#include <functional>
#include <thread>
#include <boost/system/error_code.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/windows/overlapped_ptr.hpp>
#include <boost/bind.hpp>
#include <tchar.h>
#include <Windows.h>
static const uint32_t PIPE_OUTPUT_BUFFER_RESERVED_SIZE_BYTES = 50 * 1024;
static const uint32_t PIPE_INPUT_BUFFER_RESERVED_SIZE_BYTES = 50 * 1024;
static const std::string PIPE_NAME = "\\\\.\\pipe\\BC33AFC8-BA51-4DCD-9507-0234785D4F55_native_server_pipe";
class Server
: public std::enable_shared_from_this<Server>
{
public:
Server(){}
~Server(){}
void Start()
{
mIoService = std::make_shared<boost::asio::io_service>();
mWork = std::make_shared<boost::asio::io_service::work>(*mIoService);
mThread = std::make_shared<std::thread>(
boost::bind(&boost::asio::io_service::run, mIoService));
mIoService->post(boost::bind(&Server::Accept, shared_from_this()));
}
void Accept()
{
mPipe = CreateNamedPipeA(
PIPE_NAME.c_str(),
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS,
PIPE_UNLIMITED_INSTANCES,
PIPE_OUTPUT_BUFFER_RESERVED_SIZE_BYTES,
PIPE_INPUT_BUFFER_RESERVED_SIZE_BYTES,
0,
nullptr);
if (mPipe == INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError();
//LOG(Error, "Failed create pipe: " << mPipeName << ", error: " << err);
return;
}
//LOG(Trace, "Pipe: " << mPipeName << " created successfully");
boost::asio::windows::overlapped_ptr overlappedPtr(*mIoService,
std::bind(&Server::OnClientConnected, this, std::placeholders::_1, std::placeholders::_2));
OVERLAPPED* overlapped = overlappedPtr.get();
BOOL ok = ConnectNamedPipe(mPipe, overlapped);
DWORD lastError = GetLastError();
if (!ok && lastError != ERROR_IO_PENDING)
{
// The operation completed immediately, so a completion notification needs
// to be posted. When complete() is called, ownership of the OVERLAPPED-
// derived object passes to the io_service.
boost::system::error_code ec(lastError,
boost::asio::error::get_system_category());
overlappedPtr.complete(ec, 0);
}
else
{
// The operation was successfully initiated, so ownership of the
// OVERLAPPED-derived object has passed to the io_service.
overlappedPtr.release();
}
}
void OnClientConnected(
const boost::system::error_code& ec,
size_t bytesTransferred)
{
int a = 0;
a++;
}
private:
HANDLE mPipe;
std::shared_ptr<boost::asio::io_service> mIoService;
std::shared_ptr<boost::asio::io_service::work> mWork;
std::shared_ptr<std::thread> mThread;
};
class Client
{
public:
void Connect()
{
HANDLE hPipe = CreateFileA(
PIPE_NAME.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
nullptr,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
nullptr
);
if (hPipe == INVALID_HANDLE_VALUE)
{
DWORD err = GetLastError();
if (err != ERROR_PIPE_BUSY)
{
/*LOG(Error, "Failed create pipe: " << mPipeName << ", error: " << err);
mOnConnected(nullptr);*/
return;
}
return;
}
}
};
std::shared_ptr<Server> s = std::make_shared<Server>();
Client c;
int CALLBACK WinMain(
_In_ HINSTANCE hInstance,
_In_ HINSTANCE hPrevInstance,
_In_ LPSTR lpCmdLine,
_In_ int nCmdShow
)
{
s->Start();
Sleep(10000);
c.Connect();
Sleep(10000);
}
Found the issue. I've not associated pipe with IOCP
itself in server part. It can be done by wrapping pipe native handle returned by CreateNamedPipeA
into boost::asio::windows::stream_handle
just before calling ConnectNamedPipe
.
typedef boost::asio::windows::stream_handle StreamHandler;
std::shared_ptr<StreamHandler> streamHandler = std::make_shared<StreamHandler>(*mIoService);
streamHandle->assign(pipe);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With