Boost::Asio peer-to-peer udp chat - c++

I'm writing peer-to-peer (it shouldn't have server - it's a task) program for exchanging text messages. It's a very tiny chat. Simply messages, nothing else. It's my 1st practice with Boost::Asio, therefore I've some questions.
My chat should be peer-to-peer as I said and it should use udp protocol. I think, the best way is to use broadcast. And the first problem: how can I learn about new connections?
Another problem is in sending message: I send it on broadcast address and then it spreads to all computers in local network. Is it right?
This code sends message and receives its back. Like an echo. Is it right?
#include <iostream>
#include <boost/asio.hpp>
#include <boost/array.hpp>
int main()
{
try
{
namespace ip = boost::asio::ip;
boost::asio::io_service io_service;
ip::udp::socket socket(io_service,
ip::udp::endpoint(ip::udp::v4(), 1555));
socket.set_option(boost::asio::socket_base::broadcast(true));
ip::udp::endpoint broadcast_endpoint(ip::address_v4::broadcast(), 1555);
boost::array<char, 4> buffer1;
socket.send_to(boost::asio::buffer(buffer1), broadcast_endpoint);
ip::udp::endpoint sender_endpoint;
boost::array<char, 4> buffer2;
std::size_t bytes_transferred =
socket.receive_from(boost::asio::buffer(buffer2), sender_endpoint);
std::cout << "got " << bytes_transferred << " bytes." << std::endl;
}
catch (std::exception &e)
{
std::cerr << e.what();
}
system("PAUSE");
return 0;
}

Tested on Ubuntu 20.04.3 LTS and Boost.Asio 1.71.
Usually this kind of task is accomplished by using multicast. Broadcast creates too much load on a network.
Basing on the sender and receiver examples while combining both of them, you should open your socket on a multicast address, which represents a "chat room" and at the same time subscribe to that multicast group to receive the messages sent from other chat participants.
#include <iostream>
#include <string>
#include <boost/asio.hpp>
constexpr std::uint16_t multicast_port = 30001;
class Peer {
public:
Peer(boost::asio::io_context& io_context,
const boost::asio::ip::address& chat_room,
const std::string& nickname)
: socket_(io_context)
, multicast_endpoint_(chat_room, multicast_port)
, nickname_(nickname)
{
boost::asio::ip::udp::endpoint listen_endpoint(chat_room, multicast_port);
socket_.open(listen_endpoint.protocol());
socket_.set_option(boost::asio::ip::udp::socket::reuse_address(true));
socket_.bind(listen_endpoint);
Note that we using reuse_address option, so you could test this example locally.
If you want to receive messages sent to multicast group, you have to subscribe to that multicast group:
socket_.set_option(boost::asio::ip::multicast::join_group(chat_room));
And as you asked if you want to learn about new connections (though UDP is a connectionless protocol), you can send multicast welcome message:
auto welcome_message = std::string(nickname_ + " connected to the chat\n");
socket_.async_send_to(boost::asio::buffer(welcome_message), multicast_endpoint_,
[this](const boost::system::error_code& error_code, std::size_t bytes_sent){
if (!error_code.failed()){
std::cout << "Entered chat room successfully" << std::endl;
}
});
So, for now we have to establish two loops: first one will expect local user's input, send it to the multicast group and then waits for another user input, while the other one will listen for incoming UDP datagrams on a socket, print datagram's content on every datagram received and then return back to socket listening:
void do_receive(){
socket_.async_receive_from(boost::asio::buffer(receiving_buffer_), remote_endpoint_,
[this](const boost::system::error_code& error_code, std::size_t bytes_received){
if (!error_code.failed() && bytes_received > 0){
auto received_message_string = std::string(receiving_buffer_.begin(), receiving_buffer_.begin() + bytes_received);
// We don't want to receive the messages we produce
if (received_message_string.find(name_) != 0){
std::cout.write(receiving_buffer_.data(), bytes_received);
std::cout << std::flush;
}
do_receive();
}
});
}
void do_send(){
std::string nickname = nickname_;
std::string message;
std::getline(std::cin, message);
std::string buffer = name.append(": " + message);
socket_.async_send_to(boost::asio::buffer(buffer, maximum_message_size_), multicast_endpoint_,
[this, message](const boost::system::error_code& /*error_code*/, std::size_t bytes_sent){
std::cout << "You: " << message << std::endl;
do_send();
});
}
There we also invoke the same IO function in each completion handler to achieve the loop effect still looking like recursion.
For now, all we have to do is to publish each of the function call in the separate threads because of io_context.run() invocation blocking, otherwise one of our loops will block another one, so we call io_context.run() in each thread:
int main(int argc, char* argv[])
{
boost::asio::thread_pool thread_pool(2);
if(argc != 3){
std::cerr << "Usage: ./peer <your_nickname> <multicast_address>" << std::endl;
std::exit(1);
}
boost::asio::io_context io_context;
boost::asio::ip::address chat_room(boost::asio::ip::make_address(argv[2]));
Peer peer(io_context, chat_room, argv[1]);
boost::asio::post(thread_pool, [&]{
peer.do_receive();
io_context.run();
});
boost::asio::post(thread_pool, [&]{
peer.do_send();
io_context.run();
});
thread_pool.join();
return 0;
}
Full source code is available here.

Related

Boost asio - is it possible to reuse a socket?

After I:
created a tcp-socket
put it in listening
received an Incoming "message" from the Client
"processed" the incoming accomplice and closed the socket...
Whether that it is possible to put this socket on listening again? Experiment shows that no - the accept() function - throws an error.
Is it possible, somehow, to "reset" a closed socket to its original state? In order not to release the previously allocated memory for this socket and not to create a new socket, which in the end will also have to be deleted.
PS:
my_socket_p = new boost::asio::ip::tcp::socket(io_context);
my_acceptor.accept(my_socket_p );
The socket you put in listening mode is typically not the socket that you close when you're done handling a request.
Asio makes this distinction more explicit than in the underlying sockets API. I discussed this before here: Design rationale behind that separate acceptor class exists in ASIO (see also e.g. What does it mean to "open" an acceptor?).
Simple example of a typical server that accepts requests and echoes them back reversed:
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;
int main() {
boost::asio::io_context ioc;
// listen on port 7878
tcp::acceptor acc(ioc, {{}, 7878});
acc.listen();
while (true) {
tcp::socket conn = acc.accept();
auto peer = conn.remote_endpoint();
try {
std::string request;
read_until(conn, asio::dynamic_buffer(request), "\n");
reverse(begin(request), end(request) - 1); // reverse until line-end
write(conn, asio::buffer("reversed: " + request));
conn.close();
} catch(boost::system::system_error const& se) {
std::cerr << "Error with peer " << peer << ": " << se.code().message() << std::endl;
}
}
}
With example clients:
netcat 127.0.0.1 7878 <<< "Hello world!"
netcat 127.0.0.1 7878 <<< "Bye world!"
Prints
reversed: !dlrow olleH
reversed: !dlrow eyB
Re-using?
There are other overloads of accept that take a socket by reference, and yes they can be re-used:
tcp::socket conn(ioc); // reused
while (true) {
acc.accept(conn);
auto peer = conn.remote_endpoint();
With the rest of the code un-changed: Live On Coliru
More Typical
More typically code would be asynchronous and the "connection handling" would be in some other class, e.g. Session. In such cases conn would be moved into Session. This is also explicitly documented as proper use, e.g.:
Following the move, the moved-from object is in the same state as if constructed using the basic_socket(const executor_type&) constructor.
So, you could write the code as such:
tcp::socket conn(ioc); // reused
while (true) {
acc.accept(conn);
std::make_shared<Session>(std::move(conn))->run();
}
You can see it Live On Coliru again, although I'll include the far more idiomatic version using the move-accept handler overload of async_accept instead here:
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;
using boost::system::error_code;
struct Session : std::enable_shared_from_this<Session> {
Session(tcp::socket s) : conn_(std::move(s)) {}
void run() { do_reverse_echo(); }
private:
void do_reverse_echo() {
async_read_until( //
conn_, asio::dynamic_buffer(buffer_), "\n",
[this, self = shared_from_this()](error_code ec, size_t) {
if (ec) {
std::cerr << "Error with peer " << peer_ << ": " << ec.message() << std::endl;
return;
}
reverse(begin(buffer_), end(buffer_) - 1); // reverse until line-end
buffer_ = "reversed: " + buffer_;
async_write(conn_, asio::buffer(buffer_), asio::detached);
});
}
tcp::socket conn_;
tcp::endpoint peer_{conn_.remote_endpoint()};
std::string buffer_;
};
struct Listener {
Listener(asio::any_io_executor ex, uint16_t port) : acc(ex, {{}, port}) {
acc.listen();
accept_loop();
}
private:
tcp::acceptor acc;
void accept_loop() {
acc.async_accept([this](error_code ec, tcp::socket conn) {
if (ec) {
std::cerr << "Stopping listener: " << ec.message() << std::endl;
return;
}
accept_loop();
std::make_shared<Session>(std::move(conn))->run();
});
}
};
int main() {
boost::asio::io_context ioc;
Listener s(ioc.get_executor(), 7878);
ioc.run();
}
Still with the same output, obviously.

How can I print the requests in boost.asio?

I'm using boost.asio to write a simple server. The code below is trying to do 2 things:
print the request
respond to the client hello
but it does not.
#include <iostream>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
int main(){
try{
boost::asio::io_context ioc;
tcp::acceptor acceptor(ioc, tcp::endpoint(tcp::v4(), 1010));
for(;;){
tcp::socket socket(ioc);
acceptor.accept(socket);
boost::system::error_code err;
std::string buff;
socket.read_some(boost::asio::buffer(buff), err);
std::cout << buff << '\n';
boost::asio::write(socket, boost::asio::buffer("hello"),err);
}
}
catch (std::exception& e){
std::cerr << e.what() << '\n';
}
return 0;
}
When I run the server and send a request using curl it does not respond and only print an empty line.
[amirreza#localhost ~]$ curl 127.0.0.1:1010
curl: (1) Received HTTP/0.9 when not allowed
[amirreza#localhost ~]$
and in server side (2 empty line):
[amirreza#localhost 2]$ sudo ./server
[sudo] password for amirreza:
here I have 2 questions:
why server doesn't print the curl request?
why curl doesn't receive the hello message?
I also observed packets sent and received between server and curl in wireshark. At first the tcp handshake will occur but when curl send the HTTP request, the server respond a tcp packet with RST flag to reset the connection.
First thing I notice:
std::string buff;
socket.read_some(boost::asio::buffer(buff), err);
This reads into an empty string: 0 bytes. Either reserve space:
buff.resize(1024);
socket.read_some(boost::asio::buffer(buff), err);
or use a dynamic buffer with a composed read operation (see next)
read_some reads whatever is available, not necessarily a line. see read_until for higher level read operations
std::string buff;
read_until(socket, boost::asio::dynamic_buffer(buff), "\n");
handle errors. In your case you could simply remove the ec variable, and rely on exceptions since you alreaady handle those
Fixes
#include <boost/asio/buffer.hpp>
#include <iostream>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
int main(){
try{
boost::asio::io_context ioc;
tcp::acceptor acceptor(ioc, tcp::endpoint(tcp::v4(), 11010));
for(;;) {
tcp::socket socket(ioc);
acceptor.accept(socket);
std::string buff;
auto bytes = read_until(socket, boost::asio::dynamic_buffer(buff), "\n");
std::cout << buff.substr(0, bytes) << std::flush;
boost::asio::write(socket, boost::asio::buffer("hello"));
}
}
catch (std::exception const& e){
std::cerr << e.what() << '\n';
}
}

understand boost::asio async_read behavior when there is nothing to read

I am trying to understand what would happen with async_read when there is nothing to read.
For example, a client creates a connection to a server, then start async_read(), but that server does not expect to send anything to this client. So what would happen? Should I receive a EOF?
Updata:
I think #user786653 is right. I made a simple example (see following).
#include <iostream>
#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/asio.hpp>
class test{
public:
test(boost::asio::io_service& io_service):_socket(io_service){
}
void handle_connect(){
std::cout<<"entering test::handle_connect"<<std::endl;
char reply[128];
boost::asio::async_read(_socket, boost::asio::buffer(reply, sizeof(reply)),
[](boost::system::error_code ec, std::size_t /*length*/){
std::cout<<"Read result:"<< ec<<" - "<<ec.message()<<std::endl;
});
}
boost::asio::ip::tcp::socket & socket(){
return _socket;
}
private:
boost::asio::ip::tcp::socket _socket;
};
int main() {
try {
boost::asio::io_service io_service;
boost::asio::ip::tcp::socket s(io_service);
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query("127.0.0.1", "8000");
boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
test t(io_service);
t.socket().async_connect(endpoint,boost::bind(&test::handle_connect, &t));
io_service.run();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
}
Quoting from the latest (1.68.0) documentation:
This function is used to asynchronously read a certain number of bytes of data from a stream. The function call always returns immediately. The asynchronous operation will continue until one of the following conditions is true:
The supplied buffers are full. That is, the bytes transferred is equal to the sum of the buffer sizes.
An error occurred.
So nothing will happen until the server closes the connection (resulting in an error).
You can test this out for yourself:
#include <iostream>
#include <boost/asio.hpp>
int main() {
try {
boost::asio::io_context io_context;
boost::asio::ip::tcp::socket s(io_context);
boost::asio::ip::tcp::resolver resolver(io_context);
boost::asio::connect(s, resolver.resolve("localhost", "8000"));
char reply[128];
async_read(s, boost::asio::buffer(reply, sizeof(reply)), [](boost::system::error_code ec, std::size_t /*length*/) {
std::cout << "Read result: " << ec << " - " << ec.message() << "\n";
});
io_context.run();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
}
Start a server that doesn't respond on localhost port 8000 (or change the code). E.g. something like nc -l 8000 or python -m SimpleHTTPServer. Then run the program and wait. Nothing happens. Now stop the server, on my (Windows) machine this results in:
Read result: system:10054 - An existing connection was forcibly closed by the remote host

Websockets with c++ asio library weird behavior

I have written a basic client-server application in C++ using asio library. The client sends messages from the console to the server.
If I run it on localhost on either linux or windows, it works great. However, when I run it on my actual server, I get a strange behavior. Each time I send a message, then immediately after another message is sent that contains garbage or is empty. This sometimes happens, sometimes doesn't. But it does most of the times. I tried using a different port.
For example if I send messages 1, 2, and 3 this is what I see in the server's console:
What could I be doing wrong ?
server.cpp - Almost same code as seen here
#define ASIO_STANDALONE
#include <iostream>
#include <asio.hpp>
using asio::ip::tcp;
const std::size_t max_length = 2048;
const unsigned short PORT = 15562;
class Session
: public std::enable_shared_from_this<Session>
{
public:
Session(tcp::socket server_socket)
: _session_socket(std::move(server_socket))
{
}
void start()
{
do_read();
}
private:
void do_read()
{
auto self(shared_from_this()); // shared_ptr instance to this
// Start an asynchronous read.
// This function is used to asynchronously read data from the stream socket.
_session_socket.async_read_some(asio::buffer(_data, max_length),
[this, self](std::error_code error, std::size_t length)
{
if (!error)
{
std::cout << "Data RECEIVED: " << std::endl;
std::cout << _data << std::endl;
do_write(length);
}
});
}
void do_write(std::size_t length)
{
auto self(shared_from_this()); // shared_ptr instance to this
// Start an asynchronous write.
// This function is used to asynchronously write data to the stream socket.
strncpy(_data, "Hi, from the server", max_length);
asio::async_write(_session_socket, asio::buffer(_data, length),
[this, self](std::error_code error, std::size_t /*length*/)
{
if (!error)
{
do_read();
}
});
}
tcp::socket _session_socket;
char _data[max_length];
};
class server
{
public:
server(asio::io_service &io_service, const tcp::endpoint &endpoint)
: _server_socket(io_service),
_server_acceptor(io_service, endpoint)
{
}
void do_accept()
{
// Start an asynchronous accept.
// This function is used to asynchronously accept a new connection into a socket.
_server_acceptor.async_accept(_server_socket,
[this](std::error_code error)
{
// Accept succeeded
if (!error)
{
// Create a session
auto session = std::make_shared<Session>(
std::move(_server_socket));
session->start();
}
// Continue to accept more connections
do_accept();
});
}
private:
tcp::acceptor _server_acceptor;
tcp::socket _server_socket;
};
int main()
{
try
{
asio::io_service io_service; // io_service provides functionality for sockets, connectors, etc
tcp::endpoint endpoint(tcp::v4(), PORT); // create an endpoint using a IP='any' and the specified PORT
server server(io_service, endpoint); // create server on PORT
server.do_accept();
std::cout << "Server started on port: " << PORT << std::endl;
io_service.run();
}
catch (std::exception &e)
{
std::cerr << "Exception: " << e.what() << "\n"; // Print error
}
return 0;
}
client.cpp - Almost same code as seen here
#define ASIO_STANDALONE
#include <iostream>
#include <asio.hpp>
using asio::ip::tcp;
int main(int argc, char *argv[])
{
asio::io_service io_service;
tcp::socket socket(io_service);
tcp::resolver resolver(io_service);
// Connect
asio::connect(socket, resolver.resolve({"localhost", "15562"}));
for (int i = 0; i < 10; ++i)
{
std::cout << "Enter message to sent to server:" << std::endl;
char client_message[2048];
std::cin.getline(client_message, 2048);
// Send message to server
asio::write(socket, asio::buffer(client_message, 2048));
char server_message[2048];
// Read message from server
asio::read(socket, asio::buffer(server_message, 2048));
std::cout << "Reply is: " << std::endl;
std::cout << server_message << std::endl;
}
return 0;
}
std::cin.getline(client_message, 2048);
Gets a line of input from the user. In this case "1". This will be politely NULL terminated, but without looking you have no idea how much data was actually provided by the user.
asio::write(socket, asio::buffer(client_message, 2048))
Writes the entire 2048 bytes of client_message into the socket. So in goes '1', a NULL, and 2046 more bytes of unknown contents. All of this will be read by the server.
How this causes at least some of the OP's deviant behaviour:
Some of that 2048 bytes of data wind up in one packet. The rest winds up in another packet. The server reads the first packet and processes it. A few milliseconds later the second packet arrives. The first packet as a 1 and null in it, so cout prints 1 and discards the rest because that's what cout does with char *. The second packet has god-knows-what in it. cout will try to interpret it the way it would any other null terminated string. It will print random garbage until it finds a null, the cows come home, or the program crashes.
This needs to be fixed. Quick hack fix:
std::cin.getline(client_message, 2048);
size_t len = strlen(client_message)
asio::write(socket, asio::buffer(client_message, len+1))
Now only the user's input string and a null will be sent. Consider using std::string and std::getline instead of the char array and iostream::getline
But because many messages may be put into the same packet by the TCP stack, you need to know when a message begins and ends. You can't count on one message one packet.
Typical solutions are
read-a-byte read-a-byte read-a-byte-byte-byte until a protpcol-defined terminator is reached. Slow and painful, but sometimes the best solution. Buffering packets in a std::stringstream while waiting for a terminator that may not have arrived yet can ease this pain.
I prefer prepending the length of the message to the message in a fixed size data type. Receiver reads for a the size of the length, then reads length bytes. Say you send an unsigned 32 bit length field. Receiver reads 32 bits to get the length, then reads length bytes for the message. When sending binary numbers over a network watch out for different endian among receivers. To avoid differing endians, make sure your protocol specifies what endian to use. Industry standard is to always send in big endian, but most processors you are likely to encounter these days are little endian. You make the call.
I'm fuzzy on the specifics of asio::buffer. You want to get the length (as a uint32_t) and the message (as a std::string) into the output stream. This might be as simple as
std::getline(cin, client_message);
uint32_t len = client_message.length();
asio::write(socket, asio::buffer(len, sizeof(len)))
asio::write(socket, asio::buffer(client_message.c_str(), len+1))
There may be a better way built into asio, and the above may be total craptastic nonsense. Please consult an asio expert on how to optimize this.
The receiver reads the message something like:
uint32_t len;
asio::read(socket, asio::buffer(len, sizeof(len)));
asio::read(socket, asio::buffer(server_message, len));
std::cout << "Reply is: " << std::endl;
std::cout << server_message << std::endl;
The asynch version should be somewhat similar.

Permission refused when connecting to domain socket created by Boost.Asio

I'm trying to create a server that receives connections via domain sockets. I can start the server and I can see the socket being created on the filesystem. But whenever I try to connect to it via socat I get the following error:
2015/03/02 14:00:10 socat[62720] E connect(3, LEN=19 AF=1 "/var/tmp/rpc.sock", 19): Connection refused
This is my Asio code (only the .cpp files). Despite the post title I'm using the Boost-free version of Asio but I don't think that would be a problem.
namespace myapp {
DomainListener::DomainListener(const string& addr) : socket{this->service}, Listener{addr} {
remove(this->address.c_str());
stream_protocol::endpoint ep(this->address);
stream_protocol::acceptor acceptor(this->service, ep);
acceptor.async_accept(this->socket, ep, bind(&DomainListener::accept_callback, this, _1));
}
DomainListener::~DomainListener() {
this->service.stop();
remove(this->address.c_str());
}
void DomainListener::accept_callback(const error_code& ec) noexcept {
this->socket.async_read_some(asio::buffer(this->data), bind(&DomainListener::read_data, this, _1, _2));
}
void DomainListener::read_data(const error_code& ec, size_t length) noexcept {
//std::cerr << "AAA" << std::endl;
//std::cerr << this->data[0] << std::endl;
//std::cerr << "BBB" << std::endl;
}
}
Listener::Listener(const string& addr) : work{asio::io_service::work(this->service)} {
this->address = addr;
}
void Listener::listen() {
this->service.run();
}
Listener::~Listener() {
}
In the code that uses these classes I call listen() whenever I want to start listening to the socket for connections.
I've managed to get this to work with libuv and changed to Asio because I thought it would make for more readable code but I'm finding the documentation to be very ambiguous.
The issue is most likely the lifetime of the acceptor.
The acceptor is an automatic variable in the DomainListener constructor. When the DomainListener constructor completes, the acceptor is destroyed, causing the acceptor to close and cancel outstanding operations, such as the async_accept operations. Cancelled operations will be provided an error code of asio::error::operation_aborted and scheduled for deferred invocation within the io_service. Hence, there may not be an active listener when attempting to connect to the domain socket. For more details on the affects of IO object destruction, see this answer.
DomainListener::DomainListener(const string&) : /* ... */
{
// ...
stream_protocol::acceptor acceptor(...);
acceptor.async_accept(..., bind(accept_callback, ...));
} // acceptor destroyed, and accept_callback likely cancelled
To resolve this, consider extending the lifetime of the acceptor by making it a data member for DomainListener. Additionally, checking the error_code provided to asynchronous operations can provide more insight into the asynchronous call chains.
Here is a complete minimal example demonstrating using domain sockets with Asio.
#include <cstdio>
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
/// #brief server demonstrates using domain sockets to accept
/// and read from a connection.
class server
{
public:
server(
boost::asio::io_service& io_service,
const std::string& file)
: io_service_(io_service),
acceptor_(io_service_,
boost::asio::local::stream_protocol::endpoint(file)),
client_(io_service_)
{
std::cout << "start accepting connection" << std::endl;
acceptor_.async_accept(client_,
boost::bind(&server::handle_accept, this,
boost::asio::placeholders::error));
}
private:
void handle_accept(const boost::system::error_code& error)
{
std::cout << "handle_accept: " << error.message() << std::endl;
if (error) return;
std::cout << "start reading" << std::endl;
client_.async_read_some(boost::asio::buffer(buffer_),
boost::bind(&server::handle_read, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read(
const boost::system::error_code& error,
std::size_t bytes_transferred)
{
std::cout << "handle_read: " << error.message() << std::endl;
if (error) return;
std::cout << "read: ";
std::cout.write(buffer_.begin(), bytes_transferred);
std::cout.flush();
}
private:
boost::asio::io_service& io_service_;
boost::asio::local::stream_protocol::acceptor acceptor_;
boost::asio::local::stream_protocol::socket client_;
std::array<char, 1024> buffer_;
};
int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cerr << "Usage: <file>\n";
return 1;
}
// Remove file on startup and exit.
std::string file(argv[1]);
struct file_remover
{
file_remover(std::string file): file_(file) { std::remove(file.c_str()); }
~file_remover() { std::remove(file_.c_str()); }
std::string file_;
} remover(file);
// Create and run the server.
boost::asio::io_service io_service;
server s(io_service, file);
io_service.run();
}
Coliru does not have socat installed, so the following commands use OpenBSD netcat to write "asio domain socket example" to the domain socket:
export SOCKFILE=$PWD/example.sock
./a.out $SOCKFILE &
sleep 1
echo "asio domain socket example" | nc -U $SOCKFILE
Which outputs:
start accepting connection
handle_accept: Success
start reading
handle_read: Success
read: asio domain socket example