I did the boost tutorial : An asynchronous TCP daytime server http://think-async.com/Asio/asio-1.1.1/doc/asio/tutorial/tutdaytime3.html
When I want to test it, the server is running so that's good but if I do nc -C localhost 4242 the client got the message of the server but the client is directly disconnected after.
Here my code :
#include "server.h"
#include "connection.h"
Server::Server(boost::asio::io_service& io_service) : accept(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), 4242))
{
wait_connection();
}
Server::~Server()
{
}
void Server::wait_connection()
{
std::cout << "wait_connection" << std::endl;
boost::shared_ptr<Connection> new_connection =
Connection::start_connection(accept.get_io_service());
accept.async_accept(new_connection->getSocket(), boost::bind(&Server::callback_accept, this, new_connection, boost::asio::placeholders::error));
}
void Server::callback_accept(boost::shared_ptr<Connection> new_connection, const boost::system::error_code &error)
{
if (!error)
{
new_connection->send_message_to_client();
wait_connection();
}
}
Connection::Connection(boost::asio::io_service& io_service) : socket(io_service)
{
}
Connection::~Connection()
{
std::cout << "destructeur Connection" << std::endl;
}
boost::shared_ptr<Connection> Connection::start_connection(boost::asio::io_service& io_service)
{
return (boost::shared_ptr<Connection>(new Connection(io_service)));
}
boost::asio::ip::tcp::socket& Connection::getSocket()
{
return (this->socket);
}
void Connection::send_message_to_client()
{
message = "Bienvenue!\n";
boost::asio::async_write(socket, boost::asio::buffer(message), boost::bind(&Connection::callback_send, shared_from_this()));
}
void Connection::callback_send()
{
}
int main()
{
try {
boost::asio::io_service io_service;
Server server(io_service);
io_service.run();
}
catch (std::exception& e) {
std::cerr << e.what() << std::endl;
}
return (0);
}
#ifndef SERVER_H
#define SERVER_H
#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 "connection.h"
class Server {
private:
boost::asio::ip::tcp::acceptor accept;
public:
Server (boost::asio::io_service&);
~Server ();
void wait_connection();
void callback_accept(boost::shared_ptr<Connection> new_connection, const boost::system::error_code& error);
};
#endif /* end of include guard: SERVER_H */
#ifndef CONNECTION_H
#define CONNECTION_H
#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 Connection : public boost::enable_shared_from_this<Connection>
{
private:
boost::asio::ip::tcp::socket socket;
std::string message;
public:
Connection (boost::asio::io_service&);
~Connection ();
static boost::shared_ptr<Connection> start_connection(boost::asio::io_service&);
boost::asio::ip::tcp::socket& getSocket();
void send_message_to_client();
void callback_send();
};
#endif /* end of include guard: CONNECTION_H */
Crux: Shared pointers employ keep the object alive until the reference count reaches zero.
You write the message to the client here. When it's complete, you will execute callback_send:
boost::asio::async_write(socket, boost::asio::buffer(message),
boost::bind(&Connection::callback_send, shared_from_this()));
So what do we do next?
void Connection::callback_send() {}
Oh. That's... not a lot. So. Nothing?
Well. Almost nothing.
It's a case of "not doing something is also doing something". By not posting another operation that keeps the socket/connection alive, this means that the connection is going to be released.
Because nothing else keeps the shared_ptr to the connection, shared_ptr will delete the connection (invoking the destructor, which you could see because it prints destructeur Connection every time).
So. What is the solution? We Don't Know. It's up to you what you want to do after you said "welcome". In most likely-hood you will want to wait for some kind of message from the client. This would involve some async_read* call which happily keeps the connection alive (shared_from_this() again).
Demo
Let's assume you want to keep receiving lines, and you send the same lines back, reversed:
void Connection::callback_send() {
boost::asio::async_read_until(socket, request, "\n",
boost::bind(&Connection::on_request_received, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void Connection::on_request_received(boost::system::error_code ec, size_t n) {
if (ec && !((ec == boost::asio::error::eof) && n))
std::cout << "Receive error: " << ec.message() << "\n";
else
{
std::cout << "Received request\n";
{
std::istream is(&request);
std::getline(is, message);
}
std::reverse(message.begin(), message.end());
std::cout << "Sending response: " << message << "\n";
message += '\n';
if (!ec) boost::asio::async_write(socket, boost::asio::buffer(message),
boost::bind(&Connection::callback_send, shared_from_this()));
}
}
Related
I try to open a UDP server. A baby example works (I receive what I expect and what wireshark also shows):
Baby example:
int main(int argc, char* const argv[])
{
try
{
boost::asio::io_context io_context;
boost::asio::ip::udp::endpoint ep(boost::asio::ip::udp::v4(), 60001);
boost::asio::ip::udp::socket sock(io_context, ep);
UDPServer server(std::move(sock), callbackUDP);
io_context.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
UDPServer.hpp:
#include <boost/asio.hpp>
#include <functional>
#include <vector>
#include <thread>
#define BUFFERSIZE 1501
class UDPServer
{
public:
explicit UDPServer(boost::asio::ip::udp::socket socket, std::function<void(const std::vector<char>&)> callbackFunction);
virtual ~UDPServer();
private:
void read();
boost::asio::ip::udp::socket socket_;
boost::asio::ip::udp::endpoint endpoint_;
std::function<void(const std::vector<char>&)> callbackFunction_;
char data_[1500 + 1]; // 1500 bytes is safe limit as it is max of ethernet frame, +1 is for \0 terminator
};
UDPServer.cpp:
#include <iostream>
#include "UDPServer.h"
UDPServer::UDPServer(boost::asio::ip::udp::socket socket, std::function<void(const std::vector<char>&)> callbackFunction):
socket_(std::move(socket)),
callbackFunction_(callbackFunction)
{
read();
}
UDPServer::~UDPServer()
{
}
void UDPServer::read()
{
socket_.async_receive_from(boost::asio::buffer(data_, 1500), endpoint_,
[this](boost::system::error_code ec, std::size_t length)
{
if (ec)
{
return;
}
data_[length] = '\0';
if (strcmp(data_, "\n") == 0)
{
return;
}
std::vector<char> dataVector(data_, data_ + length);
callbackFunction_(dataVector);
read();
}
);
}
Now what I want to convert this to is a class with as constructor only the port and a callback function (let forget about the latter and just print the message for now, adding the callback is normally no problem).
I tried the following, but it doesn't work:
int main(int argc, char* const argv[])
{
UDPServer server(60001);
}
UDPServer.h:
#include <boost/asio.hpp>
#include <functional>
#include <vector>
#include <thread>
#define BUFFERSIZE 1501
class UDPServer
{
public:
explicit UDPServer(uint16_t port);
virtual ~UDPServer();
private:
boost::asio::io_context io_context_;
boost::asio::ip::udp::socket socket_;
boost::asio::ip::udp::endpoint endpoint_;
std::array<char, BUFFERSIZE> recv_buffer_;
std::thread thread_;
void run();
void start_receive();
void handle_reply(const boost::system::error_code& error, std::size_t bytes_transferred);
};
UDPServer.cpp:
#include <iostream>
#include "UDPServer.h"
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <iostream>
UDPServer::UDPServer(uint16_t port):
endpoint_(boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port)),
io_context_(),
socket_(io_context_, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port)),
thread_(&UDPServer::run, this)
{
start_receive();
}
UDPServer::~UDPServer()
{
io_context_.stop();
thread_.join();
}
void UDPServer::start_receive()
{
socket_.async_receive_from(boost::asio::buffer(recv_buffer_), endpoint_,
boost::bind(&UDPServer::handle_reply, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void UDPServer::handle_reply(const boost::system::error_code& error, std::size_t bytes_transferred)
{
if (!error)
{
try {
std::string string(recv_buffer_.data(), recv_buffer_.data() + bytes_transferred);
std::cout << "Message received: " << std::to_string(bytes_transferred) << ", " << string << std::endl;
}
catch (std::exception ex) {
std::cout << "handle_reply: Error parsing incoming message:" << ex.what() << std::endl;
}
catch (...)
{
std::cout << "handle_reply: Unknown error while parsing incoming message" << std::endl;
}
}
else
{
std::cout << "handle_reply: error: " << error.message() << std::endl;
}
start_receive();
}
void UDPServer::run()
{
try {
io_context_.run();
} catch( const std::exception& e )
{
std::cout << "Server network exception: " << e.what() << std::endl;
}
catch(...)
{
std::cout << "Unknown exception in server network thread" << std::endl;
}
std::cout << "Server network thread stopped" << std::endl;
};
When running I get "Server network thread stopped". io_context doesn't seem to start and doesn't block. Someone an idea what I do wrong? Thanks a lot!
EDIT tried this after comment, same result (except that message comes after 1 second)
UDPServer::UDPServer(uint16_t port):
endpoint_(boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port)),
io_context_(),
socket_(io_context_, boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), port))
{
start_receive();
std::this_thread::sleep_for (std::chrono::seconds(1));
thread_ = std::thread(&UDPServer::run, this);
}
Your destructor explicitly tells the service to stop:
UDPServer::~UDPServer() {
io_context_.stop();
thread_.join();
}
That's part of your problem. The other part is as pointed out in the comment: you have a race condition where the thread exits before you even post your first async operation.
Solve it by adding a work guard:
boost::asio::io_context io_;
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work_ {io_.get_executor()};
Now the destructor can be:
UDPServer::~UDPServer() {
work_.reset(); // allow service to run out of work
thread_.join();
}
Other notes:
avoid chaining back to start_receive when there was an error
std::to_string was redundant
the order of initialization for members is defined by the order of their declaration, not their initializers in the initializer list. Catch these bug sources with -Wall -Wextra -pedantic
= handle exceptions in your service thread (see Should the exception thrown by boost::asio::io_service::run() be caught?)
I'd suggest std::bind over boost::bind:
std::bind(&UDPServer::handle_reply, this,
std::placeholders::_1,
std::placeholders::_2));
Or just use a lambda:
[this](error_code ec, size_t xfer) { handle_reply(ec, xfer); });
LIVE DEMO
Compiler Explorer
#include <boost/asio.hpp>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <thread>
#include <vector>
using boost::asio::ip::udp;
using boost::system::error_code;
using boost::asio::io_context;
#define BUFFERSIZE 1501
class UDPServer {
public:
explicit UDPServer(uint16_t port);
virtual ~UDPServer();
private:
io_context io_;
boost::asio::executor_work_guard<io_context::executor_type> work_ {io_.get_executor()};
udp::endpoint endpoint_;
udp::socket socket_;
std::array<char, BUFFERSIZE> recv_buffer_;
std::thread thread_;
void run();
void start_receive();
void handle_reply(const error_code& error, size_t transferred);
};
UDPServer::UDPServer(uint16_t port)
: endpoint_(udp::endpoint(udp::v4(), port)),
socket_(io_, endpoint_),
thread_(&UDPServer::run, this) {
start_receive();
}
UDPServer::~UDPServer() {
work_.reset(); // allow service to run out of work
thread_.join();
}
void UDPServer::start_receive() {
socket_.async_receive_from(boost::asio::buffer(recv_buffer_), endpoint_,
#if 0
std::bind(&UDPServer::handle_reply, this,
std::placeholders::_1,
std::placeholders::_2));
#else
[this](error_code ec, size_t xfer) { handle_reply(ec, xfer); });
#endif
}
void UDPServer::handle_reply(const error_code& error, size_t transferred) {
if (!error) {
try {
std::string_view s(recv_buffer_.data(), transferred);
std::cout << "Message received: " << transferred << ", "
<< std::quoted(s) << "\n";
} catch (std::exception const& ex) {
std::cout << "handle_reply: Error parsing incoming message:"
<< ex.what() << "\n";
} catch (...) {
std::cout
<< "handle_reply: Unknown error while parsing incoming message\n";
}
start_receive();
} else {
std::cout << "handle_reply: error: " << error.message() << "\n";
}
}
void UDPServer::run() {
while (true) {
try {
if (io_.run() == 0u) {
break;
}
} catch (const std::exception& e) {
std::cout << "Server network exception: " << e.what() << "\n";
} catch (...) {
std::cout << "Unknown exception in server network thread\n";
}
}
std::cout << "Server network thread stopped\n";
}
int main() {
std::cout << std::unitbuf;
UDPServer server(60001);
}
Testing with random words:
sort -R /etc/dictionaries-common/words | while read w; do sleep 1; netcat -u localhost 60001 -w 0 <<<"$w"; done
Live output:
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.
I can create a simple TCP server that can respond to one client, but I don't know how to create a server that can handle multiple clients at once. I have referred to examples like TCP daytime async server, but it just sends the data to client. What I need to create is keep alive the connection as long as client exists. Both Client and Server will communicate in Json. Consider one case where client will give {"hello":"Client"} and server should respond {"Hello":"Server"}, and say another {"message":"How are you?"} and its response {"response" : "Fine"}. I need to handle multiple clients at once. I read Chat server documentation, but it is too hard to comprehend. Can someone give basic flow of how to start the code using Boost.Asio? Thanks.
Below is the code given:
#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 <json/reader.h>
#include <json/writer.h>
Json::Value GetRootFromJson(std::string json)
{
Json::Value root;
Json::Reader reader;
bool success = reader.parse(json, root);
return root;
}
std::string GetJsonFromRoot(Json::Value root)
{
Json::FastWriter writer;
std::string json = writer.write(root);
return json;
}
using boost::asio::ip::tcp;
std::string make_daytime_string()
{
using namespace std; // For time_t, time and ctime;
time_t now = time(0);
return ctime(&now);
}
class tcp_connection
: public boost::enable_shared_from_this<tcp_connection>
{
public:
typedef boost::shared_ptr<tcp_connection> pointer;
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new tcp_connection(io_service));
}
tcp::socket& socket()
{
return socket_;
}
void start()
{
//message_ = make_daytime_string();
//Read from client, make json and send appropriate response
boost::asio::async_read(socket_, boost::asio::buffer(message_),
boost::bind(&tcp_connection::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
std::string messageP(message_);
std::cout << messageP << std::endl;
Json::Value root = GetRootFromJson(messageP);
std::string isHello = root["hello"].asString();
std::string isMessage = root["message"].asString();
if(!isHello.empty())
{
std::string messageTemp = "{\"Hello\":\"Server\"}";
boost::asio::async_write(socket_, boost::asio::buffer(messageTemp),
boost::bind(&tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
if(!isMessage.empty())
{
std::string messageTemp = "{\"response\":\"Fine\"}";
boost::asio::async_write(socket_, boost::asio::buffer(messageTemp),
boost::bind(&tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
/*
boost::asio::async_write(socket_, boost::asio::buffer(message_),
boost::bind(&tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
*/
}
private:
tcp_connection(boost::asio::io_service& io_service)
: socket_(io_service)
{
}
void handle_write(const boost::system::error_code& /*error*/,
size_t /*bytes_transferred*/)
{
}
void handle_read(const boost::system::error_code& /*error*/,
size_t /*bytes_transferred*/)
{
std::cout << "Handle Read of connection\n";
}
tcp::socket socket_;
std::string message_;
};
class tcp_server
{
public:
tcp_server(boost::asio::io_service& io_service)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), 1936))
{
start_accept();
}
private:
void start_accept()
{
tcp_connection::pointer new_connection =
tcp_connection::create(acceptor_.get_io_service());
acceptor_.async_accept(new_connection->socket(),
boost::bind(&tcp_server::handle_accept, this, new_connection,
boost::asio::placeholders::error));
}
void handle_accept(tcp_connection::pointer new_connection,
const boost::system::error_code& error)
{
if (!error)
{
std::cout << "A client connected" << std::endl;
new_connection->start();
}
start_accept();
}
tcp::acceptor acceptor_;
};
int main()
{
try
{
boost::asio::io_service io_service;
tcp_server server(io_service);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
Running this gives me:
Error 2 error C2679: binary '=' : no operator found which takes a right-hand operand of type 'const boost::asio::const_buffer' (or there is no acceptable conversion) C:\boost_1_55_0_dyn\boost\asio\detailconsuming_buffers.hpp 175
Edit: I have added a very simple client code, that will send "{\"Hello\":\"Client\"}" to the server and expects the output. But the server code, given by sehe could not enter into handle_read
#include <iostream>
#include <boost/array.hpp>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
int main(int argc, char* argv[])
{
try
{
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query("localhost", "1936");
tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
tcp::socket socket(io_service);
boost::asio::connect(socket, endpoint_iterator);
std::string message = "{\"hello\":\"Client\"}";
std::cout << "Sending the message: " << message << std::endl;
socket.send(boost::asio::buffer(message));
std::cout << "Sent the message: " << message << std::endl;
boost::asio::streambuf buf;
size_t len;
std::string messageReceived;
boost::asio::streambuf::mutable_buffers_type mbuf = buf.prepare(512);
std::cout << "Now receiving message\n";
std::string messageServer;
try
{
boost::asio::streambuf buf;
size_t len;
boost::asio::streambuf::mutable_buffers_type mbuf = buf.prepare(512);
do {
len = socket.receive(mbuf);
std::cout << len << std::endl;
std::string str(boost::asio::buffers_begin(mbuf), boost::asio::buffers_begin(mbuf) + len);
messageServer = messageServer + str;
} while (len>=512);
}
catch (boost::system::system_error err)
{
std::cout << err.code() << " " << err.what();
}
std::cout << messageServer << std::endl;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
Is this the correct way to implement the server? Thank you.
As documented,
boost::asio::async_read(socket_, boost::asio::buffer(message_),
takes a streambuf.
Further more it is an asynchronous operations, so it makes zero sense to access message_ after posting the read operation, and before the completion handler (handle_read) is called back.
The following at least compiles for me:
#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 <json/reader.h>
#include <json/writer.h>
namespace {
Json::Value to_json(std::string json)
{
Json::Value root;
Json::Reader reader;
/*bool success =*/ reader.parse(json, root);
return root;
}
std::string to_string(Json::Value root) // unused TODO FIXME
{
Json::FastWriter writer;
std::string json = writer.write(root);
return json;
}
}
using boost::asio::ip::tcp;
class tcp_connection : public boost::enable_shared_from_this<tcp_connection>
{
public:
typedef boost::shared_ptr<tcp_connection> pointer;
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new tcp_connection(io_service));
}
tcp::socket& socket()
{
return socket_;
}
void start()
{
//Read from client, make json and send appropriate response
boost::asio::async_read(socket_, message_,
boost::bind(&tcp_connection::handle_read, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private:
tcp_connection(boost::asio::io_service& io_service)
: socket_(io_service)
{
}
void handle_write(const boost::system::error_code& /*error*/,
size_t /*bytes_transferred*/)
{
}
void handle_read(const boost::system::error_code& error, size_t bytes_transferred)
{
std::cout << "Handle Read of connection\n";
if (error && error != boost::asio::error::eof) {
std::cout << "Error: " << error.message() << "\n";
return;
}
std::string messageP;
{
std::stringstream ss;
ss << &message_;
ss.flush();
messageP = ss.str();
}
std::cout << messageP << std::endl;
Json::Value root = to_json(messageP);
std::string isHello = root["hello"].asString();
std::string isMessage = root["message"].asString();
if(!isHello.empty())
{
std::string messageTemp = "{\"Hello\":\"Server\"}";
boost::asio::async_write(socket_, boost::asio::buffer(messageTemp),
boost::bind(&tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
if(!isMessage.empty())
{
std::string messageTemp = "{\"response\":\"Fine\"}";
boost::asio::async_write(socket_, boost::asio::buffer(messageTemp),
boost::bind(&tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
}
tcp::socket socket_;
boost::asio::streambuf message_;
};
class tcp_server
{
public:
tcp_server(boost::asio::io_service& io_service)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), 1936))
{
start_accept();
}
private:
void start_accept()
{
tcp_connection::pointer new_connection =
tcp_connection::create(acceptor_.get_io_service());
acceptor_.async_accept(new_connection->socket(),
boost::bind(&tcp_server::handle_accept, this, new_connection,
boost::asio::placeholders::error));
}
void handle_accept(tcp_connection::pointer new_connection, const boost::system::error_code& error)
{
if (!error)
{
std::cout << "A client connected" << std::endl;
new_connection->start();
}
start_accept();
}
tcp::acceptor acceptor_;
};
int main()
{
try
{
boost::asio::io_service io_service;
tcp_server server(io_service);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
The includes you need. Some maybe are unnecessary:
boost/asio.hpp, boost/thread.hpp, boost/asio/io_service.hpp
boost/asio/spawn.hpp, boost/asio/write.hpp, boost/asio/buffer.hpp
boost/asio/ip/tcp.hpp, iostream, stdlib.h, array, string
vector, string.h, stdio.h, process.h, iterator
using namespace boost::asio;
using namespace boost::asio::ip;
io_service ioservice;
tcp::endpoint sim_endpoint{ tcp::v4(), 4066 }; //{which connectiontype, portnumber}
tcp::acceptor sim_acceptor{ ioservice, sim_endpoint };
std::vector<tcp::socket> sim_sockets;
static int iErgebnis;
int iSocket = 0;
void do_write(int a) //gets the postion of the socket in the vector
{
int iWSchleife = 1; //to stay connected with putty or something
static char chData[32000];
std::string sBuf = "Received!\r\n";
while (iWSchleife > 0)
{
boost::system::error_code error;
memset(chData, 0, sizeof(chData));
iErgebnis = sim_sockets[a].read_some(boost::asio::buffer(chData), error); //recv wie bei winsock. simulator empfängt
iWSchleife = iErgebnis; //if iErgebnis is bigger then 0 it will stay in the loop. iErgebniss is always >0 when data is received
if (iErgebnis > 0) {
printf("%d Zeichen empf.vom Client : \n%s\n\n", iErgebnis, chData);
write(sim_sockets[a], boost::asio::buffer(sBuf), error);
}
else {
boost::system::error_code ec;
sim_sockets[a].shutdown(boost::asio::ip::tcp::socket::shutdown_send, ec); //close the socket when no data
if (ec)
{
printf("studown error"); // An error occurred.
}
}
}
}
void do_accept(yield_context yield)
{
while (1) //endless loop to accept limitless clients
{
sim_sockets.emplace_back(ioservice); //look to the link below for more info
sim_acceptor.async_accept(sim_sockets.back(), yield); //waits here to accept an client
boost::thread dosome(do_write, iSocket); //when accepts starts the thread do_write and passes the parameter iSocket
iSocket++; //to know the position of the socket in the vector
}
}
int main()
{
sim_acceptor.listen();
spawn(ioservice, do_accept); //here you can learn more about Coroutines https://theboostcpplibraries.com/boost.coroutine
ioservice.run(); //from here you jump to do:accept
getchar();
}
I am trying to make my first TCP server using boost::asio. The server will listen to clients and if it receives message "MESSAGE_SEND_A:", it should send the following message back to the client: "A|A|A|A|A|A". If it receives message "MESSAGE_SEND_B:", then accordingly it should send another message to the client: "B|B|B|B|B|B".
Now, I have been studying the Boost TCP server tutorial and it is more or less clear:
EDIT: Code is rewritten based on comments
#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>
using boost::asio::ip::tcp;
class tcp_connection : public boost::enable_shared_from_this<tcp_connection>
{
public:
typedef boost::shared_ptr<tcp_connection> pointer;
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new tcp_connection(io_service));
}
tcp::socket& socket()
{
return socket_;
}
void start()
{
// Start reading messages from the server
start_read();
if (messageFromClient_ == "MESSAGEA:")
{
messageToClient_ = "A|A|A|A|A|A|A|A|A|A";
}
else if (messageFromClient_ == "MESSAGEB:")
{
messageToClient_ = "B|B|B|B|B|B|B|B|B|B";
}
boost::asio::async_write(socket_, boost::asio::buffer(messageToClient_),
boost::bind(&tcp_connection::handle_write, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
private:
tcp_connection(boost::asio::io_service& io_service) : socket_(io_service)
{
}
// Reading messages from the server
void start_read()
{
// Start an asynchronous operation to read a newline-delimited message.
// When read, handle_read should kick in
boost::asio::async_read(socket_, input_buffer_,
boost::asio::transfer_at_least(1),
boost::bind(&tcp_connection::handle_read, this,
boost::asio::placeholders::error));
}
// When stream is received, handle the message from the client
void handle_read(const boost::system::error_code& ec)
{
//if (stopped_)
// return;
// Making the message nil every time the function starts. Do I need it???????
messageFromClient_ = "";
if (!ec)
{
// Extract the newline-delimited message from the buffer.
std::string line;
std::istream is(&input_buffer_);
std::getline(is, line);
// Empty messages are heartbeats and so ignored.
if (!line.empty())
{
messageFromClient_ = line;
std::cout << "Received: " << line << "\n";
}
start_read();
}
else
{
std::cout << "Error on receive: " << ec.message() << "\n";
}
}
void handle_write(const boost::system::error_code& /*error*/,
size_t /*bytes_transferred*/)
{
}
tcp::socket socket_;
std::string messageToClient_;
boost::asio::streambuf input_buffer_;
std::string messageFromClient_;
};
class tcp_server
{
public:
tcp_server(boost::asio::io_service& io_service)
: acceptor_(io_service, tcp::endpoint(tcp::v4(), 7767))
{
start_accept();
}
private:
void start_accept()
{
tcp_connection::pointer new_connection =
tcp_connection::create(acceptor_.get_io_service());
acceptor_.async_accept(new_connection->socket(),
boost::bind(&tcp_server::handle_accept, this, new_connection,
boost::asio::placeholders::error));
}
void handle_user_read(const boost::system::error_code& err,
std::size_t bytes_transferred)
{
}
void handle_accept(tcp_connection::pointer new_connection,
const boost::system::error_code& error)
{
if (!error)
{
new_connection->start();
start_accept();
}
}
tcp::acceptor acceptor_;
};
int main()
{
try
{
boost::asio::io_service io_service;
tcp_server server(io_service);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
The code is compiled well on Qt, but gives me:
Error on receive: Operation canceled
every time I am trying to send a message from my client (iPad).
Thank you!
the function start() should asynchronously read from the socket, while invoking handle_read() function when data received.
Please review this example:
http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/example/timeouts/async_tcp_client.cpp
I've implemented simple boost::asio program that starts tcp connection.
It works perfect on linux (ubuntu 12.04, boost 1_48, gcc 4.6.4),
but not on Win7 (boost 1_55, vs2008express).
After accepting few (from 3 to 20) connections it hangs up, and doesn't accept connections anymore.
Is it some windows protection problem? I turn off firewall and antivirus.
Here is the code for reference:
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
class Session : public boost::enable_shared_from_this<Session>
{
public:
Session(boost::asio::io_service &io_) : tcpSocket(io_)
{
std::cerr << "session ctor" << std::endl;
}
~Session()
{
std::cerr << "session Dtor" << std::endl;
}
boost::asio::ip::tcp::socket& getTcpSocket() { return this->tcpSocket; }
private:
boost::asio::ip::tcp::socket tcpSocket;
};
class Server
{
public:
static const unsigned int defaultPort = 55550;
Server();
void start();
private:
boost::asio::io_service io;
boost::asio::ip::tcp::acceptor acceptor;
void startAccept();
void handleAccept(boost::shared_ptr<Session> s_,
const boost::system::error_code& e_);
};
Server::Server()
: acceptor(io, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), defaultPort))
{
}
void Server::start()
{
this->startAccept();
this->io.run();
}
void Server::startAccept()
{
boost::shared_ptr<Session> s(new Session(io));
acceptor.async_accept(s->getTcpSocket(), boost::bind(&Server::handleAccept,
this, s, boost::asio::placeholders::error));
}
void Server::handleAccept(boost::shared_ptr<Session> s_,
const boost::system::error_code& e_)
{
std::cerr << "handleAccept" << std::endl;
if(e_)
std::cerr << e_ << std::endl;
this->startAccept();
}
int main(int, char**)
{
Server server;
server.start();
}
EDIT:
Problem solved. It was Cygwin problem.
It could be that the Win7 acceptor requires the option to reuse the address: i.e. SO_REUSEADDR.
Try changing your server constructor to:
Server::Server()
: acceptor(io)
{
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), defaultPort);
acceptor.open(endpoint.protocol());
acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
boost::system::error_code ec;
acceptor.bind(endpoint, ec);
if(ec)
// raise an exception or log this error, etc.
}