Boost Buffer trouble String - c++

I'm having troubles wiith socket and packets... Let me explain it
When a client send "Se|Bla|Blu", the output is "Re|Bla|Blu", like I want, but the problem is, that in the consol server, the output is "DataAsString = Se|Bla|BluouhWe are here now Noooo".
So we can see that it doesn't printthe "I am here" in writeAsync(), so we can assume that there is an error, and this is what I want to fix, the pop-up means :
Debug Assertion Failed!
Program: C:\Windows\system32\MSVCP120D.dll File: c:\program files
(x86)\microsoft visual studio 12.0\vc\include\xstring Line: 79
Expression: string iterator not deferencable
For information on how your program can cause an assertion failure,
see the visual C++ documentation on asserts.
(Press Retry to debug the application) [Abandon] [Retry] [ignore]
I tried to debug it, but no way, I don't know where the problem comes from. Any idea ?
Maybe the buffer doesn't allow string ? Maybe I have to reinitialize dataAsString and m_dataToRead to "" just after ?
Thanks in advance, see you.
Edit : The complete :
#include <boost/asio.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string.hpp>
#include <cstdlib>
#include <string>
#include <vector>
#include <iostream>
#include <memory>
#include <utility>
#include "../configuration/constantes.h"
class client : public std::enable_shared_from_this<client>
{
public:
client(boost::asio::ip::tcp::socket socket) : m_socket(std::move(socket)){}
void start()
{
readAsync();
}
private:
void readAsync()
{
auto self(shared_from_this());
m_socket.async_read_some(boost::asio::buffer(m_dataToRead, 512), [this, self](boost::system::error_code error, std::size_t length)
{
if (!error)
{
packetsTreating(m_dataToRead, length);
}
start();
});
}
void writeAsync(std::string m_dataToSend, size_t length)
{
auto self(shared_from_this());
boost::asio::async_write(m_socket, boost::asio::buffer(m_dataToSend, length), [this, self](boost::system::error_code error, std::size_t)
{
if (!error)
{
std::cout << "I am here";
start();
}
});
}
void speak(std::string channel, std::string object)
{
std::cout << "Bouh";
packetLength = 2 + sizeof(canal) + sizeof(objet);
std::cout << "We are here now";
writeAsync(("Re|" + channel + "|" + object), packetLength);
std::cout << "Noooo";
}
void logIn(std::string id, std::string wp)
{
}
void logOut(std::string whatDC)
{
}
void packetsTreating(char* data, size_t length)
{
std::string dataAsString;
dataAsString.assign(data, length);
std::cout << "DataAsString = " << dataAsString;
std::vector<std::string> fields;
boost::split(fields, dataAsString, boost::is_any_of("|"));
if (fields[0] == "Co" && fields.size() == 3)
logIn(fields[1], fields[2]);
else if (fields[0] == "Dc" && fields.size() == 2)
logOut(fields[1]);
else if (fields[0] == "Se" && fields.size() == 3)
speak(fields[1], fields[2]);
else
std::cout << "Unknown command." << std::endl;
}
size_t packetLength = 0;
boost::asio::ip::tcp::socket m_socket;
char m_dataToRead[512];
};
class server
{
public:
server(boost::asio::io_service& ios, short port) : m_acceptor(ios, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)), m_socket(ios)
{
acceptConnection();
}
private:
void acceptConnection()
{
m_acceptor.async_accept(m_socket, [this](boost::system::error_code error)
{
if (!error && connectedPerso <= maxConnec) // maxConnec is in constantes.h, = 250
{
connectedPerso++; // Btw, do you know how to -- it when a client disconnect ?
std::cout << "Connection, there is " << connectedPerso << " clients." << std::endl;
std::make_shared<client>(std::move(m_socket))->start();
}
acceptConnection();
});
}
unsigned short connectedPerso = 0;
boost::asio::ip::tcp::acceptor m_acceptor;
boost::asio::ip::tcp::socket m_socket;
};
void main()
{
try
{
std::cout << "TCP open on port " << port << ". maxConnec is " << maxConnec << "." << std::endl;
boost::asio::io_service iosConnector;
serveur serveur(iosConnector, port); // port = 2013
iosConnector.run();
}
catch (std::exception& e)
{
std::cerr << "Exception : " << e.what() << "\n";
}
}

There is a logic error. You are calling start() from both the sync write handler and the sync read handler. Since start() calls async_read on the socket, you will end up having 2 active async reads on the socket after the first write. This is illegal.

answer supplied to demonstrate that the code you posted seemed to work on my system:
$ telnet localhost 2013
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello
hello
ccccc
Re|1|hello|
Se|channel|text
Re|channel|text
Se|channel|text
Re|channel|text
Se|channel|text
Re|channel|text
^]
telnet> close
Connection closed.
stdout:
Connection, there is 1 clients.
DataAsString = hello
Unknown command.
DataAsString = hello
Unknown command.
DataAsString = ccccc
Unknown command.
DataAsString = Re|1|hello|
Unknown command.
DataAsString = Se|channel|text
BouhWe are here nowNooooI am hereDataAsString = Se|channel|text
BouhWe are here nowNooooI am hereDataAsString = Se|channel|text

Related

Boost ASIO performing async write/read/write handshake with a timer

I have an application where I need to connect to a socket, send a handshake message (send command1, get response, send command2), and then receive data. It is set to expire after a timeout, stop the io_service, and then attempt to reconnect. There is no error message when I do my first async_write but the following async_read waits until the timer expires, and then reconnects in an infinite loop.
My code looks like:
#include <boost/asio.hpp>
#include <boost/bind/bind.hpp>
#include <iostream>
#include <string>
#include <memory>
#include <boost/date_time/posix_time/posix_time.hpp>
using namespace std;
using boost::asio::ip::tcp;
static shared_ptr<boost::asio::io_service> _ios;
static shared_ptr<boost::asio::deadline_timer> timer;
static shared_ptr<boost::asio::ip::tcp::socket> tcp_sock;
static shared_ptr<tcp::resolver> _resolver;
static boost::asio::ip::tcp::resolver::results_type eps;
string buffer(1024,0);
void handle_read(const boost::system::error_code& ec, size_t bytes)
{
if (ec)
{
cout << "error: " << ec.message() << endl;
_ios->stop();
return;
}
// got first response, send off reply
if (buffer == "response")
{
boost::asio::async_write(*tcp_sock, boost::asio::buffer("command2",7),
[](auto ec, auto bytes)
{
if (ec)
{
cout << "write error: " << ec.message() << endl;
_ios->stop();
return;
}
});
}
else
{
// parse incoming data
}
// attempt next read
timer->expires_from_now(boost::posix_time::seconds(10));
boost::asio::async_read(*tcp_sock, boost::asio::buffer(buffer,buffer.size()), handle_read);
}
void get_response()
{
timer->expires_from_now(boost::posix_time::seconds(10));
boost::asio::async_read(*tcp_sock, boost::asio::buffer(buffer,buffer.size()), handle_read);
}
void on_connected(const boost::system::error_code& ec, tcp::endpoint)
{
if (!tcp_sock->is_open())
{
cout << "socket is not open" << endl;
_ios->stop();
}
else if (ec)
{
cout << "error: " << ec.message() << endl;
_ios->stop();
return;
}
else
{
cout << "connected" << endl;
// do handshake (no errors?)
boost::asio::async_write(*tcp_sock, boost::asio::buffer("command1",7),
[](auto ec, auto bytes)
{
if (ec)
{
cout << "write error: " << ec.message() << endl;
_ios->stop();
return;
}
get_response();
});
}
}
void check_timer()
{
if (timer->expires_at() <= boost::asio::deadline_timer::traits_type::now())
{
tcp_sock->close();
timer->expires_at(boost::posix_time::pos_infin);
}
timer->async_wait(boost::bind(check_deadline));
}
void init(string ip, string port)
{
// set/reset data and connect
_resolver.reset(new tcp::resolver(*_ios));
eps = _resolver->resolve(ip, port);
timer.reset(new boost::asio::deadline_timer(*_ios));
tcp_sock.reset(new boost::asio::ip::tcp::socket(*_ios));
timer->expires_from_now(boost::posix_time::seconds(5));
// start async connect
boost::asio::async_connect(*tcp_sock, eps, on_connected);
timer->async_wait(boost::bind(check_timer));
}
int main(int argc, char** argv)
{
while (1)
{
// start new io context
_ios.reset(new boost::asio::io_service);
init(argv[1],argv[2]);
_ios->run();
cout << "try reconnect" << endl;
}
return 0;
}
Why would I be timing out? When I do a netcat and follow the same procedure things look ok. I get no errors from the async_write indicating that there are any errors and I am making sure to not call the async_read for the response until I am in the write handler.
Others have been spot on. You use "blanket" read, which means it only completes at error (like EOF) or when the buffer is full (docs)
Besides your code is over-complicated (excess dynamic allocation, manual new, globals, etc).
The following simplified/cleaned up version still exhibits your problem: http://coliru.stacked-crooked.com/a/8f5d0820b3cee186
Since it looks like you just want to limit over-all time of the request, I'd suggest dropping the timer and just limit the time to run the io_context.
Also showing how to use '\n' for message delimiter and avoid manually managing dynamic buffers:
Live On Coliru
#include <boost/asio.hpp>
#include <iomanip>
#include <iostream>
#include <memory>
#include <string>
namespace asio = boost::asio;
using asio::ip::tcp;
using boost::system::error_code;
using namespace std::literals;
struct Client {
#define HANDLE(memfun) std::bind(&Client::memfun, this, std::placeholders::_1, std::placeholders::_2)
Client(std::string const& ip, std::string const& port) {
async_connect(_sock, tcp::resolver{_ios}.resolve(ip, port), HANDLE(on_connected));
}
void run() { _ios.run_for(10s); }
private:
asio::io_service _ios;
asio::ip::tcp::socket _sock{_ios};
std::string _buffer;
void on_connected(error_code ec, tcp::endpoint) {
std::cout << "on_connected: " << ec.message() << std::endl;
if (ec)
return;
async_write(_sock, asio::buffer("command1\n"sv), [this](error_code ec, size_t) {
std::cout << "write: " << ec.message() << std::endl;
if (!ec)
get_response();
});
}
void get_response() {
async_read_until(_sock, asio::dynamic_buffer(_buffer /*, 1024*/), "\n", HANDLE(on_read));
}
void on_read(error_code ec, size_t bytes) {
std::cout << "handle_read: " << ec.message() << " " << bytes << std::endl;
if (ec)
return;
auto cmd = _buffer.substr(0, bytes);
_buffer.erase(0, bytes);
// got first response, send off reply
std::cout << "Handling command " << quoted(cmd) << std::endl;
if (cmd == "response\n") {
async_write(_sock, asio::buffer("command2\n"sv), [](error_code ec, size_t) {
std::cout << "write2: " << ec.message() << std::endl;
});
} else {
// TODO parse cmd
}
get_response(); // attempt next read
}
};
int main(int argc, char** argv) {
assert(argc == 3);
while (1) {
Client(argv[1], argv[2]).run();
std::this_thread::sleep_for(1s); // for demo on COLIRU
std::cout << "try reconnect" << std::endl;
}
}
With output live on coliru:
on_connected: Connection refused
try reconnect
on_connected: Success
write: Success
command1
handle_read: Success 4
Handling command "one
"
handle_read: Success 9
Handling command "response
"
write2: Success
command2
handle_read: Success 6
Handling command "three
"
handle_read: End of file 0
try reconnect
on_connected: Success
write: Success
command1
Local interactive demo:
Sidenote: as long as resolve() isn't happening asynchronously it will not be subject to the timeouts.

Crash : terminate called after throwing an instance of 'std::system_error' what(): Resource deadlock avoided

I have a simple client /server application the code of which is mentioned below.
Please run the server in one shell and the client in another shell in linux.
First start the server and then the client.
When the server is done with it's work, it crashes with following exception:
terminate called after throwing an instance of 'std::system_error'
what(): Resource deadlock avoided
This happens from the line m_thread->join() from inside the function Service::HandleClient
I have no clue on what's going on.. Can someone please check the code.. I just want that the server application should also get closed correctly the way the client application got closed.
**Server Code : **
#include <boost/asio.hpp>
#include <thread>
#include <atomic>
#include <memory>
#include <iostream>
using namespace boost;
class Service {
public:
Service(){}
void StartHandligClient(
std::shared_ptr<asio::ip::tcp::socket> sock) {
m_thread.reset(new std::thread (([this, sock]() {
HandleClient(sock);
})) );
}
private:
void HandleClient(std::shared_ptr<asio::ip::tcp::socket> sock) {
while(1)
{
try {
asio::streambuf request;
std::cout << "Waiting to read \n";
asio::read_until(*sock.get(), request, '\n');
std::string s( (std::istreambuf_iterator<char>(&request)), std::istreambuf_iterator<char>() );
std::cout << "Server got : " << s << "\n";
// Emulate request processing.
int i = 0;
while (i != 1000000)
i++;
std::this_thread::sleep_for(
std::chrono::milliseconds(500));
// Sending response.
std::string response = "Response\n";
asio::write(*sock.get(), asio::buffer(response));
}
catch (system::system_error &e) {
boost::system::error_code ec = e.code();
if(ec == asio::error::eof)
{
std::cout << "Breaking loop \n";
break;
}
std::cout << "Error occured! Error code = "
<< e.code() << ". Message: "
<< e.what();
}
}
m_thread->join();
// Clean-up.
delete this;
}
std::unique_ptr<std::thread> m_thread;
};
class Acceptor {
public:
Acceptor(asio::io_service& ios, unsigned short port_num) :
m_ios(ios),
m_acceptor(m_ios,
asio::ip::tcp::endpoint(
asio::ip::address_v4::any(),
port_num))
{
m_acceptor.listen();
}
void Accept() {
std::cout << "Server Accept() \n" << std::flush;
std::shared_ptr<asio::ip::tcp::socket>
sock(new asio::ip::tcp::socket(m_ios));
std::cout << "BEFORE calling acceptor's accept function \n" << std::flush;
m_acceptor.accept(*sock.get());
std::cout << "AFTER calling acceptor's accept function \n" << std::flush;
(new Service)->StartHandligClient(sock);
}
void close()
{
std::cout << "Inside Acceptor.close() \n" << std::flush;
m_acceptor.close();
}
private:
asio::io_service& m_ios;
asio::ip::tcp::acceptor m_acceptor;
};
class Server {
public:
Server() : m_stop(false) {}
void Start(unsigned short port_num) {
m_thread.reset(new std::thread([this, port_num]() {
Run(port_num);
}));
}
void Stop() {
m_stop.store(true);
m_thread->join();
}
private:
void Run(unsigned short port_num) {
Acceptor acc(m_ios, port_num);
while (!m_stop.load()) {
std::cout << "Server accept\n" << std::flush;
acc.Accept();
}
acc.close();
}
std::unique_ptr<std::thread> m_thread;
std::atomic<bool> m_stop;
asio::io_service m_ios;
};
int main()
{
unsigned short port_num = 3333;
try {
Server srv;
srv.Start(port_num);
std::this_thread::sleep_for(std::chrono::seconds(4));
srv.Stop();
}
catch (system::system_error &e) {
std::cout << "Error occured! Error code = "
<< e.code() << ". Message: "
<< e.what();
}
return 0;
}
**Client Code : **
#include <boost/asio.hpp>
#include <iostream>
using namespace boost;
class SyncTCPClient {
public:
SyncTCPClient(const std::string& raw_ip_address,
unsigned short port_num) :
m_ep(asio::ip::address::from_string(raw_ip_address),
port_num),
m_sock(m_ios) {
m_sock.open(m_ep.protocol());
}
void connect() {
m_sock.connect(m_ep);
}
void close() {
m_sock.shutdown(
boost::asio::ip::tcp::socket::shutdown_both);
m_sock.close();
}
std::string emulateLongComputationOp(
unsigned int duration_sec) {
std::string request = "EMULATE_LONG_COMP_OP "
+ std::to_string(duration_sec)
+ "\n";
sendRequest(request);
return receiveResponse();
};
private:
void sendRequest(const std::string& request)
{
std::cout << "Inside sendRequest : " << request << "\n";
asio::write(m_sock, asio::buffer(request));
}
std::string receiveResponse() {
asio::streambuf buf;
asio::read_until(m_sock, buf, '\n');
std::istream input(&buf);
std::string response;
std::getline(input, response);
return response;
}
private:
asio::io_service m_ios;
asio::ip::tcp::endpoint m_ep;
asio::ip::tcp::socket m_sock;
};
int main()
{
const std::string raw_ip_address = "127.0.0.1";
const unsigned short port_num = 3333;
try {
SyncTCPClient client(raw_ip_address, port_num);
// Sync connect.
client.connect();
std::cout << "Sending request to the server... " << std::endl;
std::string response = client.emulateLongComputationOp(10);
std::cout << "Response received: " << response << std::endl;
sleep(2);
// Close the connection and free resources.
client.close();
}
catch (system::system_error &e) {
std::cout << "Error occured! Error code = " << e.code()
<< ". Message: " << e.what();
return e.code().value();
}
return 0;
}
#sehe .. can you run the code and let me know how to overcome the crash that I mentioned ? – Nishant Sharma
Actually, no I won't. The problem has already been analyzed: you can't join the current thread (it would deadlock).
But I can do something better:
Grabbing my crystal ball, I can guess you got this example from a particular book, named Boost.Asio C++ Network Programming Cookbook¹, around page 139.
I recognized it after a while when I added up all the code smells (delete this and m_stop.load() tipped me over the edge).
The good news is, I reviewed that code before:
ASIO example code closing socket before it should
You can probably profit from the particular comments I made there.
¹ from packtpub: https://www.packtpub.com/application-development/boostasio-c-network-programming-cookbook

`boost::asio::write` with a timeout [duplicate]

I´m trying to build a synchronous FTP client code with timeout using a thread as the timeout control. The thread will be started on every transaction and will close the socket in case of timeout - that will force the syncronous call to return with error.
So here is my code:
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <thread>
#include <chrono>
#include <boost/asio.hpp>
#define TIMEOUT_SECONDS 5
#define MAX_MESSAGE_SIZE 4096
using boost::asio::ip::tcp;
enum { max_length = 1024 };
bool timerOn;
void socket_timer(tcp::socket& s, int seconds)
{
std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
while (timerOn)
{
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
auto interval = std::chrono::duration_cast<std::chrono::seconds>(now - start).count();
if (interval > seconds)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Not to run in 100% CPU
}
if (timerOn)
s.close();
}
void start_timer(int seconds, tcp::socket& s)
{
timerOn = true;
std::thread t(socket_timer, s, seconds);
t.detach();
}
void stop_timer()
{
timerOn = false;
}
int main(int argc, char* argv[])
{
std::string address;
while(address != "END")
{
try
{
boost::asio::io_service io_service;
std::cout << "Enter FTP server address to connect or END to finish: " << std::endl;
std::cin >> address;
if (address == "END")
break;
tcp::socket s(io_service);
tcp::resolver resolver(io_service);
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(address), 21);
start_timer(TIMEOUT_SECONDS, s);
boost::system::error_code ec;
s.connect(endpoint, ec);
stop_timer();
if (ec)
{
throw std::runtime_error("Error connecting to server.");
}
std::cout << "Connected to " << s.remote_endpoint().address().to_string() << std::endl;
char reply[max_length];
start_timer(TIMEOUT_SECONDS, s);
size_t bytes = s.receive(boost::asio::buffer(reply, MAX_MESSAGE_SIZE), 0, ec);
stop_timer();
if (ec)
{
throw std::runtime_error("Error receiving message.");
}
std::cout << "Received message is: ";
std::cout.write(reply, bytes);
std::cout << "\n";
std::cout << "Enter message: ";
char request[max_length];
std::cin.getline(request, max_length);
size_t request_length = std::strlen(request);
start_timer(TIMEOUT_SECONDS, s);
boost::asio::write(s, boost::asio::buffer(request, request_length));
stop_timer();
if (ec)
{
throw std::runtime_error("Error sending message.");
}
}
catch (std::exception& e)
{
std::cerr << "COMMUNICATIONS ERROR." << "\n";
std::cerr << "Exception: " << e.what() << "\n";
}
}
return 0;
}
I simply cannot compile this code, as boost is showing me the following error:
1>------ Build started: Project: TestAsio, Configuration: Debug Win32 ------
1> main.cpp
1>c:\boost_1_60\boost\asio\basic_socket.hpp(1513): error C2248: 'boost::asio::basic_io_object<IoObjectService>::basic_io_object' : cannot access private member declared in class 'boost::asio::basic_io_object<IoObjectService>'
1> with
1> [
1> IoObjectService=boost::asio::stream_socket_service<boost::asio::ip::tcp>
1> ]
1> c:\boost_1_60\boost\asio\basic_io_object.hpp(230) : see declaration of 'boost::asio::basic_io_object<IoObjectService>::basic_io_object'
1> with
1> [
1> IoObjectService=boost::asio::stream_socket_service<boost::asio::ip::tcp>
1> ]
1> This diagnostic occurred in the compiler generated function 'boost::asio::basic_socket<Protocol,SocketService>::basic_socket(const boost::asio::basic_socket<Protocol,SocketService> &)'
1> with
1> [
1> Protocol=boost::asio::ip::tcp,
1> SocketService=boost::asio::stream_socket_service<boost::asio::ip::tcp>
1> ]
========== Build: 0 succeeded, 1 failed, 9 up-to-date, 0 skipped ==========
So, I wanna know about 2 things:
a) What am I doing wrong in the code ?
b) Will this approach of closing the socket on a parallel thread work for timing out the socket ? Please fell free to comment it.
Thanks for helping.
I've made a helper facility to do any Asio async operation "synchronously" with a timeout here, look for await_operation:
boost::asio + std::future - Access violation after closing socket
You should be able to adapt the pattern for your sample.
Demo
It took a while since I wanted to test this with an ftp server.
Notes:
you didn't resolve the address (effectively requiring the user to type in IP address)
you didn't make sure commands were closed with newline
you didn't handle any kind of input error
Fixing these things and using my await_operation you'd get this:
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <thread>
#include <chrono>
#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#define TIMEOUT std::chrono::seconds(5)
#define MAX_MESSAGE_SIZE 4096
using boost::asio::ip::tcp;
enum { max_length = 2048 };
struct Service {
using error_code = boost::system::error_code;
template<typename AllowTime, typename Cancel> void await_operation_ex(AllowTime const& deadline_or_duration, Cancel&& cancel) {
using namespace boost::asio;
ioservice.reset();
{
high_resolution_timer tm(ioservice, deadline_or_duration);
tm.async_wait([&cancel](error_code ec) { if (ec != error::operation_aborted) std::forward<Cancel>(cancel)(); });
ioservice.run_one();
}
ioservice.run();
}
template<typename AllowTime, typename ServiceObject> void await_operation(AllowTime const& deadline_or_duration, ServiceObject& so) {
return await_operation_ex(deadline_or_duration, [&so]{ so.cancel(); });
}
boost::asio::io_service ioservice;
};
int main()
{
while(true)
{
try
{
Service service;
std::cout << "Enter FTP server address to connect or END to finish: " << std::endl;
std::string address;
if (std::cin >> address) {
if (address == "END") break;
} else {
if (std::cin.eof())
break;
std::cerr << "Invalid input ignored\n";
std::cin.clear();
std::cin.ignore(1024, '\n');
continue;
}
tcp::socket s(service.ioservice);
tcp::resolver resolver(service.ioservice);
boost::asio::async_connect(s, resolver.resolve({address, "21"}), [](boost::system::error_code ec, tcp::resolver::iterator it) {
if (ec) throw std::runtime_error("Error connecting to server: " + ec.message());
std::cout << "Connected to " << it->endpoint() << std::endl;
});
service.await_operation_ex(TIMEOUT, [&]{
throw std::runtime_error("Error connecting to server: timeout\n");
});
auto receive = [&] {
boost::asio::streambuf sb;
size_t bytes;
boost::asio::async_read_until(s, sb, '\n', [&](boost::system::error_code ec, size_t bytes_transferred) {
if (ec) throw std::runtime_error("Error receiving message: " + ec.message());
bytes = bytes_transferred;
std::cout << "Received message is: " << &sb;
});
service.await_operation(TIMEOUT, s);
return bytes;
};
receive(); // banner
auto send = [&](std::string cmd) {
boost::asio::async_write(s, boost::asio::buffer(cmd), [](boost::system::error_code ec, size_t /*bytes_transferred*/) {
if (ec) throw std::runtime_error("Error sending message: " + ec.message());
});
service.await_operation(TIMEOUT, s);
};
auto ftp_command = [&](std::string cmd) {
send(cmd + "\r\n");
receive(); // response
};
//ftp_command("USER bob");
//ftp_command("PASS hello");
while (true) {
std::cout << "Enter command: ";
std::string request;
if (!std::getline(std::cin, request))
break;
ftp_command(request);
}
}
catch (std::exception const& e)
{
std::cerr << "COMMUNICATIONS ERROR " << e.what() << "\n";
}
}
return 0;
}
Which, in my test run, prints e.g.:

Boost async_read_until with timeout and bloking read [duplicate]

I´m trying to build a synchronous FTP client code with timeout using a thread as the timeout control. The thread will be started on every transaction and will close the socket in case of timeout - that will force the syncronous call to return with error.
So here is my code:
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <thread>
#include <chrono>
#include <boost/asio.hpp>
#define TIMEOUT_SECONDS 5
#define MAX_MESSAGE_SIZE 4096
using boost::asio::ip::tcp;
enum { max_length = 1024 };
bool timerOn;
void socket_timer(tcp::socket& s, int seconds)
{
std::chrono::system_clock::time_point start = std::chrono::system_clock::now();
while (timerOn)
{
std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
auto interval = std::chrono::duration_cast<std::chrono::seconds>(now - start).count();
if (interval > seconds)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Not to run in 100% CPU
}
if (timerOn)
s.close();
}
void start_timer(int seconds, tcp::socket& s)
{
timerOn = true;
std::thread t(socket_timer, s, seconds);
t.detach();
}
void stop_timer()
{
timerOn = false;
}
int main(int argc, char* argv[])
{
std::string address;
while(address != "END")
{
try
{
boost::asio::io_service io_service;
std::cout << "Enter FTP server address to connect or END to finish: " << std::endl;
std::cin >> address;
if (address == "END")
break;
tcp::socket s(io_service);
tcp::resolver resolver(io_service);
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(address), 21);
start_timer(TIMEOUT_SECONDS, s);
boost::system::error_code ec;
s.connect(endpoint, ec);
stop_timer();
if (ec)
{
throw std::runtime_error("Error connecting to server.");
}
std::cout << "Connected to " << s.remote_endpoint().address().to_string() << std::endl;
char reply[max_length];
start_timer(TIMEOUT_SECONDS, s);
size_t bytes = s.receive(boost::asio::buffer(reply, MAX_MESSAGE_SIZE), 0, ec);
stop_timer();
if (ec)
{
throw std::runtime_error("Error receiving message.");
}
std::cout << "Received message is: ";
std::cout.write(reply, bytes);
std::cout << "\n";
std::cout << "Enter message: ";
char request[max_length];
std::cin.getline(request, max_length);
size_t request_length = std::strlen(request);
start_timer(TIMEOUT_SECONDS, s);
boost::asio::write(s, boost::asio::buffer(request, request_length));
stop_timer();
if (ec)
{
throw std::runtime_error("Error sending message.");
}
}
catch (std::exception& e)
{
std::cerr << "COMMUNICATIONS ERROR." << "\n";
std::cerr << "Exception: " << e.what() << "\n";
}
}
return 0;
}
I simply cannot compile this code, as boost is showing me the following error:
1>------ Build started: Project: TestAsio, Configuration: Debug Win32 ------
1> main.cpp
1>c:\boost_1_60\boost\asio\basic_socket.hpp(1513): error C2248: 'boost::asio::basic_io_object<IoObjectService>::basic_io_object' : cannot access private member declared in class 'boost::asio::basic_io_object<IoObjectService>'
1> with
1> [
1> IoObjectService=boost::asio::stream_socket_service<boost::asio::ip::tcp>
1> ]
1> c:\boost_1_60\boost\asio\basic_io_object.hpp(230) : see declaration of 'boost::asio::basic_io_object<IoObjectService>::basic_io_object'
1> with
1> [
1> IoObjectService=boost::asio::stream_socket_service<boost::asio::ip::tcp>
1> ]
1> This diagnostic occurred in the compiler generated function 'boost::asio::basic_socket<Protocol,SocketService>::basic_socket(const boost::asio::basic_socket<Protocol,SocketService> &)'
1> with
1> [
1> Protocol=boost::asio::ip::tcp,
1> SocketService=boost::asio::stream_socket_service<boost::asio::ip::tcp>
1> ]
========== Build: 0 succeeded, 1 failed, 9 up-to-date, 0 skipped ==========
So, I wanna know about 2 things:
a) What am I doing wrong in the code ?
b) Will this approach of closing the socket on a parallel thread work for timing out the socket ? Please fell free to comment it.
Thanks for helping.
I've made a helper facility to do any Asio async operation "synchronously" with a timeout here, look for await_operation:
boost::asio + std::future - Access violation after closing socket
You should be able to adapt the pattern for your sample.
Demo
It took a while since I wanted to test this with an ftp server.
Notes:
you didn't resolve the address (effectively requiring the user to type in IP address)
you didn't make sure commands were closed with newline
you didn't handle any kind of input error
Fixing these things and using my await_operation you'd get this:
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <thread>
#include <chrono>
#include <boost/asio.hpp>
#include <boost/asio/high_resolution_timer.hpp>
#define TIMEOUT std::chrono::seconds(5)
#define MAX_MESSAGE_SIZE 4096
using boost::asio::ip::tcp;
enum { max_length = 2048 };
struct Service {
using error_code = boost::system::error_code;
template<typename AllowTime, typename Cancel> void await_operation_ex(AllowTime const& deadline_or_duration, Cancel&& cancel) {
using namespace boost::asio;
ioservice.reset();
{
high_resolution_timer tm(ioservice, deadline_or_duration);
tm.async_wait([&cancel](error_code ec) { if (ec != error::operation_aborted) std::forward<Cancel>(cancel)(); });
ioservice.run_one();
}
ioservice.run();
}
template<typename AllowTime, typename ServiceObject> void await_operation(AllowTime const& deadline_or_duration, ServiceObject& so) {
return await_operation_ex(deadline_or_duration, [&so]{ so.cancel(); });
}
boost::asio::io_service ioservice;
};
int main()
{
while(true)
{
try
{
Service service;
std::cout << "Enter FTP server address to connect or END to finish: " << std::endl;
std::string address;
if (std::cin >> address) {
if (address == "END") break;
} else {
if (std::cin.eof())
break;
std::cerr << "Invalid input ignored\n";
std::cin.clear();
std::cin.ignore(1024, '\n');
continue;
}
tcp::socket s(service.ioservice);
tcp::resolver resolver(service.ioservice);
boost::asio::async_connect(s, resolver.resolve({address, "21"}), [](boost::system::error_code ec, tcp::resolver::iterator it) {
if (ec) throw std::runtime_error("Error connecting to server: " + ec.message());
std::cout << "Connected to " << it->endpoint() << std::endl;
});
service.await_operation_ex(TIMEOUT, [&]{
throw std::runtime_error("Error connecting to server: timeout\n");
});
auto receive = [&] {
boost::asio::streambuf sb;
size_t bytes;
boost::asio::async_read_until(s, sb, '\n', [&](boost::system::error_code ec, size_t bytes_transferred) {
if (ec) throw std::runtime_error("Error receiving message: " + ec.message());
bytes = bytes_transferred;
std::cout << "Received message is: " << &sb;
});
service.await_operation(TIMEOUT, s);
return bytes;
};
receive(); // banner
auto send = [&](std::string cmd) {
boost::asio::async_write(s, boost::asio::buffer(cmd), [](boost::system::error_code ec, size_t /*bytes_transferred*/) {
if (ec) throw std::runtime_error("Error sending message: " + ec.message());
});
service.await_operation(TIMEOUT, s);
};
auto ftp_command = [&](std::string cmd) {
send(cmd + "\r\n");
receive(); // response
};
//ftp_command("USER bob");
//ftp_command("PASS hello");
while (true) {
std::cout << "Enter command: ";
std::string request;
if (!std::getline(std::cin, request))
break;
ftp_command(request);
}
}
catch (std::exception const& e)
{
std::cerr << "COMMUNICATIONS ERROR " << e.what() << "\n";
}
}
return 0;
}
Which, in my test run, prints e.g.:

Boost::Asio - get_io_service exception thrown

Can anyone explain to me why when I want to call get_io_service() I get the following exception?
I see that at startup acceptor is initialized but when client wants to connect and server wants to open new connection then acceptor has some random numbers. I don't know why this is happening.
My code:
main.cpp
#include "TServer.h"
#include "TDatabase.h"
#include "Includes.h"
#include "Structures.h"
int main()
{
try
{
std::cout << "========================================" << std::endl
<< "= Game Server v1.0 by Gravity1 =" << std::endl
<< "========================================" << std::endl;
boost::asio::io_service io_service;
Database database;
std::vector<std::vector<TServer>> Server;
srand(time(0));
boost::property_tree::ptree pt;
boost::property_tree::ini_parser::read_ini("game_server_config.ini", pt);
database.host = pt.get<std::string>("DATABASE.HOST");
database.username = pt.get<std::string>("DATABASE.USER");
database.password = pt.get<std::string>("DATABASE.PASS");
database.schema = pt.get<std::string>("DATABASE.SCHEMA");
std::shared_ptr<TDatabase> Database_ptr = std::make_shared<TDatabase>(database);
Database_ptr->Connect();
short server_count = pt.get<short>("GAME_SERVER.SERVER_COUNT");
if (server_count > 0)
Server.resize(server_count);
for (int i = 0; i < server_count; i++)
{
short channel_count = pt.get<short>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL_COUNT");
for (int j = 0; j < channel_count; j++)
{
Canal CanalTemp;
CanalTemp.ip = pt.get<std::string>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL" + std::to_string(j + 1) + "_IP");
CanalTemp.port = pt.get<short>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL" + std::to_string(j + 1) + "_PORT");
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string(CanalTemp.ip), CanalTemp.port);
Server[i].emplace_back(io_service, Database_ptr,endpoint);
}
}
io_service.run();
}
catch (std::exception &e)
{
std::cerr << e.what() << std::endl;
}
std::cin.get();
return 0;
}
TServer.cpp
TServer::TServer(boost::asio::io_service &io_service,std::shared_ptr<TDatabase> database, const boost::asio::ip::tcp::endpoint &endpoint) :
acceptor(io_service,endpoint)
{
Accept_Connection();
}
void TServer::Accept_Connection()
{
Connection = std::make_shared<TSession>(acceptor.get_io_service(),Database);
acceptor.async_accept(*(Connection->Socket()),(boost::bind(&TServer::Handle_Connection, this, Connection, boost::asio::placeholders::error)));
}
void TServer::Handle_Connection(std::shared_ptr<TSession> Connection, const boost::system::error_code &error)
{
if (!error)
{
Connection->Start();
Accept_Connection();
}
}
The problem is quite simple.
You're emplacing TServers to the back of a vector. When you do, it will (may) reallocate, invalidating the references that are held in other parts of your program. See Iterator invalidation rules
In your case, such a reference is immediately held, because Accept_Connection() is called from within the constructor and it binds to the this pointer. Remember, the this pointer points to the address of a TServer element inside the vector.
OOPS. When your completion handler fires, the element is/may have been reallocated. So the pointer is simply dangling and you have Undefined Behaviour.
You can fix it in different ways:
replace the vectors with a container that guarantees stability of reference on insertion. For example, you can just use a list<> instead:
std::list<std::list<TServer> > servers;
if (server_count > 0)
servers.resize(server_count);
auto current_server = servers.begin();
for (int i = 0; i < server_count; i++, ++current_server) {
short channel_count = pt.get<short>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL_COUNT");
for (int j = 0; j < channel_count; j++) {
Canal CanalTemp;
CanalTemp.ip = pt.get<std::string>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL" + std::to_string(j + 1) + "_IP");
CanalTemp.port = pt.get<short>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL" + std::to_string(j + 1) + "_PORT");
tcp::endpoint endpoint(boost::asio::ip::address::from_string(CanalTemp.ip), CanalTemp.port);
current_server->emplace_back(io_service, Database_ptr, endpoint);
}
}
Alternatively, you can postpone the initial bind until after all the channels were added to all servers:
TServer(boost::asio::io_service &io_service, std::shared_ptr<TDatabase> database, const boost::asio::ip::tcp::endpoint &endpoint)
: acceptor(io_service, endpoint), database(database)
{
//Accept_Connection();
}
And do that explicitly before io_service::run():
for(auto& server: servers)
for(auto& channel: server)
channel.Accept_Connection();
io_service.run();
Note: In fact, in idiomatic Asio code, running asynchronous operations directly from within a constructor is frequently not possible. Look e.g. at the TSession type; it couldn't bind a completion handler to a member function because shared_from_this() is not allowed from within the constructor ("Note that prior to calling shared_from_this on an object t, there must be a shared_ptr that owns t.").
Both work. I opt for the first here:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/make_shared.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/bind.hpp>
#include <iostream>
// for iterator and reference stability (see:
// https://stackoverflow.com/questions/6438086/iterator-invalidation-rules)
#include <list>
using tcp = boost::asio::ip::tcp;
struct Canal {
std::string ip;
int port;
};
struct Database {
std::string host, username, password, schema;
};
struct TDatabase {
TDatabase(Database config) : details(config) {}
void Connect() {
std::cout
<< "Connecting to fake database " << details.host << "/" << details.schema
<< " with user " << details.username << " and password '" << std::string(details.password.size(), '*') << "'\n";
}
private:
Database details;
};
struct TSession : std::enable_shared_from_this<TSession> {
TSession(boost::asio::io_service& svc, std::shared_ptr<TDatabase> db) :
_svc(svc), _socket(_svc), _db(db) {}
tcp::socket& Socket() { return _socket; }
void Start() {
boost::asio::async_read(_socket, _sb,
boost::bind(&TSession::HandleReceived, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void HandleReceived(boost::system::error_code ec, size_t bytes_transferred) {
if (!ec || boost::asio::error::eof == ec) {
std::cout << "Received from " << _socket.remote_endpoint() << ": '" << &_sb << "'\n";
} else
{
std::cout << "Error reading from peer: " << ec.message() << "\n";
}
}
private:
boost::asio::io_service& _svc;
tcp::socket _socket;
std::shared_ptr<TDatabase> _db;
boost::asio::streambuf _sb;
};
struct TServer {
tcp::acceptor acceptor;
std::shared_ptr<TDatabase> database;
TServer(boost::asio::io_service &io_service, std::shared_ptr<TDatabase> database, const boost::asio::ip::tcp::endpoint &endpoint)
: acceptor(io_service, endpoint), database(database)
{
Accept_Connection();
}
void Accept_Connection() {
auto Connection = std::make_shared<TSession>(acceptor.get_io_service(), database);
acceptor.async_accept(Connection->Socket(),
boost::bind(&TServer::Handle_Connection, this, Connection, boost::asio::placeholders::error));
}
void Handle_Connection(std::shared_ptr<TSession> Connection, const boost::system::error_code &error) {
if (!error) {
Connection->Start();
Accept_Connection();
} else
std::cout << "Error: " << error.message() << "\n";
}
};
//#include "TServer.h"
//#include "TDatabase.h"
//#include "Includes.h"
//#include "Structures.h"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ini_parser.hpp>
int main() {
try {
std::cout << "========================================" << std::endl
<< "= Game Server v1.0 by Gravity1 =" << std::endl
<< "========================================" << std::endl;
boost::asio::io_service io_service;
Database database;
std::list<std::list<TServer> > servers;
srand(time(0));
boost::property_tree::ptree pt;
boost::property_tree::read_ini("game_server_config.ini", pt);
database.host = pt.get<std::string>("DATABASE.HOST");
database.username = pt.get<std::string>("DATABASE.USER");
database.password = pt.get<std::string>("DATABASE.PASS");
database.schema = pt.get<std::string>("DATABASE.SCHEMA");
std::shared_ptr<TDatabase> Database_ptr = std::make_shared<TDatabase>(database);
Database_ptr->Connect();
short server_count = pt.get<short>("GAME_SERVER.SERVER_COUNT");
if (server_count > 0)
servers.resize(server_count);
auto current_server = servers.begin();
for (int i = 0; i < server_count; i++, ++current_server) {
short channel_count = pt.get<short>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL_COUNT");
for (int j = 0; j < channel_count; j++) {
Canal CanalTemp;
CanalTemp.ip = pt.get<std::string>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL" + std::to_string(j + 1) + "_IP");
CanalTemp.port = pt.get<short>("GAME_SERVER.SERVER_" + std::to_string(i + 1) + "_CHANNEL" + std::to_string(j + 1) + "_PORT");
tcp::endpoint endpoint(boost::asio::ip::address::from_string(CanalTemp.ip), CanalTemp.port);
current_server->emplace_back(io_service, Database_ptr, endpoint);
}
}
io_service.run();
}
catch (std::exception &e) {
std::cerr << e.what() << std::endl;
}
std::cin.get();
}
I used a configuration of
[DATABASE]
HOST=localhost
USER=root
PASS=youbet
SCHEMA=my_game
[GAME_SERVER]
SERVER_COUNT=1
SERVER_1_CHANNEL_COUNT=2
SERVER_1_CHANNEL1_IP=127.0.0.1
SERVER_1_CHANNEL1_PORT=6767
SERVER_1_CHANNEL2_IP=127.0.0.2
SERVER_1_CHANNEL2_PORT=6868
Which, when running clients on both channels (port 6767 and 6868) prints an "endless" repeat of:
========================================
= Game Server v1.0 by Gravity1 =
========================================
Connecting to fake database localhost/my_game with user root and password '******'
Received from 127.0.0.1:54942: 'hello channel
'
Received from 127.0.0.1:37217: 'hello OTHER channel
'
Received from 127.0.0.1:54945: 'hello channel
'
Received from 127.0.0.1:37220: 'hello OTHER channel
'
Received from 127.0.0.1:54947: 'hello channel
'
Received from 127.0.0.1:37222: 'hello OTHER channel
'
Received from 127.0.0.1:54949: 'hello channel
'
Received from 127.0.0.1:37224: 'hello OTHER channel
'
Received from 127.0.0.1:54951: 'hello channel
'
Received from 127.0.0.1:37226: 'hello OTHER channel
'
Received from 127.0.0.1:54953: 'hello channel
'
Received from 127.0.0.1:37228: 'hello OTHER channel
'
Received from 127.0.0.1:54955: 'hello channel
'
Received from 127.0.0.1:37230: 'hello OTHER channel
'
Received from 127.0.0.1:54957: 'hello channel
'
Received from 127.0.0.1:37232: 'hello OTHER channel
'
Completely unrelated, but your configuration format really begs for a hierarchical format like JSON or XML.
For fun, I refactored that sample to use XML:
<?xml version="1.0"?>
<CONFIG>
<DATABASE>
<HOST>localhost</HOST>
<USER>root</USER>
<PASS>youbet</PASS>
<SCHEMA>my_game</SCHEMA>
</DATABASE>
<GAME_SERVER>
<SERVER>
<CHANNEL>
<IP>127.0.0.1</IP>
<PORT>6767</PORT>
</CHANNEL>
<CHANNEL>
<IP>127.0.0.2</IP>
<PORT>6868</PORT>
</CHANNEL>
</SERVER>
</GAME_SERVER>
</CONFIG>
Which you can read with the following snippets:
boost::property_tree::ptree pt;
boost::property_tree::read_xml("game_server_config.xml", pt);
if (auto dbconfig = pt.get_child_optional("CONFIG.DATABASE")) {
database.host = dbconfig->get<std::string>("HOST");
database.username = dbconfig->get<std::string>("USER");
database.password = dbconfig->get<std::string>("PASS");
database.schema = dbconfig->get<std::string>("SCHEMA");
}
And for the server/channels:
for (auto& serverconfig: pt.get_child("CONFIG.GAME_SERVER")) {
if ("SERVER" != serverconfig.first)
continue;
servers.emplace_back();
auto& current_server = servers.back();
for (auto& channelconfig: serverconfig.second) {
if ("CHANNEL" != channelconfig.first)
continue;
Canal CanalTemp;
CanalTemp.ip = channelconfig.second.get<std::string>("IP");
CanalTemp.port = channelconfig.second.get<short>("PORT");
tcp::endpoint endpoint(boost::asio::ip::address::from_string(CanalTemp.ip), CanalTemp.port);
current_server.emplace_back(io_service, Database_ptr, endpoint);
}
}
See it Live On Coliru as well :)
#include <boost/asio.hpp>
#include <boost/make_shared.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/bind.hpp>
#include <iostream>
// for iterator and reference stability (see:
// http://stackoverflow.com/questions/6438086/iterator-invalidation-rules)
#include <list>
using tcp = boost::asio::ip::tcp;
struct Canal {
std::string ip;
int port;
};
struct Database {
std::string host, username, password, schema;
};
struct TDatabase {
TDatabase(Database config) : details(config) {}
void Connect() {
std::cout
<< "Connecting to fake database " << details.host << "/" << details.schema
<< " with user " << details.username << " and password '" << std::string(details.password.size(), '*') << "'\n";
}
private:
Database details;
};
struct TSession : std::enable_shared_from_this<TSession> {
TSession(boost::asio::io_service& svc, std::shared_ptr<TDatabase> db) :
_svc(svc), _socket(_svc), _db(db) {}
tcp::socket& Socket() { return _socket; }
void Start() {
boost::asio::async_read(_socket, _sb,
boost::bind(&TSession::HandleReceived, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void HandleReceived(boost::system::error_code ec, size_t bytes_transferred) {
if (!ec || boost::asio::error::eof == ec) {
std::cout << "Received from " << _socket.remote_endpoint() << ": '" << &_sb << "'\n";
} else
{
std::cout << "Error reading from peer: " << ec.message() << "\n";
}
}
private:
boost::asio::io_service& _svc;
tcp::socket _socket;
std::shared_ptr<TDatabase> _db;
boost::asio::streambuf _sb;
};
struct TServer {
tcp::acceptor acceptor;
std::shared_ptr<TDatabase> database;
TServer(boost::asio::io_service &io_service, std::shared_ptr<TDatabase> database, const boost::asio::ip::tcp::endpoint &endpoint)
: acceptor(io_service, endpoint), database(database)
{
Accept_Connection();
}
void Accept_Connection() {
auto Connection = std::make_shared<TSession>(acceptor.get_io_service(), database);
acceptor.async_accept(Connection->Socket(),
boost::bind(&TServer::Handle_Connection, this, Connection, boost::asio::placeholders::error));
}
void Handle_Connection(std::shared_ptr<TSession> Connection, const boost::system::error_code &error) {
if (!error) {
Connection->Start();
Accept_Connection();
} else
std::cout << "Error: " << error.message() << "\n";
}
};
//#include "TServer.h"
//#include "TDatabase.h"
//#include "Includes.h"
//#include "Structures.h"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
int main() {
try {
std::cout << "========================================" << std::endl
<< "= Game Server v1.0 by Gravity1 =" << std::endl
<< "========================================" << std::endl;
boost::asio::io_service io_service;
Database database;
std::list<std::list<TServer> > servers;
srand(time(0));
boost::property_tree::ptree pt;
boost::property_tree::read_xml("game_server_config.xml", pt);
if (auto dbconfig = pt.get_child_optional("CONFIG.DATABASE")) {
database.host = dbconfig->get<std::string>("HOST");
database.username = dbconfig->get<std::string>("USER");
database.password = dbconfig->get<std::string>("PASS");
database.schema = dbconfig->get<std::string>("SCHEMA");
}
std::shared_ptr<TDatabase> Database_ptr = std::make_shared<TDatabase>(database);
Database_ptr->Connect();
for (auto& serverconfig: pt.get_child("CONFIG.GAME_SERVER")) {
if ("SERVER" != serverconfig.first)
continue;
servers.emplace_back();
auto& current_server = servers.back();
for (auto& channelconfig: serverconfig.second) {
if ("CHANNEL" != channelconfig.first)
continue;
Canal CanalTemp;
CanalTemp.ip = channelconfig.second.get<std::string>("IP");
CanalTemp.port = channelconfig.second.get<short>("PORT");
tcp::endpoint endpoint(boost::asio::ip::address::from_string(CanalTemp.ip), CanalTemp.port);
current_server.emplace_back(io_service, Database_ptr, endpoint);
}
}
io_service.run();
}
catch (std::exception &e) {
std::cerr << e.what() << std::endl;
}
std::cin.get();
}
Prints
========================================
= Game Server v1.0 by Gravity1 =
========================================
Connecting to fake database localhost/my_game with user root and password '******'
Received from 127.0.0.1:55712: 'hello channel
'
Received from 127.0.0.1:37987: 'hello OTHER channel
'
Received from 127.0.0.1:55714: 'hello channel
'
Received from 127.0.0.1:37989: 'hello OTHER channel
'
Received from 127.0.0.1:55716: 'hello channel
'
etc.