Making an asynchronous Client with boost::asio - boost

i'm trying to make an asynchronous Client with boost::asio,
i use the daytime asynchronous Server(in the tutorial).
However sometimes the Client don't receive the Message, sometimes it do :O
I'm sorry if this is too much code, but i don't know what's wrong :/
Client:
#include <iostream>
#include <stdio.h>
#include <ostream>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using namespace std;
using boost::asio::ip::tcp;
class TCPClient
{
public:
TCPClient(boost::asio::io_service& IO_Service, tcp::resolver::iterator EndPointIter);
void Write();
void Close();
private:
boost::asio::io_service& m_IOService;
tcp::socket m_Socket;
boost::array<char, 128> m_Buffer;
size_t m_BufLen;
private:
void OnConnect(const boost::system::error_code& ErrorCode,
tcp::resolver::iterator EndPointIter);
void OnReceive(const boost::system::error_code& ErrorCode);
void DoClose();
};
TCPClient::TCPClient(boost::asio::io_service& IO_Service, tcp::resolver::iterator EndPointIter)
: m_IOService(IO_Service), m_Socket(IO_Service)
{
tcp::endpoint EndPoint = *EndPointIter;
m_Socket.async_connect(EndPoint,
boost::bind(&TCPClient::OnConnect, this, boost::asio::placeholders::error, ++EndPointIter));
}
void TCPClient::Close()
{
m_IOService.post(
boost::bind(&TCPClient::DoClose, this));
}
void TCPClient::OnConnect(const boost::system::error_code& ErrorCode,
tcp::resolver::iterator EndPointIter)
{
if (ErrorCode == 0)
// Successful connected
{
m_Socket.async_receive(boost::asio::buffer(m_Buffer.data(), m_BufLen),
boost::bind(&TCPClient::OnReceive, this, boost::asio::placeholders::error));
} else if (EndPointIter != tcp::resolver::iterator())
{
m_Socket.close();
tcp::endpoint EndPoint = *EndPointIter;
m_Socket.async_connect(EndPoint,
boost::bind(&TCPClient::OnConnect, this, boost::asio::placeholders::error, ++EndPointIter));
}
}
void TCPClient::OnReceive(const boost::system::error_code& ErrorCode)
{
if (ErrorCode == 0)
{
std::cout << m_Buffer.data() << std::endl;
m_Socket.async_receive(boost::asio::buffer(m_Buffer.data(), m_BufLen),
boost::bind(&TCPClient::OnReceive, this, boost::asio::placeholders::error));
} else {
DoClose();
}
}
void TCPClient::DoClose()
{
m_Socket.close();
}
int main()
{
try {
boost::asio::io_service IO_Service;
tcp::resolver Resolver(IO_Service);
tcp::resolver::query Query("127.0.0.1", "daytime");
tcp::resolver::iterator EndPointIterator = Resolver.resolve(Query);
TCPClient Client(IO_Service, EndPointIterator);
boost::thread ClientThread(
boost::bind(&boost::asio::io_service::run, &IO_Service));
std::cout << "Client started." << std::endl;
std::string Input;
while (Input != "exit")
{
std::cin >> Input;
}
Client.Close();
ClientThread.join();
} catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
Server:
http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/tutorial/tutdaytime3/src.html

Bother to init m_BufLen?

Related

How to write content to file asynchronously with boost::beast or boost::asio?

Based on websocket_client_async_ssl.cpp, I modify the function of on_read so that I can save the content into a local file.
class session : public std::enable_shared_from_this<session>
{
std::ofstream outfile_text; // outfile_text.open("test.txt", std::ofstream::out);
const int MAX_LINE_COUNT; // 10
int current_line_;
...
}
void on_read_version2( beast::error_code ec, std::size_t)
{
if(ec)
return fail(ec, "read");
else
{
++current_line_;
const std::string buf_string = beast::buffers_to_string(buffer_.data());
buffer_.consume(buffer_.size());
outfile_text.write((char*)buf_string.data(), buf_string.size());
outfile_text.write("\n", 1);
if (current_line_ > MAX_LINE_COUNT)
{
outfile_text.close();
return;
}
// Re-read a message into our buffer
ws_.async_read( buffer_, beast::bind_front_handler( &session::on_read, shared_from_this()));
}
}
void on_read_version3( beast::error_code ec, std::size_t)
{
if(ec)
return fail(ec, "read");
else
{
++current_line_;
buffer_.consume(buffer_.size());
queue_.push_back(beast::buffers_to_string(buffer_.data()));
// Are we already writing?
if (queue_.size() > 1)
return;
else
// async_write to file from queue_
if (current_line_ > MAX_LINE_COUNT)
{
outfile_text.close();
return;
}
// Re-read a message into our buffer
ws_.async_read( buffer_, beast::bind_front_handler( &session::on_read, shared_from_this()));
}
}
In version2, I used a blocking method to write the content to file. While in version 3, I list the psedo-code where I like to write this part of logic with async-method.
Question>
Does boost::asio or boost::beast support async_write file?
If not, what is the best way to write content to file within the on_read function?
Thank you
Assuming POSIX, you could use stream_descriptor:
net::posix::stream_descriptor stream_{ex_, ::creat("test.txt", 0755)};
Which models the ASIO AsyncStream concept.
On Windows, you have similar types (including stream_handle).
Demo:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>
#include <boost/beast.hpp>
#include <chrono>
#include <deque>
#include <fstream>
#include <iostream>
namespace net = boost::asio;
namespace beast = boost::beast;
namespace websocket = beast::websocket;
using net::ip::tcp;
using namespace std::chrono_literals;
static inline void fail(beast::error_code ec, std::string_view msg) {
if (ec) {
std::cerr << msg << ": " << ec.message() << std::endl;
}
}
class session : public std::enable_shared_from_this<session> {
public:
session(net::any_io_executor ex) : ex_(ex) {}
void start()
{
// assumed on logical strand
ws_.next_layer().connect({{}, 8989});
ws_.handshake("localhost", "/");
do_read();
}
private:
const int MAX_LINE_COUNT = 10;
int current_line_ = 0;
net::streambuf buffer_;
net::any_io_executor ex_;
net::posix::stream_descriptor stream_{ex_, ::creat("test.txt", 0755)};
websocket::stream<tcp::socket> ws_{ex_};
std::deque<std::string> queue_;
void do_read() {
// assumed on strand
ws_.async_read(
buffer_,
beast::bind_front_handler(&session::on_read, shared_from_this()));
}
void on_read(beast::error_code ec, std::size_t)
{
if (ec)
return fail(ec, "read");
++current_line_; // TODO fixme count `\n` in buffer?
enqueue_output(beast::buffers_to_string(buffer_.data()) + '\n');
do_read();
}
bool file_full() const {
return current_line_ > MAX_LINE_COUNT;
}
void enqueue_output(std::string msg) {
if (file_full())
return;
queue_.push_back(std::move(msg));
buffer_.consume(buffer_.size());
// Are we already writing?
if (queue_.size() == 1)
do_write_loop();
}
void do_write_loop()
{
if (queue_.empty()){
if (file_full())
stream_.close();
return;
}
// async_write to file from queue_
net::async_write(
stream_, net::buffer(queue_.front()),
[this, self = shared_from_this()](beast::error_code ec, size_t) {
if (!ec) {
queue_.pop_front();
do_write_loop();
} // TODO error handling
});
}
};
int main()
{
net::io_context io;
std::make_shared<session>(make_strand(io.get_executor())) //
->start();
io.run_for(5s);
}
And a live demo using websocat: https://imgur.com/0TyHmBj

How to start Boost echo server in a separate thread

I was using boost version boost 1_64_0 to run a Tcp server in a seprate thread
This was the code for the server.
https://www.boost.org/doc/libs/1_52_0/doc/html/boost_asio/example/echo/async_tcp_echo_server.cpp
in the Main loop i could spawn a new thread like this.
int main()
{
// Run the boost echo server as a different thread
boost::asio::io_service io_service;
server server1(io_service, 1980);
boost::thread t(boost::bind(&io_service::run, &io_service));
// when about to close the program
io_service.stop(); // stop the server
t.join();
}
Now i have changed the boost version to boost_1_73_0 and i am using this example to create a server
https://www.boost.org/doc/libs/1_71_0/doc/html/boost_asio/example/cpp11/echo/async_tcp_echo_server.cpp
how do to create a new thread ?
The existing code to create a new thread gives error.
io_service: left of :: must be a class/struct/union
run : undeclared identifier
In &io_service::run, io_service is not a type but the local variable by that name.
Starting from https://www.boost.org/doc/libs/1_74_0/doc/html/boost_asio/example/cpp11/echo/async_tcp_echo_server.cpp
io_context.run();
Needs to be like
std::thread t([&] { io_context.run(); });
// ...
t.join();
Or if you insist:
std::thread t(boost::bind(&boost::asio::io_context::run, &io_context));
I would use a thread_pool to shortcut all the complexities¹:
boost::asio::thread_pool io(1);
server s(io.get_executor(), std::atoi(argv[1]));
// ...
io.join();
A trivial edit to the class interface is required (see e.g. Boost 1.70 io_service deprecation)
Live On Coliru
//
// async_tcp_echo_server.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2020 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
using boost::asio::ip::tcp;
class session : public std::enable_shared_from_this<session> {
public:
session(tcp::socket socket) : socket_(std::move(socket)) {}
void start() { do_read(); }
private:
void do_read() {
auto self(shared_from_this());
socket_.async_read_some(
boost::asio::buffer(data_, max_length),
[this, self](boost::system::error_code ec, std::size_t length) {
if (!ec) {
do_write(length);
}
});
}
void do_write(std::size_t length) {
auto self(shared_from_this());
boost::asio::async_write(
socket_, boost::asio::buffer(data_, length),
[this, self](boost::system::error_code ec, std::size_t /*length*/) {
if (!ec) {
do_read();
}
});
}
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server {
public:
template <typename Executor>
server(Executor ex, short port)
: acceptor_(ex, tcp::endpoint(tcp::v4(), port)) {
do_accept();
}
private:
void do_accept() {
acceptor_.async_accept(
[this](boost::system::error_code ec, tcp::socket socket) {
if (!ec) {
std::make_shared<session>(std::move(socket))->start();
}
do_accept();
});
}
tcp::acceptor acceptor_;
};
int main(int argc, char* argv[]) {
try {
if (argc != 2) {
std::cerr << "Usage: async_tcp_echo_server <port>\n";
return 1;
}
boost::asio::thread_pool io(1);
server s(io.get_executor(), std::atoi(argv[1]));
// ...
io.join();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
}
}
¹ see e.g. Should the exception thrown by boost::asio::io_service::run() be caught?

Do i need boost strand

I modified echo example but I have issues understanding boost strand.
Do I need or not?
I yes please be kind and give code for.
I searched examples but I can not understand (Im just stupid..)
Thank you
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include "mingw.thread.h"
#include <boost/bind.hpp>
class session
: public std::enable_shared_from_this<session>
{
public:
session(boost::asio::ip::tcp::socket socket)
: socket_(std::move(socket))
{
}
void start()
{
input_buffer_str = "[2J[H[A\r\n*ServerV8*\r\nSCAN_BADGE:\r\n";
out_msg =input_buffer_str;
do_write(out_msg.length());
}
private:
void do_read()
{
// std::cout << "do_read" <<"" <<std::endl;
auto self(shared_from_this());
//async_read_until for Telnet client
async_read_until(socket_,input_buffer_,'\r',
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec)
{
do_write(length);
}
else
{
// std::cout<<ec <<"Disconection?"<<std::endl;
input_buffer_str.clear();
out_msg.clear();
input_buffer_.consume(length);
// Some Cleanup
}
});
}
// async_write back to Telnet client based on received
// will be result on SQL Query
void do_write(std::size_t length)
{
auto self(shared_from_this());
std::istream(&input_buffer_) >> input_buffer_str;
input_buffer_.consume(length);
std::cout << input_buffer_str <<std::endl;
if (input_buffer_str == "c") close();
if (input_buffer_str == "a") out_msg = "test_a\r\n";
if (input_buffer_str == "t")
{
Sleep(30000);
out_msg = "test_t\r\n";
}
if(input_buffer_str.length()>0 && out_msg.length()>0)
{
input_buffer_str.clear();
boost::asio::async_write(socket_, boost::asio::buffer(out_msg, out_msg.length()),
[this, self](boost::system::error_code ec, std::size_t length)
{
if (!ec) do_read();
else
{
out_msg.clear();
input_buffer_.consume(length);
// Some Cleanup
}
out_msg.clear();
});
}else do_read();
}
// close() Telnet client will be when user pushes F2 (Handheld scanner)
void close()
{
std::cout <<"Closing..."<<std::endl;
boost::system::error_code ec;
input_buffer_str.clear();
out_msg.clear();
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
Sleep(250);
socket_.close(ec);
// delete data_;
if (ec)
{
std::cout<<ec << std::endl;
// An error occurred.
}
std::cout <<"Closed..."<<std::endl;
}
boost::asio::streambuf input_buffer_;
std::string input_buffer_str;
std::string out_msg ;
boost::asio::ip::tcp::socket socket_;
//enum { max_length = 1024 };
//char data_[max_length];
//std::string ClearScreen= "[2J[H[A"; "\r\n*ServerV8*\r\nSCAN_BADGE:\r\n";
};
Copy paste from example
class server
{
public:
server(boost::asio::io_context& io_context, short port)
: acceptor_(io_context, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port))
{
do_accept();
}
private:
void do_accept()
{
acceptor_.async_accept(
[this](boost::system::error_code ec, boost::asio::ip::tcp::socket socket)
{
if (!ec)
{
std::make_shared<session>(std::move(socket))->start();
}
do_accept();
});
}
boost::asio::ip::tcp::acceptor acceptor_;
};
added Threads Some queries to sql might take depends on Network
int main(int argc, char* argv[])
{
auto thread_count = std::thread::hardware_concurrency(); // for multi core
boost::thread_group m_Threads;
boost::asio::io_context m_io_context;
server srv(m_io_context, 23);
for(unsigned int i = 0; i < thread_count; ++i)
m_Threads.create_thread( [&](){m_io_context.run();});
m_Threads.join_all();
return 0;
}

How to make a non blocking server

I want to have a non blocking server to read the incoming data in my application.
this is the workflow i want.
void main()
{
// create a socket server
CreateSocket();
while( true )
{
// keep doing the other tasks.
// if we receive a message than process it
}
}
this is the code for the Socket connection
#pragma once
//importing libraries
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
using namespace boost::asio;
using ip::tcp;
using std::cout;
using std::endl;
class con_handler : public boost::enable_shared_from_this<con_handler>
{
private:
tcp::socket sock;
std::string message = "Hello From Server";
enum { max_length = 2048};
char data[max_length];
public:
typedef boost::shared_ptr<con_handler> pointer;
con_handler(boost::asio::io_service& io_service) : sock( io_service) {}
// creating the pointer
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new con_handler(io_service));
}
// socket creation
tcp::socket& socket()
{
return sock;
}
void start()
{
sock.async_read_some(
boost::asio::buffer(data, max_length),
boost::bind(&con_handler::handle_read,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read(const boost::system::error_code& err, size_t bytes_transferred)
{
if (!err) {
cout << data << endl;
}
else {
std::cerr << "error: " << err.message() << std::endl;
sock.close();
}
}
void handle_write(const boost::system::error_code& err, size_t bytes_transferred)
{
if (!err) {
cout << "Server sent Hello message!" << endl;
}
else {
std::cerr << "error: " << err.message() << endl;
sock.close();
}
}
};
////////////////////////////////////////////////////////
#pragma once
#include "TCP_Server.h"
class Server
{
private:
tcp::acceptor acceptor_;
void start_accept()
{
// socket
con_handler::pointer connection = con_handler::create(acceptor_.get_io_service());
// asynchronous accept operation and wait for a new connection.
acceptor_.async_accept(connection->socket(),
boost::bind(&Server::handle_accept, this, connection,
boost::asio::placeholders::error));
}
public:
//constructor for accepting connection from client
Server(boost::asio::io_service& io_service) : acceptor_(io_service, tcp::endpoint(tcp::v4(), 1234))
{
start_accept();
}
void handle_accept(con_handler::pointer connection, const boost::system::error_code& err)
{
if (!err) {
connection->start();
}
start_accept();
}
};
Currently the socket connection is working but it is blocking the other process.
Do i need to have a separate thread for this workflow or i can do it in the same thread.
this is my main function
#include "pch.h"
#include <iostream>
#include "Server.h"
int main()
{
boost::asio::io_service io_service;
Server server(io_service);
io_service.run();
while (true)
{
std::cout << " function is running Running";
}
}
I am able to receive input messages but i never reach the while statement.
i would want while to keep printing and the server to receive message at the same time.

Boost asio send and receive messages

I'm trying to send and receive messages from a client and server using TCP. I'm trying it with threading, and I don't know how to do this at all. I can connect to the server just fine, but I need to be able to send and receive messages from both places on demand. I've been searching for hours and have come up empty, as all of the results on Google are overcomplicated and cluttering.
struct Client
{
boost::asio::ip::tcp::socket socket;
Client(const char* host = HOST, const char* port = PORT) : socket(io_service)
{
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::iterator endpoint = resolver.resolve(boost::asio::ip::tcp::resolver::query(HOST, PORT));
boost::asio::connect(this->socket, endpoint);
};
};
That's all I have so far for the client.
For the server:
const int PORT = 52275;
int main()
{
try
{
boost::asio::io_service io_service;
boost::asio::ip::tcp::acceptor acceptor(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), PORT));
{
boost::asio::ip::tcp::socket socket(io_service);
acceptor.accept(socket);
main();
}
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << std::endl;
}
}
Since it's apparently safe to assume you just want to send /any/ message by the simplest means possible:
Live On Coliru
#include <boost/asio.hpp>
struct Client
{
boost::asio::io_service& io_service;
boost::asio::ip::tcp::socket socket;
Client(boost::asio::io_service& svc, std::string const& host, std::string const& port)
: io_service(svc), socket(io_service)
{
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::iterator endpoint = resolver.resolve(boost::asio::ip::tcp::resolver::query(host, port));
boost::asio::connect(this->socket, endpoint);
};
void send(std::string const& message) {
socket.send(boost::asio::buffer(message));
}
};
#include <iostream>
static const int PORT = 52275;
void client_thread() {
boost::asio::io_service svc;
Client client(svc, "127.0.0.1", std::to_string(PORT));
client.send("hello world\n");
client.send("bye world\n");
}
void server_thread() {
try
{
boost::asio::io_service io_service;
boost::asio::ip::tcp::acceptor acceptor(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), PORT));
{
boost::asio::ip::tcp::socket socket(io_service);
acceptor.accept(socket);
boost::asio::streambuf sb;
boost::system::error_code ec;
while (boost::asio::read(socket, sb, ec)) {
std::cout << "received: '" << &sb << "'\n";
if (ec) {
std::cout << "status: " << ec.message() << "\n";
break;
}
}
}
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << std::endl;
}
}
#include <boost/thread.hpp>
int main() {
boost::thread_group tg;
tg.create_thread(server_thread);
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
tg.create_thread(client_thread);
tg.join_all();
}

Resources