MultiThread Server in C++ - c++

I've to make a project of a synchronous TCP server and I started with an example of the boost library, the main problem is I would like to make a multithread server, actually with the example code server is just able to accept one connection per time. I've read carefully the documentation but I'm not understanding which function is invoked when a new request has been receved to the server.
Conceptually I understand how to operate, I've the launch a new thread when a new connection request is arrived, but by reading code i don't know how to start.
#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;
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();
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*/)
{
}
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(), 13))
{
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)
{
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;
}

Related

How to split the sample code below in source/header?

Is there a nice way of splitting the following sample C++ code in source and header such that all the users of the server don't need to indirectly include headers that are really only needed for the server's internals ?
Source listing for Daytime.3
//
// server.cpp
// ~~~~~~~~~~
//
// Copyright (c) 2003-2013 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 <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;
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();
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*/)
{
}
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(), 13))
{
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)
{
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;
}
Most examples I can find assume all the code will be in a single file, for some reason.
The question might seem stupid, but every time I try to refactor this, some template decides to crash. I could really use the example.
The general answer to this is to use Pimpl Idiom. You can make the header as simple as
#pragma once
#include <memory>
class tcp_server {
public:
tcp_server();
~tcp_server();
void run();
private:
struct impl;
std::unique_ptr<impl> _pimpl;
};
Then main.cpp can be:
#include "server.h"
#include <iostream>
int main()
{
try {
tcp_server server;
server.run();
} catch (std::exception const& e) {
std::cerr << e.what() << std::endl;
}
}
Of course, that leaves the question how you implement test.cpp:
tcp_server::tcp_server() : _pimpl(std::make_unique<impl>()) {}
void tcp_server::run() { _pimpl->run(); }
And all the logic has been moved to the implementation type and all local functions can have file linkage.
Live Demo
Live On Wandbox
File test.cpp
#include "server.h"
#include <iostream>
int main()
{
try {
tcp_server server;
server.run();
} catch (std::exception const& e) {
std::cerr << e.what() << std::endl;
}
}
File server.h
#pragma once
#include <memory>
class tcp_server {
public:
tcp_server();
~tcp_server();
void run();
private:
struct impl;
std::unique_ptr<impl> _pimpl;
};
File server.cpp
#include "server.h"
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <ctime>
#include <string>
using boost::asio::ip::tcp;
namespace /*anonymous, file linkage*/ {
static 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::any_io_executor executor)
{
return pointer(new tcp_connection(executor));
}
tcp::socket& socket() { return socket_; }
void start()
{
message_ = make_daytime_string();
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::any_io_executor executor)
: socket_(executor)
{
}
void handle_write(const boost::system::error_code& /*error*/,
size_t /*bytes_transferred*/)
{
}
tcp::socket socket_;
std::string message_;
};
} // namespace
struct tcp_server::impl {
void run() { io_service_.run(); }
void start_accept()
{
tcp_connection::pointer new_connection =
tcp_connection::create(acceptor_.get_executor());
acceptor_.async_accept(new_connection->socket(),
boost::bind(&impl::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) {
new_connection->start();
}
start_accept();
}
boost::asio::io_service io_service_;
tcp::acceptor acceptor_{io_service_, tcp::endpoint(tcp::v4(), 13)};
};
tcp_server::tcp_server() : _pimpl(std::make_unique<impl>()) {}
tcp_server::~tcp_server() = default;
void tcp_server::run() { _pimpl->run(); }

C++ Boost Assio - Start in another thread

I have Boost Assio implementation in my project. Here it is:
Session.h:
#ifndef VIBRANIUM_CORE_SESSION_H
#define VIBRANIUM_CORE_SESSION_H
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
namespace Vibranium{
class Session
: public std::enable_shared_from_this<Session>
{
public:
Session(tcp::socket socket)
: socket_(std::move(socket))
{
}
void start();
private:
void do_read();
void do_write(std::size_t length);
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
}
#endif //VIBRANIUM_CORE_SESSION_H
Session.cpp:
#include "Session.h"
void Vibranium::Session::start() {
do_read();
}
void Vibranium::Session::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 Vibranium::Session::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();
}
});
}
Server.h:
#ifndef VIBRANIUM_CORE_SERVER_H
#define VIBRANIUM_CORE_SERVER_H
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
namespace Vibranium{
class Server {
public:
Server(boost::asio::io_context& io_context, short port)
: acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
{
do_accept();
}
private:
void do_accept();
tcp::acceptor acceptor_;
};
}
Server.cpp:
#include "Server.h"
#include "Session.h"
using namespace Vibranium;
void Server::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();
});
}
And Here is how I start the server:
#include "Config.h"
#include "Database/MySQLConnection.h"
#include "Implementation/LoginDatabase.h"
#include "Banner.h"
#include "Server/Server.h"
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
using namespace std;
using namespace Vibranium;
int main() {
//Don't mind Logger::FatalError it's just for coloring!
Banner::Show(Logger::Error,"AuthServer");
Config config("AuthServer");
MySQLConnectionInfo mySqlConnectionInfo(config, "LoginDatabaseInfo");
LoginDatabaseConnection loginDatabaseConnection(mySqlConnectionInfo);
loginDatabaseConnection.LoadDatabase();
try
{
boost::asio::io_context io_context;
Server s(io_context, 8080);
io_context.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "\n";
}
std::cout << "Server Started!" << std::endl;
return 0;
}
I have several questions:
When I start the server I can't see the message "Server Started!". I suspect that is because it is blocking my current thread. How can I start the TCP server in another thread? Should it be in separate thread or ?
The example I have implemented just return back what is sent to the server. Can you suggest any example of how can I distinguish different event messages? What I want to achieve is something like when I sent message starting with 0x001 to point that this is chat message for example or when message starts with 0x002 my server to know that client is informing that somebody has logged off.
I think Boost Assio is using Binary Protocol over TCP. Am I right?
Can you show me how can I send structs over TCP with the binary protocol ?
If questions are too much, please consider only question 1.
1.When I start the server I can't see the message "Server Started!". I suspect that is because it is blocking my current thread. How can I
start the TCP server in another thread? Should it be in separate
thread or ?
std::cout << "Server Started!" << std::endl;
is called after
io_context.run();
io_context.run() will block as long there are any ongoing tasks. You need to call "Server Started" before io_context.run(), probably in Server constructor
2.The example I have implemented just return back what is sent to the server. Can you suggest any example of how can I distinguish different
event messages? What I want to achieve is something like when I sent
message starting with 0x001 to point that this is chat message for
example or when message starts with 0x002 my server to know that
client is informing that somebody has logged off.
There are many ways you could do this, but for ex. you could just set a field of your message to carry this information.
3.I think Boost Assio is using Binary Protocol over TCP. Am I right?
In your case yes, because your are using boost::asio::ip::tcp, but not necessary. There is also boost::asio::ip::udp for udp support.
4.Can you show me how can I send structs over TCP with the binary protocol ?
You could can just implement boost serialization method for your struct
template
void serialize(Archive& ar, const unsigned int version).
See boost example. or provide your own serialization (example to text) and send those data.
If you implement boost serialization, then you can pass your struct direct to asio functions, otherwise you have to send the serialized bytes.
Sorry, but having no time to write the whole code for you.

Io_context error in Boost Library

I'm trying to build a chat room using by Boost Libraries. But when I'm trying to use asio::io_context, the compiler says:
io_context is not an member of asio.
I built Boost 4 times and I thought maybe the problem was due to an installation failure on my part, but it doesn't seem to be.
#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;
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_context& io_context)
{
return pointer(new tcp_connection(io_context));
}
tcp::socket& socket()
{
return socket_;
}
void start()
{
message_ = make_daytime_string();
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_context& io_context)
: socket_(io_context)
{
}
void handle_write(const boost::system::error_code& /*error*/,
size_t /*bytes_transferred*/)
{
}
tcp::socket socket_;
std::string message_;
};
class tcp_server
{
public:
tcp_server(boost::asio::io_context& io_context) //error
: acceptor_(io_context, tcp::endpoint(tcp::v4(), 13)) //error
{
start_accept();
}
Things changed in Boost 1.66:
The release notes show the renamed/changed interfaces:
Boost.Asio now provides the interfaces and functionality specified by the "C++ Extensions for Networking" Technical Specification. In addition to access via the usual Boost.Asio header files, this functionality may be accessed through special headers that correspond to the header files defined in the TS. These are listed in the table below:

boost::asio async_read/async_send are bypassing it's handler

I made a static-lib. And I created this three classes in
Connection Class
#ifndef _CONNECTION_H_
#define _CONNECTION_H_
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
#include <memory>
#include "ByteBuffer.h"
class Connection: public boost::enable_shared_from_this<Connection>
{
public:
typedef boost::shared_ptr<Connection> pointer;
explicit Connection(boost::asio::io_service& io_service);
virtual ~Connection();
boost::asio::ip::tcp::socket& socket();
virtual void OnConnected()=0;
virtual void Send(std::shared_ptr<uint8_t> buffer, int length);
void Receive();
void Disconnect();
bool connected;
protected:
virtual void OnReceived(ByteBuffer &b) = 0;
private:
void handle_Receive(const boost::system::error_code& error, std::size_t bytes_transferred );
void handle_Send(const boost::system::error_code& error, std::size_t bytes_transferred);
boost::asio::ip::tcp::socket socket_;
bool disconnecting;
boost::array<uint8_t, 1000> read_buffer_;
};
#endif
#include "Connection.h"
Connection::Connection(boost::asio::io_service& io_service)
:socket_(io_service),disconnecting(false),connected(false){}
Connection::~Connection(){}
boost::asio::ip::tcp::socket& Connection::socket(){
return socket_;
}
void Connection::Send(std::shared_ptr<uint8_t> buf, int length){
boost::asio::async_write(socket_,boost::asio::buffer(buf.get(),length),
boost::bind(&Connection::handle_Send, shared_from_this(), boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
void Connection::handle_Send(const boost::system::error_code& error, std::size_t bytes_transferred){
}
void Connection::Receive(){
boost::asio::async_read(socket_,boost::asio::buffer(this->read_buffer_),
boost::bind(&Connection::handle_Receive, shared_from_this(),boost::asio::placeholders::error,boost::asio::placeholders::bytes_transferred));
}
void Connection::handle_Receive(const boost::system::error_code& error, std::size_t bytes_transferred)
{
if(!error)
{
if(bytes_transferred <=0){
this->Disconnect();
}else{
ByteBuffer b((std::shared_ptr)this->read_buffer_.data(), this->read_buffer_.size());
this->OnReceived(b);
this->Receive();}
}
}
void Connection::Disconnect()
{
if (!disconnecting) {
boost::system::error_code ec;
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_send,ec);
socket_.close(ec);
disconnecting = true;
std::cout<<"disconnected"<<std::endl;
}
}
ConnectionFactory class
#pragma once
#include "Connection.h"
class ConnectionFactory
{
public:
ConnectionFactory(void);
virtual ~ConnectionFactory(void);
virtual Connection::pointer create(boost::asio::io_service& io_service) = 0;
};
#include "ConnectionFactory.h"
ConnectionFactory::ConnectionFactory(void)
{
}
ConnectionFactory::~ConnectionFactory(void)
{
}
Server Class
#ifndef _SERVER_H_
#define _SERVER_H_
#include "Connection.h"
#include "ConnectionFactory.h"
class Server
{
public:
Server(boost::asio::io_service& io_service , std::string ip,short port,boost::shared_ptr<ConnectionFactory> factory);
~Server();
private:
void start_accept();
void handle_accept(boost::shared_ptr<Connection> conn,const boost::system::error_code& error);
boost::shared_ptr<ConnectionFactory> m_factory;
boost::asio::io_service &io_service;
boost::asio::ip::tcp::acceptor acceptor_;
};
#endif
#include "Server.h"
Server::Server(boost::asio::io_service& io_service,std::string ip,short port,boost::shared_ptr<ConnectionFactory> factory)
:io_service(io_service), acceptor_(io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4::from_string(ip.data()), port)){
m_factory = factory;
start_accept();
std::cout<<"Socket accepting connections..."<<std::endl;
}
Server::~Server()
{
}
void Server::start_accept(){
boost::shared_ptr<Connection> conn = m_factory->create(this->io_service);
acceptor_.async_accept(conn->socket(),
boost::bind(&Server::handle_accept, this,conn,boost::asio::placeholders::error));
}
void Server::handle_accept(boost::shared_ptr<Connection> conn,const boost::system::error_code& error){
if (!error){
std::cout<<"on connected"<<std::endl;
conn->OnConnected();
conn->Receive();
start_accept();
}
//conn->Disconnect();
}
and I drevid from the static-lib and used this classes and it's working perfect
in my main.cpp
#include <iostream>
#include "auth_proto.h"
#include <Server.h>
#include <ConnectionFactory.h>
#include "AuthConnectionFactory.h"
using namespace std;
int main()
{
Auth_Setup();
try
{
boost::asio::io_service io_service;
boost::shared_ptr<ConnectionFactory> fact (new AuthConnectionFactory(io_service));
Server s(io_service,"5.113.195.156",9959,fact);
io_service.run();
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
return 0;
}
I really don't understand what is wrong here , when it comes to the Receive() function it's receive the data from the client but it does not invoking the handle_Receive() method so I can use this data, the behavior that am waiting that it should call the handle_Receive() so I can pass the data to ByteBuffer and use it but that's not happening ......
boost::asio::async_read seems to call the read handler only when it reaches the "amount of data" passed to it.
Quoting boost's 1.46.0 reference:
async_read
Start an asynchronous operation to read a certain amount of data from a stream.
So as a solution, use socket_.async_read_some instead of boost::asio::async_read if you wish to be informed of any arbitrary amount of data received.
Another solution, as Sam Miller was trying to say in the comments, you can add a fixed size header containing the number of bytes incoming before each frame you're supposed to receive, read the header then call boost::asio::async_read with the previously extracted number.

boost asio deadline_timer

I expected the code below to print Hello, world! every 5 seconds, but what happens is that the program pauses for 5 seconds and then prints the message over and over with no subsequent pauses. What am I missing?
#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::asio;
using namespace std;
io_service io;
void print(const boost::system::error_code& /*e*/)
{
cout << "Hello, world!\n";
deadline_timer t(io, boost::posix_time::seconds(5));
t.async_wait(print);
}
int main()
{
deadline_timer t(io, boost::posix_time::seconds(5));
t.async_wait(print);
io.run();
return 0;
}
edit to add working code below. thanks guys.
#include <iostream>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::asio;
using namespace std;
class Deadline {
public:
Deadline(deadline_timer &timer) : t(timer) {
wait();
}
void timeout(const boost::system::error_code &e) {
if (e)
return;
cout << "tick" << endl;
wait();
}
void cancel() {
t.cancel();
}
private:
void wait() {
t.expires_from_now(boost::posix_time::seconds(5));
t.async_wait(boost::bind(&Deadline::timeout, this, boost::asio::placeholders::error));
}
deadline_timer &t;
};
class CancelDeadline {
public:
CancelDeadline(Deadline &d) :dl(d) { }
void operator()() {
string cancel;
cin >> cancel;
dl.cancel();
return;
}
private:
Deadline &dl;
};
int main()
{
io_service io;
deadline_timer t(io);
Deadline d(t);
CancelDeadline cd(d);
boost::thread thr1(cd);
io.run();
return 0;
}
You're creating the deadline_timer as a local variable and then immediately exiting the function. This causes the timer to destruct and cancel itself, and calls your function with an error code which you ignore, causing the infinite loop.
Using a single timer object, stored in a member or global variable, should fix this.
#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace boost::asio;
using namespace std;
io_service io;
deadline_timer t(io, boost::posix_time::seconds(5));
void print(const boost::system::error_code& /*e*/)
{
cout << "Hello, world!\n";
t.expires_from_now(boost::posix_time::seconds(5));
t.async_wait(print);
}
int main()
{
//deadline_timer t(io, boost::posix_time::seconds(5));
t.async_wait(print);
io.run();
return 0;
}
If you look at the error code, you're getting operation cancelled errors.