I created the main cpp file and three classes to create an asynchronous server.
Server, Service, and Acceptor respectively.
However, they caused errors in the build process, even though there were no errors in the visual studio 2019 environment.
I tried to fix the error, but most of the errors occurred in other files, so I couldn't even think of it myself.
main
#include "Server.h"
#include <iostream>
#include <boost/asio.hpp>
#include <thread>
#include <atomic>
#include <memory>
#define DEFAULT_THREAD_SIZE 2;
using namespace boost;
int main()
{
unsigned short port_num;
std::cin >> port_num;
try {
Server srv;
unsigned int threads = std::thread::hardware_concurrency() * 2;
if (threads == 0) {
threads = DEFAULT_THREAD_SIZE;
}
std::cout << "\nPort - " << port_num << "\nServer start\n";
srv.Start(port_num, threads);
while (1) {
std::cin >> srv;
}
}
catch (system::system_error& e) {
std::cout << "\nError code: " << e.code() << "\nError Message\n" << e.what();
}
return 0;
}
This includes Server.h, which defines Server class.
#include "Acceptor.h"
#include <boost/asio.hpp>
#include <thread>
#include <atomic>
#include <memory>
using namespace boost;
class Server
{
public:
Server();
void Start(unsigned short port_num, unsigned int threads);
void Stop();
int Command(std::string& str);
private:
asio::io_service mios;
std::unique_ptr<asio::io_service::work> mWork;
std::unique_ptr<Acceptor> mAcceptor;
std::vector <std::unique_ptr<std::thread>> mThreads;
};
std::istream& operator>>(std::istream& is, Server& srv);
Here's the implementation, Server.cpp.
#include "Server.h"
Server::Server() {
mWork.reset(new asio::io_service::work(mios));
}
void Server::Start(unsigned short port_num, unsigned int threads) {
assert(thread > 0);
mAcceptor.reset(new Acceptor(mios, port_num));
mAcceptor->Start();
for (int i = 0; i < threads; i++) {
std::unique_ptr<std::thread> th(new std::thread([this]() {mios.run(); }));
mThreads.push_back(std::move(th));
}
}
void Server::Stop() {
mAcceptor->Stop();
mios.stop();
for (auto& th : mThreads) {
th->join();
}
}
int Server::Command(std::string& str) {
return 0;
}
std::istream& operator>>(std::istream& is, Server& srv) {
std::string str;
is >> str;
srv.Command(str);
return is;
}
This is Acceptor class.
#include "Service.h"
#include <boost/asio.hpp>
#include <thread>
#include <atomic>
#include <memory>
using namespace boost;
class Acceptor
{
public:
Acceptor(asio::io_service& ios, unsigned short port_num);
void Start();
void Stop();
private:
std::shared_ptr<asio::io_service> mios;
std::shared_ptr<asio::ip::tcp::acceptor> mAcceptor;
std::atomic<bool> mIsStopped;
void InitAccept();
void OnAccept(const system::error_code ec, std::shared_ptr<asio::ip::tcp::socket> sock);
};
#include "Acceptor.h"
Acceptor::Acceptor(asio::io_service& ios, unsigned short port_num) {
mios = std::make_shared<asio::io_service>(ios);
mAcceptor = std::make_shared<asio::ip::tcp::acceptor>(mios, asio::ip::tcp::endpoint(asio::ip::address_v4::any(), port_num));
mIsStopped = false;
}
void Acceptor::Start() {
mAcceptor->listen();
InitAccept();
}
void Acceptor::Stop() {
mIsStopped.store(true);
}
void Acceptor::InitAccept() {
std::shared_ptr<asio::ip::tcp::socket> sock(new asio::ip::tcp::socket(mios));
mAcceptor->async_accept(*sock, [this, sock](const system::error_code& error) {OnAccept(error, sock);});
}
void Acceptor::OnAccept(const system::error_code ec, std::shared_ptr<asio::ip::tcp::socket> sock) {
if (ec.value() == 0 || ER) {
(new Service(sock))->StartHandling();
}
else{
std::cout << "Error code:" << ec.value() << "error " << "Error message: " << ec.message() << "\n";
}
if (!mIsStopped.load()) {
InitAccept();
}
else {
mAcceptor->close();
}
}
Service class
#define ER true
#include <iostream>
#include <boost/asio.hpp>
#include <thread>
#include <atomic>
#include <memory>
using namespace boost;
class Service
{
public:
Service(std::shared_ptr<asio::ip::tcp::socket> sock);
void StartHandling();
private:
void OnRequestReceived(const boost::system::error_code& ec, std::size_t bytes_transferred);
std::string mReponse;
std::shared_ptr<asio::ip::tcp::socket> mSock;
asio::streambuf mRequest;
void OnReponseSent(const system::error_code& ec, std::size_t bytes_transferred);
void OnFinish();
std::string ProcessRequest(asio::streambuf& request);
};
#include "Service.h"
Service::Service(std::shared_ptr<asio::ip::tcp::socket> sock){
mSock = sock;
}
void Service::StartHandling() {
asio::async_read_until(mSock, mRequest, '\n', [this](const system::error_code ec, std::size_t bytes_transferred) {OnRequestReceived(ec, bytes_transferred); });
}
void Service::OnRequestReceived(const system::error_code& ec, std::size_t bytes_transferred) {
if (ec.value() != 0 || ER) {
std::cout << "Error code:" << ec.value() << "Error message: " << ec.message() << "\n";
OnFinish();
return;
}
mReponse = ProcessRequest(mRequest);
asio::async_write(mSock, asio::buffer(mReponse), [this](const system::error_code& ec, std::size_t bytes_transferred) {OnReponseSent(ec, bytes_transferred); });
}
void Service::OnReponseSent(const system::error_code& ec, std::size_t bytes_transferred) {
if (ec.value() != 0 || ER) {
std::cout << "Error code:" << ec.value() << "Error message: " << ec.message() << "\n";
}
OnFinish();
}
void Service::OnFinish() {
delete this;
}
std::string Service::ProcessRequest(asio::streambuf& request) {
std::string reponse;
std::istream input(&request);
std::getline(input, reponse);
assert(reponse.back() == '\n');
return reponse;
}
I have no idea what to do. I wanted to do it myself, but I couldn't even debug because I couldn't figure out where the problem was going and it wasn't built.
It simply doesn't compile. I genuinely wonder how people can come up with /so much/ code before noticing that stuff doesn't compile.
Rule #1: Baby Steps (this goes for the professionals just as much, only they have it internalized).
You're doing stuff like:
mios = std::make_shared<asio::io_service>(ios);
This requires io_service to be copyable (which it isn't). You would probably make mios a reference:
asio::io_service& mios;
There seems to be a lot of "superstitious" use of shared_ptr all around.
The fact that
assert(thread > 0);
misspelled threads indicates that you may have been building Release-only builds.
Read the compiler messages:
void Service::StartHandling() {
asio::async_read_until(mSock, mRequest, '\n', [this](const system::error_code ec, std::size_t bytes_transferred) {OnRequestReceived(ec, bytes_transferred); });
}
This triggers the error:
/home/sehe/custom/boost_1_73_0/boost/asio/impl/read_until.hpp|959 col 53| error: no type named ‘executor_type’ in ‘class std::shared_ptr<boost::asio::basic_stream_socket<boost::asio::ip::tcp> >’
Obviously you meant *mSock. Same later:
asio::async_write(*mSock, asio::buffer(mReponse), [this](const system::error_code& ec, std::size_t bytes_transferred) {OnReponseSent(ec, bytes_transferred); });
A pointer is not the object it points to - not even smart pointers. The point [sic] of smart pointers is not to make C++ equal to (say) Java - you should use Java if you wanted that.
With these it compiles: Live ON Wandbox
More Review
top-level const makes no difference in value arguments
Don't use new or delete:
mWork.reset(new asio::io_service::work(mios));
use make_unique instead
mWork = std::make_unique<asio::io_service::work>(mios);
// ...
mAcceptor = std::make_unique<Acceptor>(mios, port_num);
Use header guards (or #pragma once)
Do not use namespace using-directives; use using-declarations instead
Especially don't use namespace using-directives in header files (you make it
impossible for your users to prevent/fix name collisions, which may lead to compile error or silent change of behaviour)
Use constructor initializer lists (and move-semantics):
Service::Service(std::shared_ptr<asio::ip::tcp::socket> sock){
mSock = sock;
}
Becomes
Service::Service(std::shared_ptr<asio::ip::tcp::socket> sock)
: mSock(std::move(sock))
{ }
Here:
(new Service(std::move(sock)))->StartHandling();
Don't use new, don't superstitious-use shared pointer, and, ironically, in
the case of Service consider using enable_shared_from_this so you do
use shared_ptr instead of the delete this; anti-pattern.
Initialize your primitive class members1
std::atomic<bool> mIsStopped{};
Without, it will have indeterminate value, which usually leads to UB when used
Don't ignore errors:
if (ec.value() == 0 || ER) {
(new Service(std::move(sock)))->StartHandling();
}
Instead, report / log. Also, detect errors portably:
if (!ec) {
Or
if (!ec.failed()) {
generally, handle errors (cin >> port_num e.g.),
catch by const&
Intermediate result (still compiles): Live on Wandbox
BONUS
Simplify, use asio::thread_pool, uniform initalization
USE bytes_transferred! read_until does not guarantee it stops on the
delimiter, because that's not how TCP works. Trailing data can be present
in the buffer. This means that in DEBUG builds this assert would sometimes fail:
assert(request.back() == '\n');
Actually the code read response.back() which is guaranteed to fail because getline doesn't include it ¯\(ツ)/¯
You might use boost::iostreams::restrict or instead
asio::dynamic_buffer() on a std::string and pass a string_view into
the handler (ProcessRequest):
mReponse = ProcessRequest(std::string_view(mRequest).substr(0, bytes_transferred));
And later
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream_buffer.hpp>
std::string Service::ProcessRequest(std::string_view request) {
assert(request.back() == '\n');
boost::iostreams::stream_buffer<boost::iostreams::array_source> buf(
request.data(), request.size());
std::istream input(&buf);
std::string reponse;
std::getline(input, reponse);
return reponse;
}
Get rid of all the redundant shared pointers. If Acceptor is already
dynamically allocated managed by a shared-pointer, there is really no need
to also make it own its tcp::acceptor instance by shared_ptr. In general
all the members could just be by value in your code. As long as the
surrounding object stays around (as you do with Service) the members are
guaranteed to stay alive as well.
mIsStopped can be eliminated by simply cancel()-ing the acceptor instead. To get thread-safety, simply post to the relevant executor.
If you wanted the server to actually exit when the stop command is executed, you need to make the while(true) loop have a stop condition, e.g.
int Server::Command(std::string const& cmd) {
std::cout << "Command: " << std::quoted(cmd) << "\n";
if (cmd == "quit") {
Stop();
return 1;
}
std::cerr << "Unknown command (\"quit\" to exit)" << std::endl;
return 0;
}
std::istream& operator>>(std::istream& is, Server& srv) {
std::string str;
is >> str;
if (srv.Command(str)) {
is.setstate(std::ios::badbit);
}
return is;
}
And in main:
while (std::cin >> srv) { }
FULL DEMO:
Live On Wandbox
File Acceptor.h
#ifndef _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_ACCEPTOR_H
#define _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_ACCEPTOR_H
#include "Service.h"
class Acceptor {
public:
template <typename Executor>
Acceptor(Executor ex, unsigned short port_num) : mAcceptor(make_strand(ex), {{}, port_num}) {}
void Start();
void Stop();
private:
tcp::acceptor mAcceptor;
void InitAccept();
void OnAccept(error_code ec, tcp::socket&& sock);
};
#endif
File Common.h
#pragma once
#include <boost/asio.hpp>
#include <memory>
#include <thread>
#include <atomic>
namespace asio = boost::asio;
using boost::system::error_code;
using asio::ip::tcp;
File Server.h
#ifndef _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_SERVER_H
#define _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_SERVER_H
#include "Acceptor.h"
class Server {
public:
explicit Server(unsigned short port_num);
void Start();
void Stop();
int Command(std::string const& str);
private:
asio::thread_pool mio;
Acceptor mAcceptor;
};
std::istream& operator>>(std::istream& is, Server& srv);
#endif
File Service.h
#ifndef _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_SERVICE_H
#define _HOME_SEHE_PROJECTS_STACKOVERFLOW_ASIO_SERVICE_H
#include "Common.h"
#include <iostream>
class Service : public std::enable_shared_from_this<Service> {
public:
explicit Service(tcp::socket&& sock);
void StartHandling();
private:
void OnRequestReceived(error_code ec, std::size_t bytes_transferred);
std::string mRequest, mReponse;
tcp::socket mSock;
void OnReponseSent(error_code ec, size_t bytes_transferred);
std::string ProcessRequest(std::string_view request);
};
#endif
File Acceptor.cpp
#include "Acceptor.h"
#include <utility>
void Acceptor::Start() {
mAcceptor.listen();
InitAccept();
}
void Acceptor::Stop() {
// be thread safe
post(mAcceptor.get_executor(), [this] { mAcceptor.cancel(); });
}
void Acceptor::InitAccept() {
mAcceptor.async_accept(
make_strand(mAcceptor.get_executor()),
[this](error_code error, tcp::socket&& sock) { OnAccept(error, std::move(sock)); });
}
void Acceptor::OnAccept(error_code ec, tcp::socket&& sock) {
if (!ec.failed()) {
std::make_shared<Service>(std::move(sock))->StartHandling();
InitAccept();
} else {
std::cout << "OnAccept: " << ec.message() << "\n";
}
}
File main.cpp
#include "Server.h"
#include <iostream>
int main() {
if (uint16_t port_num; std::cin >> port_num) {
try {
Server srv(port_num);
std::cout << "Port - " << port_num << "\nServer start\n";
srv.Start();
while (std::cin >> srv) { }
} catch (boost::system::system_error const& e) {
std::cout << "Error " << e.code().message() << "\n";
}
} else {
std::cerr << "Invalid input (port number required)\n";
}
}
File Server.cpp
#include "Server.h"
#include <iomanip>
Server::Server(unsigned short port_num)
: mAcceptor(make_strand(mio), port_num) {}
void Server::Start() { mAcceptor.Start(); }
void Server::Stop() { mAcceptor.Stop(); }
int Server::Command(std::string const& cmd) {
std::cout << "Command: " << std::quoted(cmd) << "\n";
if (cmd == "quit") {
Stop();
return 1;
}
std::cerr << "Unknown command (\"quit\" to exit)" << std::endl;
return 0;
}
std::istream& operator>>(std::istream& is, Server& srv) {
std::string str;
is >> str;
if (srv.Command(str)) {
is.setstate(std::ios::badbit);
}
return is;
}
File Service.cpp
#include "Service.h"
#include <utility>
#include <iomanip>
Service::Service(tcp::socket&& sock)
: mSock(std::move(sock)) {}
void Service::StartHandling() {
asio::async_read_until(
mSock, asio::dynamic_buffer(mRequest), '\n',
[this, self = shared_from_this()](error_code ec, std::size_t bytes_transferred) {
OnRequestReceived(ec, bytes_transferred);
});
}
void Service::OnRequestReceived(error_code ec, std::size_t bytes_transferred) {
if (ec) {
std::cout << "OnRequestReceived: " << ec.message() << "\n";
return;
}
std::string_view view = mRequest;
mReponse = ProcessRequest(view.substr(0, bytes_transferred));
asio::async_write(
mSock, asio::buffer(mReponse),
[this, self = shared_from_this()](error_code ec, std::size_t bytes_transferred) {
OnReponseSent(ec, bytes_transferred);
});
}
void Service::OnReponseSent(error_code ec, std::size_t /*bytes_transferred*/) {
if (ec) {
std::cout << "OnReponseSent: " << ec.message() << "\n";
}
}
#include <boost/iostreams/device/array.hpp>
#include <boost/iostreams/stream_buffer.hpp>
std::string Service::ProcessRequest(std::string_view request) {
//std::cerr << "TRACE: " << std::quoted(request) << "\n";
assert(request.back() == '\n');
boost::iostreams::stream_buffer<boost::iostreams::array_source> buf(
request.data(), request.size());
std::istream input(&buf);
std::string reponse;
std::getline(input, reponse);
return reponse + '\n';
}
E.g. when running with 2323 and later quit command:
# (echo 2323; sleep 30; echo quit) | ./sotest
Port - 2323
Server start
Command: "quit"
OnAccept: Operation canceled
It does correctly accept multiple connections:
# for a in {1..10}; do printf "Message with random data $RANDOM\n" | nc localhost 2323; done
Message with random data 8002
Message with random data 28046
Message with random data 17943
Message with random data 17845
Message with random data 10832
Message with random data 20049
Message with random data 27593
Message with random data 18979
Message with random data 2773
Message with random data 31159
Here is my sample code that opens a pipe in read mode. It uses boost::asio to read from the pipe. When data (let's say X bytes) is written to the pipe, it calls on_read with ec=EOF, bytes=X and X bytes of data in the buffer.
EOF is sent because the writer, after finishing writing to the pipe, closes it. I want to keep reading. That is why I call pipe.async_wait() in on_read. However, even if nothing is ready to be read from the pipe, on_read calls my_pipe::async_read() which again calls on_read() with bytes = 0 and ec=EOF. This goes on in an infinite loop.
Why does it keeping reading EOF repeatedly?
#include <boost/asio/io_service.hpp>
#include <boost/asio/placeholders.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>
#include <unistd.h>
#include <chrono>
#include <cstdint>
#include <fstream>
#include <iostream>
#include <string>
#include <boost/asio/read.hpp>
#include <boost/asio/write.hpp>
#include <boost/bind.hpp>
class my_pipe final
{
public:
explicit my_pipe(boost::asio::io_service& io_service);
~my_pipe();
private:
boost::asio::posix::stream_descriptor pipe;
std::vector<char> _buf{};
void async_read(const boost::system::error_code& ec);
void on_read(const boost::system::error_code& ec, std::size_t bytes_transferred);
};
my_pipe::my_pipe(boost::asio::io_service& io_service) : pipe(io_service)
{
int fd = open("/tmp/pipe1", O_RDONLY | O_NONBLOCK);
if (fd == -1)
return;
_buf.resize(8192);
pipe.assign(fd);
pipe.async_wait(boost::asio::posix::stream_descriptor::wait_read,
boost::bind(&my_pipe::async_read, this, boost::asio::placeholders::error));
}
my_pipe::~my_pipe()
{
pipe.close();
}
void my_pipe::async_read(const boost::system::error_code& ec)
{
std::cout << "async_read\n";
if (ec)
return;
boost::asio::async_read(pipe, boost::asio::buffer(_buf),
boost::bind(&my_pipe::on_read, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void my_pipe::on_read(const boost::system::error_code& ec, std::size_t bytes)
{
std::cout << "on_read. bytes=" << bytes << "\n";
if (!ec || ec == boost::asio::error::eof) {
if (ec == boost::asio::error::eof)
std::cout << "eof\n";
std::cout << "call async_read\n";
pipe.async_wait(boost::asio::posix::stream_descriptor::wait_read,
boost::bind(&my_pipe::async_read, this, boost::asio::placeholders::error));
} else {
std::cout << "on_read error: " << ec.message() << "\n";
}
}
int main()
{
boost::asio::io_service ios;
my_pipe obj(ios);
ios.run();
}
Thanks for all your help.
A pipe is "widowed" when all handles on one end are closed.
In this case, after you get an EOF, you should close the pipe handle and then reopen the pipe. You can then issue an async_read() on the new descriptor to wait for more data.
If you have multiple writers, also consider that writes are only guaranteed to be atomic up to PIPE_BUF bytes.
I am trying to capture single keyboard inputs in a non blocking way inside a while loop using boost asio async_read. The handler is expected to display the read characters.
My code:
#include <boost/asio/io_service.hpp>
#include <boost/asio/posix/stream_descriptor.hpp>
#include <boost/asio/read.hpp>
#include <boost/system/error_code.hpp>
#include <iostream>
#include <unistd.h>
#include <termios.h>
using namespace boost::asio;
void read_handler(const boost::system::error_code&, std::size_t)
{
char c;
std::cin>>c;
std::cout << "keyinput=" << c << std::endl;
}
int main()
{
io_service ioservice;
posix::stream_descriptor stream(ioservice, STDIN_FILENO);
char buf[1];
while(1)
{
async_read(stream, buffer(buf,sizeof(buf)), read_handler);
ioservice.run();
}
return 0;
}
My output is not as expected(keyinput=char format):
a
key input
b
c
d
e
Where am I going wrong?
Also the program is very cpu intensive. How to rectify it?
There's an important restriction on async IO with stdin: Strange exception throw - assign: Operation not permitted
Secondly, if you use async_read do not use std::cin at the same time (you will just do two reads). (Do look at async_wait instead).
That aside, you should be able to fix the high CPU load by using async IO properly:
#include <boost/asio.hpp>
#include <iostream>
using namespace boost::asio;
int main()
{
io_service ioservice;
posix::stream_descriptor stream(ioservice, STDIN_FILENO);
char buf[1] = {};
std::function<void(boost::system::error_code, size_t)> read_handler;
read_handler = [&](boost::system::error_code ec, size_t len) {
if (ec) {
std::cerr << "exit with " << ec.message() << std::endl;
} else {
if (len == 1) {
std::cout << "keyinput=" << buf[0] << std::endl;
}
async_read(stream, buffer(buf), read_handler);
}
};
async_read(stream, buffer(buf), read_handler);
ioservice.run();
}
As you can see the while loop has been replaced with a chain of async operations.
I am using c++ boost asio for making a server client application.
I followed the guide lines from here.
And I am still wondering why I get the following result:
./server #ok
./client # error
bind: Address already in use
server.cpp:
#include <ctime>
#include <iostream>
#include <stdio.h>
#include <string>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::udp;
struct UDP_Message
{
double number;
};
int main()
{
try
{
boost::asio::io_service io_service;
udp::socket socket(io_service, udp::endpoint(udp::v4(), config::udp_port));
UDP_Message message;
message.number=0;
for (;;)
{
udp::endpoint remote_endpoint;
message.number=message.number+0.001;
boost::system::error_code ignored_error;
socket.send_to(boost::asio::buffer(&message,sizeof(message)),
remote_endpoint, 0, ignored_error);
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
client.cpp:
#include <ctime>
#include <iostream>
#include <stdio.h>
#include <string>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::udp;
namespace config
{
const unsigned short udp_port=1414;
}
struct UDP_Message
{
double number;
};
int main()
{
try
{
boost::asio::io_service io_service;
boost::asio::socket_base::reuse_address option(true);
udp::socket socket(io_service, udp::v4());
socket.set_option(option);
socket.bind(udp::endpoint(udp::v4(), config::udp_port));
UDP_Message message;
for (;;)
{
boost::array<char, 1> recv_buf;
udp::endpoint remote_endpoint;
boost::system::error_code error;
socket.receive_from(boost::asio::buffer(recv_buf),
remote_endpoint, 0, error);
if (error && error != boost::asio::error::message_size)
throw boost::system::system_error(error);
std::memcpy(&message,recv_buf.data(),sizeof(message));
std::cout<<message.number<<std::endl;
}
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
You are trying to bind both your client and server to the same port, udp_port=1414. This you can not do.
I'm currently working with Boost::Asio to do a basic Read/Write server, I have a little problem when it comes to the usage of the async_read function of the library.
Here's my code snippet :
main.cpp :
#include <ctime>
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include "TCPServer.hpp"
#include "TCPConnection.hpp"
using boost::asio::ip::tcp;
int main()
{
try
{
boost::asio::io_service io_service;
TCPServer server(io_service);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
TCPServer.hpp :
#ifndef TCPSERVER_HPP_
#define TCPSERVER_HPP_
#include <ctime>
#include <iostream>
#include <string>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
#include "TCPConnection.hpp"
using boost::asio::ip::tcp;
class TCPServer
{
private:
tcp::acceptor _acceptor;
public:
TCPServer(boost::asio::io_service& ioService)
: _acceptor(ioService, tcp::endpoint(tcp::v4(), 4040))
{
startAccept();
}
private:
void startAccept()
{
TCPConnection::pointer newConnection =
TCPConnection::create(_acceptor.get_io_service());
_acceptor.async_accept(newConnection->getSocket(),
boost::bind(&TCPServer::handleAccept, this, newConnection,
boost::asio::placeholders::error));
}
void handleAccept(TCPConnection::pointer newConnection, const boost::system::error_code& error)
{
if (!error)
{
newConnection->asyncWrite("JIOLE");
startAccept();
}
}
};
#endif
TCPConnection.hpp :
#ifndef TCPCONNECTION_HPP_
#define TCPCONNECTION_HPP_
#include <ctime>
#include <iostream>
#include <string>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
class TCPConnection : public boost::enable_shared_from_this<TCPConnection>
{
private:
boost::asio::ip::tcp::socket _socket;
std::string _readMessage;
boost::asio::streambuf _response;
public:
typedef boost::shared_ptr<TCPConnection> pointer;
static pointer create(boost::asio::io_service& ios)
{
return pointer(new TCPConnection(ios));
}
boost::asio::ip::tcp::socket& getSocket()
{
return _socket;
}
void asyncWrite(const std::string &message)
{
boost::asio::async_write(_socket,
boost::asio::buffer(message),
boost::bind(&TCPConnection::handleWrite, shared_from_this(),
boost::asio::placeholders::error));
std::cout << "AsyncWrite" << std::endl;
}
void asyncRead()
{
std::cout << "1st \"asyncRead\"" << std::endl;
boost::asio::async_read(_socket,
_response,
boost::bind(&TCPConnection::handleRead, shared_from_this(),
boost::asio::placeholders::error));
std::cout << "2nd \"asyncRead\"" << std::endl;
}
void close()
{
_socket.close();
}
private:
TCPConnection(boost::asio::io_service &ioS) : _socket(ioS) {}
void handleWrite(const boost::system::error_code& error)
{
std::cout << "Write Handler" << std::endl;
if (!error)
{
asyncRead();
}
//SEE WHAT TO DO IN CASE OF ERROR
}
void handleRead(const boost::system::error_code& error)
{
std::cout << "Read Handler" << std::endl;
if (!error)
{
std::cout << &_response << std::endl;
asyncRead();
}
else
{
std::cout << &error << std::endl;
_socket.close();
}
//CREATE SENDER(RSEPONSE::ERROR)
}
};
#endif
The problem is that my async_read doesn't call the handler. Here is the output :
Output :
AsyncWrite
Write Handler
1st "asyncRead"
2nd "asyncRead"
When I'm writing something on a NetCat Client nothing is being received.
Here what happens when i'm pressing ctrl+C :
Read Handler
0x7fff645f3be0
I don't understand why nothing is received.
The problem is that you did not tell async_read when it should call the handler function. There are two ways to specify this: Either specify the input size after which the handler shall be called or specifiy a delimiter through async_read_until.
In your case, you could use the following:
boost::asio::async_read_until(_socket,
_response,
"\n",
boost::bind(&TCPConnection::handleRead,
shared_from_this(),
boost::asio::placeholders::error)
);
This will call the handler when a newline is sent from the client.