Writing a simple C++ protobuf streaming client/server - c++

I want to use protobuf to send messages back and forth between a client and server. In my case, I want to send an arbitrary number of protobuf messages from the server to the client. How can I build this quickly in C++?
Note: I wrote this question along with my answer after pooling a really useful Kenton Varda answer and Fulkerson answer on stackoverflow. Others have asked similar questions and hit similar roadblocks - see here, here, and here.
I'm new with protobuf and asio so feel free to correct/suggest improvements, or provide your own answer.

First, the C++ protobuf API lacks built-in support for sending multiple protobuf messages over a single stream/connection. The Java API has it, but it still hasn't been added to the C++ version. Kenton Varda (creator of protobuf v2) was nice enough to post the C++ version. So you need that code to get support for multiple messages on your single connection.
Then, you can create your client/server using boost::asio . Don't try to use the istream/ostream style interface asio provides; it is easier to wrap that and create the stream types (ZeroCopyInputStream/ZeroCopyOutputStream) required by protobuf, but it doesn't work. I don't completely understand why, but this answer by Fulkerson talks about the brittle nature of trying to do it. It also provides sample code to adapt the raw sockets into the types we need.
Putting all of this together along with a basic boost::asio tutorial, here are the client and server, followed by the supporting code. We are sending multiple instances of a simple protobuf class called persistence::MyMessage located in MyMessage.pb.h. Replace it with your own.
Client:
#include <boost/asio.hpp>
#include "ProtobufHelpers.h"
#include "AsioAdapting.h"
#include "MyMessage.pb.h"
using boost::asio::ip::tcp;
int main()
{
const char* hostname = "127.0.0.1";
const char* port = "27015";
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(hostname, port);
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::socket socket(io_service);
boost::asio::connect(socket, endpoint_iterator);
AsioInputStream<tcp::socket> ais(socket);
CopyingInputStreamAdaptor cis_adp(&ais);
for (;;)
{
persistence::MyMessage myMessage;
google::protobuf::io::readDelimitedFrom(&cis_adp, &myMessage);
}
return 0;
}
Server:
#include <boost/asio.hpp>
#include "ProtobufHelpers.h"
#include "AsioAdapting.h"
#include "MyMessage.pb.h"
using boost::asio::ip::tcp;
int main()
{
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 27015));
for (;;)
{
tcp::socket socket(io_service);
acceptor.accept(socket);
AsioOutputStream<boost::asio::ip::tcp::socket> aos(socket); // Where m_Socket is a instance of boost::asio::ip::tcp::socket
CopyingOutputStreamAdaptor cos_adp(&aos);
int i = 0;
do {
++i;
persistence::MyMessage myMessage;
myMessage.set_myString("hello world");
myMessage.set_myInt(i);
google::protobuf::io::writeDelimitedTo(metricInfo, &cos_adp);
// Now we have to flush, otherwise the write to the socket won't happen until enough bytes accumulate
cos_adp.Flush();
} while (true);
}
return 0;
}
Here are the supporting files courtesy of Kenton Varda:
ProtobufHelpers.h
#pragma once
#include <google/protobuf/io/coded_stream.h>
#include <google/protobuf/io/zero_copy_stream.h>
#include <google/protobuf/message_lite.h>
namespace google {
namespace protobuf {
namespace io {
bool writeDelimitedTo(
const google::protobuf::MessageLite& message,
google::protobuf::io::ZeroCopyOutputStream* rawOutput);
bool readDelimitedFrom(
google::protobuf::io::ZeroCopyInputStream* rawInput,
google::protobuf::MessageLite* message);
}
}
}
and
ProtobufHelpers.cpp
#include "ProtobufHelpers.h"
namespace google {
namespace protobuf {
namespace io {
bool writeDelimitedTo(
const google::protobuf::MessageLite& message,
google::protobuf::io::ZeroCopyOutputStream* rawOutput) {
// We create a new coded stream for each message. Don't worry, this is fast.
google::protobuf::io::CodedOutputStream output(rawOutput);
// Write the size.
const int size = message.ByteSize();
output.WriteVarint32(size);
uint8_t* buffer = output.GetDirectBufferForNBytesAndAdvance(size);
if (buffer != NULL) {
// Optimization: The message fits in one buffer, so use the faster
// direct-to-array serialization path.
message.SerializeWithCachedSizesToArray(buffer);
}
else {
// Slightly-slower path when the message is multiple buffers.
message.SerializeWithCachedSizes(&output);
if (output.HadError()) return false;
}
return true;
}
bool readDelimitedFrom(
google::protobuf::io::ZeroCopyInputStream* rawInput,
google::protobuf::MessageLite* message) {
// We create a new coded stream for each message. Don't worry, this is fast,
// and it makes sure the 64MB total size limit is imposed per-message rather
// than on the whole stream. (See the CodedInputStream interface for more
// info on this limit.)
google::protobuf::io::CodedInputStream input(rawInput);
// Read the size.
uint32_t size;
if (!input.ReadVarint32(&size)) return false;
// Tell the stream not to read beyond that size.
google::protobuf::io::CodedInputStream::Limit limit =
input.PushLimit(size);
// Parse the message.
if (!message->MergeFromCodedStream(&input)) return false;
if (!input.ConsumedEntireMessage()) return false;
// Release the limit.
input.PopLimit(limit);
return true;
}
}
}
}
and courtesy of Fulkerson:
AsioAdapting.h
#pragma once
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
using namespace google::protobuf::io;
template <typename SyncReadStream>
class AsioInputStream : public CopyingInputStream {
public:
AsioInputStream(SyncReadStream& sock);
int Read(void* buffer, int size);
private:
SyncReadStream& m_Socket;
};
template <typename SyncReadStream>
AsioInputStream<SyncReadStream>::AsioInputStream(SyncReadStream& sock) :
m_Socket(sock) {}
template <typename SyncReadStream>
int
AsioInputStream<SyncReadStream>::Read(void* buffer, int size)
{
std::size_t bytes_read;
boost::system::error_code ec;
bytes_read = m_Socket.read_some(boost::asio::buffer(buffer, size), ec);
if (!ec) {
return bytes_read;
}
else if (ec == boost::asio::error::eof) {
return 0;
}
else {
return -1;
}
}
template <typename SyncWriteStream>
class AsioOutputStream : public CopyingOutputStream {
public:
AsioOutputStream(SyncWriteStream& sock);
bool Write(const void* buffer, int size);
private:
SyncWriteStream& m_Socket;
};
template <typename SyncWriteStream>
AsioOutputStream<SyncWriteStream>::AsioOutputStream(SyncWriteStream& sock) :
m_Socket(sock) {}
template <typename SyncWriteStream>
bool
AsioOutputStream<SyncWriteStream>::Write(const void* buffer, int size)
{
boost::system::error_code ec;
m_Socket.write_some(boost::asio::buffer(buffer, size), ec);
return !ec;
}

I'd recommend using gRPC. It supports "streaming" requests in which the client and server can send multiple messages in either direction over time as part of a single logical request, which should suit your needs. With gRPC a lot of the nitty-gritty setup is taken care of for you, you have extensive documentation and tutorials to follow, TLS encryption is built-in, you have cross-language support, you can easily add new kinds of requests and parallel streams, etc.

Related

How to access the contents of the boost::asio::streambuf in the async_write() handler

I need to print the contents of the boost::asio::streambuf in the async_write() handler into the log after it was sent with the same async_write(). But although streambuf::size() returns 95 before async_write(), it will return 0 in the async_write() handler, while containing the exact sent data. That makes logging impossible, as we don't know the buffer size (how many symbols to log).
As far as I understand, the problem is that after async_write() has been executed, the internal pointers of the streambuf are changed because of the "write" operation, and the data in the buffer is "invalidated" after been sent. That's why, despite the fact that streambuf::size() returns 95 before async_write(), it will return 0 in the async_write() handler.
I also noticed that the buffer still contains the needed content in the async_write() handler. One could suggest saving the size of the buffer before sending and reuse it in the handler. Still, I assume I cannot rely on the fact that it will always be available in the handler, as far as the streambuf implementation may delete the content if it thinks it would be necessary. Intuitively such approach feels unreliable.
Are there any workarounds to safely print the buffer content into the log in async_write() handler?
// Message class.
struct MyTcpMsg {
...
public:
// Buffer "getter".
boost::asio::streambuf& buffer();
private:
// Buffer that keeps the data that is going to be sent over the network.
boost::asio::streambuf m_buffer;
};
// Message queue.
std::deque<std::unique_ptr<MyTcpMsg>> messagesOut;
//.. fill messagesOut queue with messages... (code omitted)
// Code that executes sending the message.
// Attempting to log the sent data in the handler.
auto& msgStream = m_messagesOut.front()->buffer();
// msgStream.size() is 95 here.
boost::asio::async_write(
getSocket(), msgStream,
[this](boost::system::error_code ec, std::size_t length) {
if (ec == 0) {
auto& msgStreamOut = m_messagesOut.front()->buffer();;
// Log important info. But the buffer size
// in (msgStreamOut.size()) is 0 now, which makes logging impossible,
// although the data is still present in the buffer.
printBufferToLog(msgStreamOut, msgStreamOut.size());
}
});
Thanks in advance
Yeah. You correctly understood the way DynamicBuffer operates. If you don't want that, use a non-dynamic buffer or sequence of buffers.
The good news is that you can get a buffer sequence instance from the streambuf in no effort at all:
auto& sb = m_messagesOut.front()->buffer();
asio::const_buffers_1 buf = sb.data();
So you can update your code:
void stub_send_loop() {
auto& sb = m_messagesOut.front()->buffer();
asio::const_buffers_1 buf = sb.data();
async_write(getSocket(), buf, [=, &sb](error_code ec, size_t length) {
if (!ec) {
// Log important info
(std::cout << "Wrote : ").write(buffer_cast<char const*>(buf), length) << std::endl;
// update stream
sb.consume(length);
}
});
}
Side-note: The exact type of buf is a bit of an implementation detail. I recommend depending on it indirectly to make sure that the implementation of the streambufs buffer sequence is guaranteed to be a single buffer. async_write doesn't care, but your logging code might (as shown). See also is it safe to use boost::asio::streambuf as both an istream and an array as string_view?
Live On Coliru
#include <boost/asio.hpp>
#include <deque>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;
using boost::system::error_code;
struct MyTcpMsg {
asio::streambuf& buffer() { return m_buffer; }
template <typename... T> MyTcpMsg(T const&... args) {
(std::ostream(&m_buffer) << ... << args);
}
private:
asio::streambuf m_buffer;
};
struct X {
asio::io_context io;
tcp::socket sock_{io};
std::deque<std::unique_ptr<MyTcpMsg>> m_messagesOut;
X() {
m_messagesOut.push_back(std::make_unique<MyTcpMsg>("Hello world: ", 21 * 2, "."));
m_messagesOut.push_back(std::make_unique<MyTcpMsg>("Bye"));
};
tcp::socket& getSocket() {
if (!sock_.is_open())
sock_.connect({{}, 7878});
return sock_;
}
void stub_send_loop() {
auto& sb = m_messagesOut.front()->buffer();
asio::const_buffers_1 buf = sb.data();
async_write(getSocket(), buf, [=, &sb](error_code ec, size_t length) {
if (!ec) {
// Log important info
(std::cout << "Wrote : ").write(buffer_cast<char const*>(buf), length) << std::endl;
// update stream
sb.consume(length);
}
});
}
};
int main() {
X x;
x.stub_send_loop();
}
Local demo:
Side Note
I think you might want to rethink your design a little. Likely, the use of streambuf is a pessimization. You could "just" return a buffer sequence, which may allow you to avoid allocation. Also, the fact that you expose it by mutable reference (via a quasi-class "getter") breaks encapsulation.

Simple Boost::Asio asynchronous UDP echo server

I'm currently making my way through a book on C++ called "C++ Crash Course". The chapter on networking shows how to use Boost::Asio to write a simple uppercasing TCP server (synchronously or asynchronously). One of the excersises is to recreate it with UDP, which is what I'm having trouble with. Here's my implementation:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/algorithm/string/case_conv.hpp>
using namespace boost::asio;
struct UdpServer {
explicit UdpServer(ip::udp::socket socket)
: socket_(std::move(socket)) {
read();
}
private:
void read() {
socket_.async_receive_from(dynamic_buffer(message_),
remote_endpoint_,
[this](boost::system::error_code ec, std::size_t length) {
if (ec || this->message_ == "\n") return;
boost::algorithm::to_upper(message_);
this->write();
}
);
}
void write() {
socket_.async_send_to(buffer(message_),
remote_endpoint_,
[this](boost::system::error_code ec, std::size_t length) {
if (ec) return;
this->message_.clear();
this->read();
}
);
}
ip::udp::socket socket_;
ip::udp::endpoint remote_endpoint_;
std::string message_;
};
int main() {
try {
io_context io_context;
ip::udp::socket socket(io_context, ip::udp::v4(), 1895);
UdpServer server(std::move(socket));
io_context.run();
} catch (std::exception & e) {
std::cerr << e.what() << std::endl;
}
}
(Note: The original example uses enable_shared_from_this to capture this by shared_ptr into the lambdas, but I deliberately omitted it to see what would happen without it.)
My code does not compile, and I feel it will take me a thousand years to fully parse the error message (posted on pastebin.com since it's enormous).
It seems the issue is that the buffers are being used/constructed the wrong way, but I have no idea what exactly is wrong with this code. The few answers here on SO concerning Asio either use TCP or tackle an entirely different problem, so the mistake I made has to be really basic. I didn't find anything relevant in the Asio docs.
To be fair, Asio seems way too complicated to my newbie self. Maybe I just don't have the qualifications to use it right now. Nonetheless, I would still like to get the exercise done and move on. Any help would be appreciated.
Templates have the ugliest of compiler error messages. You often just have to go through the compiler error output and look for the first reference in your own source file. Ala:
/home/atmaks/Code/CCC_chapter20/main.cpp:53:9: required from here
In any case, on Visual Studio, the error was a bit more clear. (Not really, it just identified the offending line better).
Stare at it and contemplate all your life's decisions that led you to want to be developing in C++ in the first place. :)
I can't for the life of me figure out how to get dynamic_buffer to work. It may simply be the case that async_read doesn't like this type. And I think that actually makes sense for UDP. The receive buffer has to be sized before the recvfrom call in a synchronous mode. And I suspect async UDP, especially for Windows, the buffer has to be passed down to the kernel to be filled up. By then it's too late to be sized.
Asio lacks proper documentation and leaves us with cryptic template types to figure out. And the only Asio documentation that is worthwhile are the decent examples - none of which reference dynamic_buffer.
So let's change to a fixed sized buffer for receiving.
While we're at it, it didn't like your socket constructor and threw an exception. So I fixed it up such that it will work.
#include <iostream>
#include <boost/asio.hpp>
#include <boost/algorithm/string/case_conv.hpp>
using namespace boost::asio;
struct UdpServer {
explicit UdpServer(ip::udp::socket socket)
: socket_(std::move(socket)) {
read();
}
private:
void read() {
socket_.async_receive_from(buffer(data_, 1500),
remote_endpoint_,
[this](boost::system::error_code ec, std::size_t length) {
if (ec)
{
return;
}
data_[length] = '\0';
if (strcmp(data_, "\n") == 0)
{
return;
}
boost::algorithm::to_upper(data_);
this->write();
}
);
}
void write() {
socket_.async_send_to(buffer(data_, strlen(data_)),
remote_endpoint_,
[this](boost::system::error_code ec, std::size_t length) {
if (ec) return;
data_[0] = '\0';
this->read();
}
);
}
ip::udp::socket socket_;
ip::udp::endpoint remote_endpoint_;
char data_[1500 + 1]; // +1 for we can always null terminate safely
};
int main() {
try {
io_context io_context;
ip::udp::endpoint ep(ip::udp::v6(), 1895); // also listens on ipv4
ip::udp::socket sock(io_context, ep);
UdpServer server(std::move(sock));
io_context.run();
}
catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
}
Update
I did get dynamic_buffer to work, but it still requires a pre-allocation to be made.
Update the the start of the read() function as follows:
void read() {
auto db = dynamic_buffer(message_);
auto b = db.prepare(1500);
socket_.async_receive_from(b,
...
That at least lets you stick with std::string instead of using a flat C array.
And now for evidence that it's working:

C++ Boost ASIO async_send_to memory leak

I am currently working on a UDP socket client. I am currently noticing a memory leak and I've tried several things in hopes to squash it, but it still prevails. In my main, I have a char* that has been malloc'd. I then call the below function to send the data:
void Send(const char* data, const int size) {
Socket.async_send_to(boost::asio::buffer(data, size), Endpoint, boost::bind(&MulticastSender::HandleSendTo, this, boost::asio::placeholders::error));
}
If I run this code, it will always leak memory. However, if I comment out the async_send_to call, the memory stays consistent.
I have tried several variations(see below) on this, but they all only appear to speed up the memory leak.
A couple notes, there is a chance that the char* that gets passed to Send may get free'd before the call completes. However, in my variations, I have taken precaution to do handle that.
Variation 1:
void Send(const char* data, const int size) {
char* buf = (char*)malloc(size);
memcpy(buf, data, size);
Socket.async_send_to(boost::asio::buffer(buf, size), Endpoint, boost::bind(&MulticastSender::HandleSendTo, this, boost::asio::placeholders::error, buf));
}
void HandleSendTo(const boost::system::error_code& ec, const char* buf) {
free(buf);
}
Variation 2:
class MulticastSender {
char* Buffer;
public:
void Send(const char* data, const int size) {
Buffer = (char*)malloc(size);
memcpy(Buffer, data, size);
Socket.async_send_to(boost::asio::buffer(Buffer, size), Endpoint, boost::bind(&MulticastSender::HandleSendTo, this, boost::asio::placeholders::error));
}
void HandleSendTo(const boost::system::error_code& ec) {
free(Buffer);
}
}
However, both variations seem to only speed up the memory leak. I have also tried removing the async_send_to and just calling boost::asio::buffer(data, size), but as has been explained in other questions, the buffer does not own the memory and thus it is up to the user to safely manage it. Any thoughts on what could be causing this issue and how to resolve it?
EDIT 1:
As suggested in the comments, I have preallocated a single buffer (for test purposes) and I am never deallocating it, however, the memory leak still persists.
class MulticastSender {
char* Buffer;
const int MaxSize = 16384;
public:
MulticastSender() {
Buffer = (char*)malloc(MaxSize);
}
void Send(const char* data, const int size) {
memcpy(Buffer, data, size);
Socket.async_send_to(boost::asio::buffer(Buffer, size), Endpoint, boost::bind(&MulticastSender::HandleSendTo, this, boost::asio::placeholders::error));
}
void HandleSendTo(const boost::system::error_code& ec) {
}
}
EDIT 2:
As requested here is an MCVE of the problem. In making this I have also observed an interesting behavior that I will explain below.
#include <string>
#include <iostream>
#include <functional>
#include <thread>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
class MulticastSender {
private:
boost::asio::io_service IOService;
const unsigned short Port;
const boost::asio::ip::address Address;
boost::asio::ip::udp::endpoint Endpoint;
boost::asio::ip::udp::socket Socket;
boost::asio::streambuf Buffer;
void HandleSendTo(const boost::system::error_code& ec) {
if(ec) {
std::cerr << "Error writing data to socket: " << ec.message() << '\n';
}
}
void Run() {
IOService.run();
}
public:
MulticastSender(const std::string& address,
const std::string& multicastaddress,
const unsigned short port) : Address(boost::asio::ip::address::from_string(address)),
Port(port),
Endpoint(Address, port),
Socket(IOService, Endpoint.protocol()) {
std::thread runthread(&MulticastSender::Run, this);
runthread.detach();
}
void Send(const char* data, const int size) {
std::ostreambuf_iterator<char> out(&Buffer);
std::copy(data, data + size, out);
Socket.async_send_to(Buffer.data(), Endpoint, boost::bind(&MulticastSender::HandleSendTo, this, boost::asio::placeholders::error));
}
};
const int SIZE = 8192;
int main() {
MulticastSender sender("127.0.0.1", "239.255.0.0", 30000);
while(true) {
char* data = (char*)malloc(SIZE);
std::memset(data, 0, SIZE);
sender.Send(data, SIZE);
usleep(250);
free(data);
}
}
The above code still produces a memory leak. I should mention that I am running this on CentOS 6.6 with kernel Linux dev 2.6.32-504.el6.x86_64 and running Boost 1.55.0. I am observing this simply by watching the process in top.
However, if I simply move the creation of the MulticastSender into the while loop, I no longer observe the memory leak. I am concerned about the speed of the application though, so this is not a valid option.
Memory is not leaking, as there is still a handle to the allocated memory. However, there will be continual growth because:
The io_service is not running because run() is returning as there is no work. This results in completion handlers being allocated, queued into the io_service, but neither executed nor freed. Additionally, any cleanup that is expected to occur within the completion handler is not occurring. It is worth noting that during the destruction of the io_service, completion handlers will be destroyed and not invoked; hence, one cannot depend on only performing cleanup within the execution of the completion handler. For more details as to when io_service::run() blocks or unblocks, consider reading this question.
The streambuf's input sequence is never being consumed. Each iteration in the main loop will append to the streambuf, which will then send the prior message content and the newly appended data. See this answer for more details on the overall usage of streambuf.
A few other points:
The program fails to meet a requirement of async_send_to(), where ownership of the underlying buffer memory is retained by the caller, who must guarantee that it remains valid until the handler is called. In this case, when copying into the streambuf via the ostreambuf_iterator, the streambuf's input sequence is modified and invalidates the buffer returned from streambuf.data().
During shutdown, some form of synchronization will need to occur against threads that are running the io_service. Otherwise, undefined behavior may be invoked.
To resolve these issues, consider:
Using boost::asio::io_service::work to ensure that the io_service object's run() does not exit when there is no work remaining.
Passing ownership of the memory to the completion handler via std::shared_ptr or another class that will manage the memory via resource acquisition is initialization (RAII) idiom. This will allow for proper cleanup and meet the requirement's of the buffer validity for async_send_to().
Not detaching and joining upon the worker thread.
Here is a complete example based on the original that demonstrates these changes:
#include <string>
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
class multicast_sender
{
public:
multicast_sender(
const std::string& address,
const std::string& multicast_address,
const unsigned short multicast_port)
: work_(io_service_),
multicast_endpoint_(
boost::asio::ip::address::from_string(multicast_address),
multicast_port),
socket_(io_service_, boost::asio::ip::udp::endpoint(
boost::asio::ip::address::from_string(address),
0 /* any port */))
{
// Start running the io_service. The work_ object will keep
// io_service::run() from returning even if there is no real work
// queued into the io_service.
auto self = this;
work_thread_ = std::thread([self]()
{
self->io_service_.run();
});
}
~multicast_sender()
{
// Explicitly stop the io_service. Queued handlers will not be ran.
io_service_.stop();
// Synchronize with the work thread.
work_thread_.join();
}
void send(const char* data, const int size)
{
// Caller may delete before the async operation finishes, so copy the
// buffer and associate it to the completion handler's lifetime. Note
// that the completion may not run in the event the io_servie is
// destroyed, but the the completion handler will be, so managing via
// a RAII object (std::shared_ptr) is ideal.
auto buffer = std::make_shared<std::string>(data, size);
socket_.async_send_to(boost::asio::buffer(*buffer), multicast_endpoint_,
[buffer](
const boost::system::error_code& error,
std::size_t bytes_transferred)
{
std::cout << "Wrote " << bytes_transferred << " bytes with " <<
error.message() << std::endl;
});
}
private:
boost::asio::io_service io_service_;
boost::asio::io_service::work work_;
boost::asio::ip::udp::endpoint multicast_endpoint_;
boost::asio::ip::udp::socket socket_;
std::thread work_thread_;
};
const int SIZE = 8192;
int main()
{
multicast_sender sender("127.0.0.1", "239.255.0.0", 30000);
char* data = (char*) malloc(SIZE);
std::memset(data, 0, SIZE);
sender.send(data, SIZE);
free(data);
// Give some time to allow for the async operation to complete
// before shutting down the io_service.
std::this_thread::sleep_for(std::chrono::seconds(2));
}
Output:
Wrote 8192 bytes with Success
The class variation looks better, and you can use boost::asio::streambuf as a buffer for network io (it doesn't leak and doesn't need much maintenance).
// The send function
void
send(char const* data, size_t size)
{
std::ostreambuf_iterator<char> out(&buffer_);
std::copy(data, data + size, out);
socket.async_send_to(buffer_, endpoint,
std::bind( &multicast_sender,
this, std::placeholders::_1 ));
}
Moving the socket and endpoint inside the class would be a good idea. Also you should bear in mind that the async operation can finish when your object goes out of scope. I would recommend using enable_shared_from_this (boost or std flavours) and pass shared_from_this() instead of this to the bind function.
The whole solution would look like this:
#include <boost/asio.hpp>
class multicast_sender :
public std::enable_shared_from_this<multicast_sender> {
using boost::asio::ip::udp;
udp::socket socket_;
udp::endpoint endpoint_;
boost::asio::streambuf buffer_;
public:
multicast_sender(boost::asio::io_service& io_service, short port,
udp::endpoint const& remote) :
socket_(io_service, udp::endpoint(udp::v4(), port)),
endpoint_(remote)
{
}
void
send(char const* data, size_t size)
{
std::ostreambuf_iterator<char> out(&buffer_);
std::copy(data, data + size, out);
socket_.async_send_to(buffer_, endpoint_,
std::bind( &multicast_sender,
shared_from_this(), std::placeholders::_1 ));
}
void
handle_send(boost::system::error_code const& ec)
{
}
};
EDIT
And as far as you don't have to do anything in the write handler, you can use a lambda (requires C++11) as a completion callback
// The send function
void
send(char const* data, size_t size)
{
std::ostreambuf_iterator<char> out(&buffer_);
std::copy(data, data + size, out);
socket.async_send_to(buffer_, endpoint,
[](boost::system::error_code const& ec){
std::cerr << "Error sending :" << ec.message() << "\n";
});
}

How can I make sure all async_handler is finished before stop in boost::asio?

My class Reciever... It recieve some string and push it to buffer.
Reciever::Reciever(boost::shared_ptr<TSBuffer<std::string>> buffer, int port)
: port(port), buffer(buffer)
{
using namespace boost::asio;
acceptor_ = new ip::tcp::acceptor(iosev, ip::tcp::endpoint(ip::tcp::v4(), port));
}
Reciever::~Reciever()
{
delete acceptor_;
}
void Reciever::run()
{
using namespace boost::asio;
_start();
iosev.run();
}
void Reciever::stop()
{
Sender s("127.0.0.1", std::string(8, '$'), port);
}
void Reciever::_start()
{
using namespace boost::asio;
boost::shared_ptr<ip::tcp::socket> socket(new ip::tcp::socket(iosev));
acceptor_->async_accept(*socket, boost::bind(&Reciever::acceptHanlder, this, socket));
}
void Reciever::acceptHanlder(boost::shared_ptr<boost::asio::ip::tcp::socket> socket)
{
std::string delim(8, '$');
boost::system::error_code ec;
boost::asio::streambuf strmbuf;
boost::asio::read_until(*socket, strmbuf, delim, ec);
std::istream is(&strmbuf);
std::string re((std::istreambuf_iterator<char>(is)),std::istreambuf_iterator<char>());
re.replace(re.end() - delim.size(), re.end(), "");
if (re.size() && re != std::string(8, '$')){
buffer->push(re);
_start();
}
In main() function I use X(uncertain) threads send X strings to the Reciever. I want to stop the reciever(call the Reciever::stop) when Reciever::acceptHanlder is called X times(which means X strings is handled).
But I don't know how to make sure that?
I see there is not possible calling async_accept before last line of acceptHanlder. So, you can simply implement a counter inside Reciever object and just dont call _start when job is done. When same Reciever is used in different threads, its far more complicated, i prefer to use "object for connection" paradigm.
Also you can use asio::strand to restrict acceptHanlder-s so only one acceptHanlder of given Reciever can be active at any time. This eliminates concurrency issues within Reciever. So you can safely call Reciever::stop (using same strand).

Is there a boost::iostreams (bidirectional) Device for a blocking boost::asio TCP connection?

I'm surveying c++ libraries for portable, blocking I/O access to the filesystem and network. It looks like boost::filesystem, boost::iostreams and boost::asio will, between the three of them, do the job.
To be clear, I'm not currently interested in the asynchronous aspects of boost::asio; I just want a portable, blocking interface to the network.
Digging in, I see boost::iostreams has a notion of Devices, each of which has an associated mode concept. The bidirectional mode specifically seems hand-tailored for streaming access to a full-duplex TCP connection. Awesome.
boost::iostreams does not seem to offer support for actually opening TCP connections (unlike the local filesystem.) That's fine, surely boost::asio will let me open the connection, appropriately model it as a bidirectional Device, and wrap it in a boost::iostreams::stream.
..except it won't? I see boost::asio::ip::tcp::iostream, which would replace the boost::iostreams::stream, but presumably not act as a Device.
I understand the tcp::iostream would act similarly, but I would still prefer to learn and code against just one interface, not two. Specifically, dealing with two error handling regimes & exception hierarchies is not very palatable.
So, the question: am I blind? Maybe an adapter between the two libraries exists, that I missed googling around. Or perhaps someone has already released such an adapter as a 3rd-party component I could drop in?
I'm not aware of a direct mapping. However, if you were interested, writing such a device is fairly straightforward. This version throws boost::system::system_error for non-EOF errors, but you could choose to do something else.
#include <iosfwd>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/iostreams/categories.hpp>
#include <boost/system/system_error.hpp>
class asio_stream_device
{
public:
typedef char char_type;
typedef boost::iostreams::bidirectional_device_tag category;
explicit asio_stream_device(boost::asio::ip::tcp::socket& sock) : socket_(sock)
{
}
std::streamsize read(char* s, std::streamsize n)
{
// Read up to n characters from the underlying data source
// into the buffer s, returning the number of characters
// read; return -1 to indicate EOF
boost::system::error_code ec;
std::size_t rval = socket_.read_some(boost::asio::buffer(s, n), ec);
if (!ec)
{
return rval;
}
else if (ec == boost::asio::error::eof)
{
return -1;
}
else
{
throw boost::system::system_error(ec,"read_some");
}
}
std::streamsize write(const char* s, std::streamsize n)
{
// Write up to n characters to the underlying
// data sink into the buffer s, returning the
// number of characters written
boost::system::error_code ec;
std::size_t rval = socket_.write_some(boost::asio::buffer(s, n), ec);
if (!ec)
{
return rval;
}
else if (ec == boost::asio::error::eof)
{
return -1;
}
else
{
throw boost::system::system_error(ec,"write_some");
}
}
private:
boost::asio::ip::tcp::socket& socket_;
};
Basically, open/connect the socket as normal, then pass it to the constructor. The example simply reads and outputs to the screen.
void test
{
namespace asio = boost::asio;
namespace io = boost::iostreams;
asio::io_service service;
asio::ip::tcp::socket socket(service);
asio::ip::tcp::endpoint remote - ...; ////
socket.connect(remote);
io::stream<asio_stream_device> str(socket);
std::string line;
while (std::getline(str, line)) {
std::cout << line << std::endl;
}
}