How can I run miltiple ServerApplications with POCO C++? - c++

I've started learning POCO C++ library and I'm stuck while trying to run 2 servers in the same application (so that they can use some common runtime variables). These are 2 different servers, one of them is TCP TimeServer and the other one is simple UDP EchoServer. The code:
#include "Poco/Net/TCPServer.h"
#include "Poco/Net/TCPServerConnection.h"
#include "Poco/Net/TCPServerConnectionFactory.h"
#include "Poco/Net/TCPServerParams.h"
#include "Poco/Net/StreamSocket.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/Net/DatagramSocket.h"
#include "Poco/Timestamp.h"
#include "Poco/DateTimeFormatter.h"
#include "Poco/DateTimeFormat.h"
#include "Poco/Exception.h"
#include "Poco/Util/ServerApplication.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"
#include <iostream>
using Poco::Net::ServerSocket;
using Poco::Net::StreamSocket;
using Poco::Net::TCPServerConnection;
using Poco::Net::TCPServerConnectionFactory;
using Poco::Net::TCPServer;
using Poco::Timestamp;
using Poco::DateTimeFormatter;
using Poco::DateTimeFormat;
using Poco::Util::ServerApplication;
using Poco::Util::Application;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::HelpFormatter;
class TimeServerConnection : public TCPServerConnection
{
public:
TimeServerConnection(const StreamSocket& s, const std::string& format) :
TCPServerConnection(s),
_format(format)
{
}
void run()
{
Application& app = Application::instance();
bool isOpen = true;
Poco::Timespan timeOut(10, 0);
unsigned char incommingBuffer[1000];
app.logger().information("SYSLOG from " + this->socket().peerAddress().toString());
while (isOpen) {
if (socket().poll(timeOut, Poco::Net::Socket::SELECT_READ) == false) {
std::cout << "TIMEOUT!" << std::endl << std::flush;
} else {
int nBytes = -1;
try {
nBytes = socket().receiveBytes(incommingBuffer, sizeof(incommingBuffer));
std::cout << incommingBuffer << std::endl;
} catch (Poco::Exception& exc) {
std::cerr << "Network error: " << exc.displayText() << std::endl;
isOpen = false;
}
if (nBytes == 0) {
std::cout << "Client closes connection!" << std::endl << std::flush;
isOpen = false;
} else {
std::cout << "Receiving nBytes: " << nBytes << std::endl << std::flush;
}
}
}
try
{
Timestamp now;
std::string dt(DateTimeFormatter::format(now, _format));
dt.append("\r\n");
socket().sendBytes(dt.data(), (int)dt.length());
}
catch (Poco::Exception& exc)
{ app.logger().log(exc); }
}
private:
std::string _format;
};
class TimeServerConnectionFactory : public TCPServerConnectionFactory
{
public:
TimeServerConnectionFactory(const std::string& format) :
_format(format)
{
}
TCPServerConnection* createConnection(const StreamSocket& socket)
{ return new TimeServerConnection(socket, _format); }
private:
std::string _format;
};
class UDPServer : public Poco::Util::ServerApplication
{
public:
UDPServer(){}
~UDPServer(){}
protected:
void initialize(Application& self)
{
loadConfiguration(); // load default configuration files, if present
ServerApplication::initialize(self);
}
void uninitialize() { ServerApplication::uninitialize(); }
int main(const std::vector<std::string>& args)
{
unsigned short port = (unsigned short)config().getInt("udpport", 9002);
std::cout << "[UDP] Using port " << port << std::endl;
std::string format(config().getString("TimeServer.format", DateTimeFormat::ISO8601_FORMAT));
Poco::Net::SocketAddress socketaddress(Poco::Net::IPAddress(), 9001);
Poco::Net::DatagramSocket datagramsocket(socketaddress);
char buffer[1024]; // 1K byte
while (1) {
Poco::Net::SocketAddress sender;
int n = datagramsocket.receiveFrom(buffer, sizeof(buffer) - 1, sender);
buffer[n] = '\0';
std::cout << sender.toString() << ":" << buffer << std::endl;
}
return 0;
}
};
class TimeServer : public Poco::Util::ServerApplication
{
public:
TimeServer() : _helpRequested(false)
{
}
~TimeServer()
{
}
protected:
void initialize(Application& self)
{
loadConfiguration(); // load default configuration files, if present
ServerApplication::initialize(self);
}
void uninitialize()
{
ServerApplication::uninitialize();
}
void defineOptions(OptionSet& options)
{
ServerApplication::defineOptions(options);
options.addOption(
Option("help", "h", "display help information on command line arguments")
.required(false)
.repeatable(false));
}
void handleOption(const std::string& name, const std::string& value)
{
ServerApplication::handleOption(name, value);
if (name == "help")
_helpRequested = true;
}
void displayHelp()
{
HelpFormatter helpFormatter(options());
helpFormatter.setCommand(commandName());
helpFormatter.setUsage("OPTIONS");
helpFormatter.setHeader("A server application that serves the current date and time.");
helpFormatter.format(std::cout);
}
int main(const std::vector<std::string>& args)
{
if (_helpRequested)
{
displayHelp();
}
else
{
unsigned short port = (unsigned short)config().getInt("tcpport", 9911);
std::cout << "Using port " << port << std::endl;
std::string format(config().getString("TimeServer.format", DateTimeFormat::ISO8601_FORMAT));
ServerSocket svs(port);
TCPServer srv(new TimeServerConnectionFactory(format), svs);
srv.start();
std::cout << "Server started!\n";
waitForTerminationRequest();
srv.stop();
std::cout << "Server stopped!\n";
}
return Application::EXIT_OK;
}
private:
bool _helpRequested;
};
int main(int argc, char** argv)
{
TimeServer app;
UDPServer app2;
app.run(argc, argv);
app2.run(argc, argv);
}
In the end of code I have int main() method where I'm trying to run 2 servers. However I get assertion violation here. There is a similar question on StackOverflow, however boost is used there while I'm using plain C++, so that solution is not relevant to me.
How can I run simultaneously these 2 servers?

ServerApplication was not designed for multiple instances. What you should do is run one ServerApplication and launch TCPServer and UDPServer in that application.

Actually if you want to made like this (as you question), seperated both
tcp (a) class and
udp (b) class.
Call both in other class (c) and
define which one
(c) -> (a)
(c) -> (b)
u need to call first and when. So u need make condition and decision.
Note: give them space time before run to made poco breath. 😂

Related

Server socket doesn't work properly - "accept is already open"

I've tried to separate my server socket in a singleton. Here's the code:
ServerSocket.h
#pragma once
#include <asio.hpp>
#include <iostream>
using asio::ip::tcp;
class ServerSocket
{
public:
ServerSocket(ServerSocket& otherSingleton) = delete;
void operator=(const ServerSocket& copySingleton) = delete;
tcp::acceptor* InitAcceptor();
tcp::socket* InitSocket();
void StartServerSocket();
void SendData(std::string);
std::array<char, 5000> RecieveData();
static ServerSocket* GetInstance();
private:
static ServerSocket* instance;
tcp::acceptor* acceptor;
tcp::socket* socket;
asio::io_context io_context;
ServerSocket() {
acceptor = InitAcceptor();
socket = InitSocket();
}
~ServerSocket()
{
std::cout << "Server closed";
}
};
ServerSocket.cpp
#include "ServerSocket.h"
tcp::acceptor* ServerSocket::InitAcceptor()
{
try
{
tcp::acceptor* acceptor = new tcp::acceptor(io_context, tcp::endpoint(tcp::v4(), 27015));
return acceptor;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
tcp::socket* ServerSocket::InitSocket()
{
try
{
tcp::socket* socket = new tcp::socket(io_context);
return socket;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
void ServerSocket::StartServerSocket()
{
try
{
std::cout << "Server started";
for (;;)
{
acceptor->accept(*socket);
};
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
std::array<char, 5000> ServerSocket::RecieveData()
{
try {
std::array<char, 5000> buf;
asio::error_code error;
size_t len = socket->read_some(asio::buffer(buf), error);
buf[len] = '\0';
return buf;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
}
}
ServerSocket* ServerSocket::instance(nullptr);
ServerSocket* ServerSocket::GetInstance()
{
if (instance == nullptr)
{
instance = new ServerSocket();
}
return instance;
}
Server socket starts, I get:
Server started
when a client connects, I get:
accept: Already open
and the server stops.
I think the error comes from the acceptor being in a for function. But according to the docs, it should work this way. (or at least that's how I understand - https://think-async.com/Asio/asio-1.20.0/doc/asio/tutorial/tutdaytime2.html)
I tried deleting the for loop, like this:
try
{
std::cout << "Server started";
acceptor->accept(*socket);
}
and now there is no problem. But the connection isn't kept open by the server. The client connects once, sends data, and the server stops running.
As far as I understand from the docs, if I set the acceptor in a for(;;), it should be running - but it doesn't work in my case.
So, how can I keep my socket open in my implementation? I want it to be running for more than one SendData - I want it to be able to communicate with the client as long as the client is connected.
Thanks.
//Edit:
Here's the client code:
#include <iostream>
#include <asio.hpp>
#include "../../cereal/archives/json.hpp"
using asio::ip::tcp;
int main(int argc, char* argv[])
{
try
{
if (argc != 2)
{
std::cerr << "Usage: client <host>" << std::endl;
return 1;
}
// Socket Parameters
const unsigned port = 27015;
auto ip_address = asio::ip::make_address_v4(argv[1]);
auto endpoint = tcp::endpoint{ ip_address, port };
// Creating and Connecting the Socket
asio::io_context io_context;
auto resolver = tcp::resolver{ io_context };
auto endpoints = resolver.resolve(endpoint);
auto socket = tcp::socket{ io_context };
asio::connect(socket, endpoints);
std::array<char, 5000> buf;
std::cout << "Message to server: ";
asio::error_code ignored_error;
std::string username = "test", password = "mihai";
std::stringstream os;
{
cereal::JSONOutputArchive archive_out(os);
archive_out(
CEREAL_NVP(username),
CEREAL_NVP(password)
);
}
asio::write(socket, asio::buffer(os.str()), ignored_error);
return 0;
}
catch (std::exception& e)
{
std::cerr << e.what() << std::endl;
return 1;
}
And Communication.h which is responsible to catching the operation from the client and sending it to the server
#pragma once
#include <iostream>
#include "DBUser.h"
#include "DBPost.h"
class Communication
{
public:
enum class Operations {
eLogin,
eRegister
};
void ExecuteOperation(Operations operation,const std::array<char, 5000>& buffer);
};
.cpp
#include "Communication.h"
void Communication::ExecuteOperation(Operations operation,const std::array<char, 5000>& buffer)
{
DBUser* user= DBUser::getInstance();
switch (operation)
{
case Communication::Operations::eLogin:
{
std::string username, password;
std::stringstream is(buffer.data());
{
cereal::JSONInputArchive archive_in(is);
archive_in(username,password);
}
try
{
user->LoginUser(username, password);
}
catch (const std::exception& e)
{
std::cout << e.what();
}
break;
}
case Communication::Operations::eRegister:
{
std::string username, password;
std::stringstream is(buffer.data());
{
cereal::JSONInputArchive archive_in(is);
archive_in(username, password);
}
try
{
user->CreateUser(username, password);
}
catch (const std::exception& e)
{
std::cout << e.what();
}
break;
}
}
}
Main
#include <iostream>
#include <pqxx/pqxx>
#include "DBLink.h"
#include "DBUser.h"
#include "DBPost.h"
#include "../Logging/Logging.h"
#include <iostream>
#include <string>
#include <asio.hpp>
#include "ServerSocket.h"
#include "Communication.h"
int main()
{
ServerSocket* test = ServerSocket::GetInstance();
test->StartServerSocket();
std::array<char, 5000> buf = test->RecieveData();
Communication communicationInterface;
communicationInterface.ExecuteOperation(Communication::Operations::eRegister, buf);
system("pause");
}
There's a lot of antipattern going on.
Overuse of pointers.
Overuse of new (without any delete, a guaranteed leak)
The destructor claims that "Server closed" but it doesn't actually do a single thing to achieve that.
Two-step initialization (InitXXXX functions). Firstly, you should obviously favor initializer lists
ServerSocket()
: acceptor_(InitAcceptor()), socket_(InitSocket())
{ }
And you need to makeInitAcceptor/InitSocket private to the implementation.
I'll forget the Singleton which is anti-pattern 99% of the time, but I guess that's almost debatable.
In your StartServerSocket you have a loop that reuses the same socket all the time. Of course, it will already be connected. You need separate socket instances:
for (;;) {
acceptor_->accept(*socket_);
};
Simplify/Fix
#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;
struct Listener {
void Start()
{
std::cout << "Server started";
for (;;) {
auto socket = acceptor_.accept();
std::cout << "Accepted connection from " << socket.remote_endpoint()
<< std::endl;
};
}
static Listener& GetInstance() {
static Listener s_instance{27015}; // or use weak_ptr for finite lifetime
return s_instance;
}
private:
asio::io_context ioc_; // order of declaration is order of init!
tcp::acceptor acceptor_;
Listener(uint16_t port) : acceptor_{ioc_, tcp::endpoint{tcp::v4(), port}} {}
};
int main() {
try {
Listener::GetInstance().Start();
} catch (std::exception const& e) {
std::cerr << e.what() << std::endl;
}
}
Now you could hand the socket instances to a thread. I concur with the other commenters that thread-per-request is likely also an anti-pattern, and you should consider using async IO with Asio (hence the name).
Live Demo
EDIT complete and working example based on the server code from the question:
// main.cxx
#include "ServerSocket.hxx"
#include <boost/asio.hpp>
#include <iostream>
#include <string>
using boost::asio::ip::tcp;
int
main ()
{
ServerSocket *test = ServerSocket::GetInstance ();
test->StartServerSocket ();
std::cout << std::endl;
while (auto msg = test->RecieveData ())
{
std::cout << msg.value ();
}
}
// ServerSocket.hxx
#pragma once
#include <boost/asio.hpp>
#include <iostream>
#include <optional>
using boost::asio::ip::tcp;
class ServerSocket
{
public:
ServerSocket (ServerSocket &otherSingleton) = delete;
void operator= (const ServerSocket &copySingleton) = delete;
tcp::acceptor *InitAcceptor ();
tcp::socket *InitSocket ();
void StartServerSocket ();
void SendData (std::string);
std::optional<std::string> RecieveData ();
static ServerSocket *GetInstance ();
private:
static ServerSocket *instance;
tcp::acceptor *acceptor;
tcp::socket *socket;
boost::asio::io_context io_context;
ServerSocket ()
{
acceptor = InitAcceptor ();
socket = InitSocket ();
}
~ServerSocket () {
delete socket;
delete acceptor;
std::cout << "Server closed"; }
};
// ServerSocket.cxx
#include "ServerSocket.hxx"
#include <optional>
tcp::acceptor *
ServerSocket::InitAcceptor ()
{
try
{
return new tcp::acceptor (io_context, tcp::endpoint (tcp::v4 (), 27015));
}
catch (std::exception &e)
{
std::cerr << e.what () << std::endl;
}
return nullptr;
}
tcp::socket *
ServerSocket::InitSocket ()
{
try
{
return new tcp::socket (io_context);
}
catch (std::exception &e)
{
std::cerr << e.what () << std::endl;
}
return nullptr;
}
void
ServerSocket::StartServerSocket ()
{
try
{
std::cout << "Server started";
acceptor->accept (*socket);
}
catch (std::exception &e)
{
std::cerr << e.what () << std::endl;
}
}
std::optional<std::string>
ServerSocket::RecieveData ()
{
try
{
char data[5000];
for (;;)
{
boost::system::error_code error;
size_t length = socket->read_some (boost::asio::buffer (data), error);
if (error == boost::asio::error::eof) return std::nullopt; // Connection closed cleanly by peer.
else if (error)
throw boost::system::system_error (error); // Some other error.
return std::string{ data, length };
}
}
catch (std::exception &e)
{
std::cerr << e.what () << std::endl;
}
return {};
}
ServerSocket *ServerSocket::instance (nullptr);
ServerSocket *
ServerSocket::GetInstance ()
{
if (instance == nullptr)
{
instance = new ServerSocket ();
}
return instance;
}
Note that there are still some problems with the server:
Error handling
More than one connection
The server does not send a message if the operation was successful
If you disconnect the client the server shuts down
We could replace some pointers with optional no need to write "new"
Just make a normal class do not write it as singleton.
If you like to test the server you can run
telnet localhost 27015
and then write some text and press enter

POST request body is empty (cpp-netlib 0.13.0)

I have upgraded cpp-netlib from v0.11.0 to 0.13.0 and run into some difficulties.
Previously, when a request was sent to the server, the body of the request could be read from the request object.
The request body is now empty when I send the same request to a server using v0.13.0.
The rest of the request object appears to be correct - only the body is empty.
Is there something I need to do differently? I can't find any examples on the site that show how the body is extracted.
I have confirmed the same behaviour from the hello world example.
#include <boost/network/protocol/http/server.hpp>
#include <iostream>
namespace http = boost::network::http;
struct hello_world;
typedef http::server<hello_world> server;
struct hello_world
{
void operator()(const server::request &request, server::connection_ptr connection)
{
///////////////////////////////////
// request.body is empty
///////////////////////////////////
server::string_type ip = source(request);
unsigned int port = request.source_port;
std::ostringstream data;
data << "Hello, " << ip << ':' << port << '!';
connection->set_status(server::connection::ok);
connection->write(data.str());
}
};
int main(int argc, char *argv[]) {
try {
hello_world handler;
server::options options(handler);
server server_(options.address("192.168.0.19").port("9999"));
server_.run();
}
catch (std::exception &e) {
std::cerr << e.what() << std::endl;
return 1;
}
return 0;
}
Here is the request I'm sending:
curl -v -X POST http://192.168.0.19:9999/my-app/rest/foo/1.0/bar -H 'Content-Type: application/x-www-form-urlencoded' --data key=value
In older versions of cpp-netlib you could choose between a sync_server and a async_server class. Since version 0.12 only the async_server class is available. This class does not read body data of a POST request into request.body automatically, but requires the user to read the data in an asynchronous way using connection->read(callback).
Long story short, I've compiled a minimal echo server example that shows how to do this correctly. It also explains how to deal with the not well known Expect: 100-continue header that might be involved.
Please check out echo_async_server.cpp which has been added to the repo recently.
#include <vector>
#include <boost/config/warning_disable.hpp>
#include <boost/network/include/http/server.hpp>
#include <boost/network/utils/thread_pool.hpp>
#include <boost/range/algorithm/find_if.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/thread.hpp>
namespace net = boost::network;
namespace http = boost::network::http;
namespace utils = boost::network::utils;
struct async_hello_world;
typedef http::async_server<async_hello_world> server;
struct connection_handler : boost::enable_shared_from_this<connection_handler> {
connection_handler(server::request const &request)
:req(request), body("") {}
~connection_handler() {
std::cout << "connection_handler dctor called!" << std::endl;
}
void operator()(server::connection_ptr conn) {
int cl;
server::request::headers_container_type const &hs = req.headers;
for(server::request::headers_container_type::const_iterator it = hs.begin(); it!=hs.end(); ++it) {
if(boost::to_lower_copy(it->name)=="content-length") {
cl = boost::lexical_cast<int>(it->value);
break;
}
}
read_chunk(cl, conn);
}
void read_chunk(size_t left2read, server::connection_ptr conn) {
std::cout << "left2read: " << left2read << std::endl;
conn->read(
boost::bind(
&connection_handler::handle_post_read,
connection_handler::shared_from_this(),
_1, _2, _3, conn, left2read
)
);
}
void handle_post_read(
server::connection::input_range range, boost::system::error_code error, size_t size, server::connection_ptr conn, size_t left2read) {
if(!error) {
std::cout << "read size: " << size << std::endl;
body.append(boost::begin(range), size);
size_t left = left2read - size;
if(left>0) {
read_chunk(left, conn);
} else {
//std::cout << "FINISHED at " << body.size()<< std::endl;
}
}
std::cout << "error: " << error.message() << std::endl;
}
void handle_post_request(server::connection_ptr conn)
{
std::cout << "handle request..." << std::endl;
std::cout << "post size: " << body.size() << std::endl;
}
server::request const &req;
std::string body;
};
struct async_hello_world {
void operator()(server::request const &request, server::connection_ptr conn) {
boost::shared_ptr<connection_handler> h(new connection_handler(request));
(*h)(conn);
}
void error(boost::system::error_code const & ec) {
// do nothing here.
std::cout << "async error: " << ec.message() << std::endl;
}
};
int main(int argc, char * argv[]) {
utils::thread_pool thread_pool(4);
async_hello_world handler;
server instance("0.0.0.0", "1935", handler, thread_pool);
instance.run();
return 0;
}
You need to read manually the body. Now a connection_ptr object is used and a handler must be attached for doing the read.
So it must look something like this:
if (r.method == "POST") {
auto foundIt = std::find_if(r.headers.begin(), r.headers.end(),
[](auto const & h) { return h.name == "Content-Length"; });
if (foundIt == r.headers.end())
throw std::logic_error("No Content-Length header found in POST");
auto handleReadFunc = [this](auto &&... args) {
this->handleReadBody(args...);
};
//This attaches a callback to read the body
connection->read(handleReadFunc);
}

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

ASIO example code closing socket before it should

I need a parallel synchronous TCP solution using ASIO. I'm trying to get the example code from these examples working: https://github.com/jvillasante/asio-network-programming-cookbook/tree/master/src (using the server in ch04: 02_Sync_parallel_tcp_server.cpp and the client in ch03: 01_Sync_tcp_client.cpp).
The only thing I changed is the logging to append to text files.
The problem is that while the server runs fine, the client dies after returning a single response from the server:
libc++abi.dylib: terminating with uncaught exception of type boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >: shutdown: Socket is not connected
Code for the server:
#include <boost/asio.hpp>
#include <atomic>
#include <memory>
#include <thread>
#include <iostream>
#include <fstream>
using namespace boost;
class Service {
public:
Service() = default;
void StartHandlingClient(std::shared_ptr<asio::ip::tcp::socket> sock) {
std::thread th{[this, sock]() { HandleClient(sock); }};
th.detach();
}
private:
void HandleClient(std::shared_ptr<asio::ip::tcp::socket> sock) {
try {
asio::streambuf request;
asio::read_until(*sock.get(), request, '\n');
std::istream is(&request);
std::string line;
std::getline(is, line);
std::ofstream log("logfile2.txt", std::ios_base::app | std::ios_base::out);
log << "Request: " << line << "\n" << std::flush;
// 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 (std::system_error& e) {
std::ofstream log("logfile1.txt", std::ios_base::app | std::ios_base::out);
log << "Error occurred! Error code = " << e.code().value() << ". Message: " << e.what() << "\n" << std::flush;
}
// Clean up
delete this;
}
};
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() {
auto sock = std::make_shared<asio::ip::tcp::socket>(m_ios);
m_acceptor.accept(*sock.get());
(new Service)->StartHandlingClient(sock);
}
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()) {
acc.Accept();
}
}
private:
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(60));
srv.Stop();
} catch (std::system_error& e) {
std::ofstream log("logfile1.txt", std::ios_base::app | std::ios_base::out);
log << "Error occurred! Error code = " << e.code().value() << ". Message: " << e.what() << "\n" << std::flush;
}
return 0;
}
Code for the client:
#include <boost/asio.hpp>
#include <iostream>
#include <fstream>
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());
}
~SyncTCPClient() { close(); }
void connect() { m_sock.connect(m_ep); }
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 close() {
if (m_sock.is_open()) {
std::ofstream log("logfile1.txt", std::ios_base::app | std::ios_base::out);
log << "shutting down\n" << std::flush;
m_sock.shutdown(asio::ip::tcp::socket::shutdown_both);
log << "closing the socket\n" << std::flush;
m_sock.close();
log << "socket closed\n" << std::flush;
}
}
void sendRequest(const std::string& request) { 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...\n";
std::string response = client.emulateLongComputationOp(10);
std::cout << "Response received: " << response << "\n";
} catch (std::system_error& e) {
std::ofstream log("logfile1.txt", std::ios_base::app | std::ios_base::out);
log << "Error occurred! Error code = " << e.code().value() << ". Message: " << e.what() << "\n" << std::flush;
return e.code().value();
}
return 0;
}
I don't see a lot wrong, and I cannot reproduce the problem with the code shown.
Things I do see:
the thread procedure could be a static because it's stateless (delete this is a code smell)
the thread needn't be detached (using boost::thread_group::join_all would be much better)
you were writing to the same logfile from server as well as client; results are undefined
spelling .store() and .load() on an atomic<bool> is un-idiomatic
spelling out *sock.get() on any kind of smart pointer is unforgivably un-idiomatic
writing code().value() - swallowing the category - is a BAD thing to do, and e.what() is NOT the way to get the message (use e.code().message()).
If you need flush, you might as well use std::endl
There's really no reason to use a shared_ptr in c++14:
asio::ip::tcp::socket sock(m_ios);
m_acceptor.accept(sock);
std::thread([sock=std::move(sock)]() mutable { HandleClient(sock); }).detach();
In C++11 stick to:
auto sock = std::make_shared<asio::ip::tcp::socket>(m_ios);
m_acceptor.accept(*sock);
std::thread([sock] { HandleClient(*sock); }).detach();
This means HandleClient can just take a ip::tcp::socket& instead of a smart pointer.
INTEGRATING
Server.cpp
#include <atomic>
#include <boost/asio.hpp>
#include <fstream>
#include <iostream>
#include <memory>
#include <thread>
using namespace boost;
static void HandleClient(asio::ip::tcp::socket& sock) {
try {
asio::streambuf buf;
asio::read_until(sock, buf, '\n');
std::string request;
getline(std::istream(&buf), request);
std::ofstream log("server.log", std::ios_base::app | std::ios_base::out);
log << "Request: " << request << std::endl;
// 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, asio::buffer(response));
} catch (std::system_error &e) {
std::ofstream log("server.log", std::ios_base::app | std::ios_base::out);
log << e.what() << " " << e.code() << ": " << e.code().message() << std::endl;
}
}
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() {
auto sock = std::make_shared<asio::ip::tcp::socket>(m_ios);
m_acceptor.accept(*sock);
std::thread([sock] { HandleClient(*sock); }).detach();
}
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 = true;
m_thread->join();
}
private:
void Run(unsigned short port_num) {
Acceptor acc{ m_ios, port_num };
while (!m_stop) {
acc.Accept();
}
}
private:
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(60));
srv.Stop();
} catch (std::system_error &e) {
std::ofstream log("server.log", std::ios_base::app | std::ios_base::out);
log << e.what() << " " << e.code() << ": " << e.code().message() << std::endl;
}
}
Client.cpp
#include <boost/asio.hpp>
#include <fstream>
#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());
}
~SyncTCPClient() { close(); }
void connect() { m_sock.connect(m_ep); }
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 close() {
if (m_sock.is_open()) {
std::ofstream log("client.log", std::ios_base::app | std::ios_base::out);
log << "shutting down" << std::endl;
m_sock.shutdown(asio::ip::tcp::socket::shutdown_both);
log << "closing the socket" << std::endl;
m_sock.close();
log << "socket closed" << std::endl;
}
}
void sendRequest(const std::string &request) { asio::write(m_sock, asio::buffer(request)); }
std::string receiveResponse() {
asio::streambuf buf;
asio::read_until(m_sock, buf, '\n');
std::string response;
getline(std::istream(&buf), 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...\n";
std::string response = client.emulateLongComputationOp(10);
std::cout << "Response received: " << response << std::endl;
} catch (std::system_error &e) {
std::ofstream log("client.log", std::ios_base::app | std::ios_base::out);
log << e.what() << " " << e.code() << ": " << e.code().message() << std::endl;
return e.code().value();
}
}

cpprestsdk handler class crashes when using STL container as member

I wrote a simple code using cpprestsdk. I use a map (records) as a member of CommandHandler class, and manipulate it in a public method (has_record()).
it works before handler.open().wait() runs, but when I call it in a request, it crashes!
Here is my code:
#define BOOST_DATE_TIME_NO_LIB
#include <string>
#include <vector>
#include <cpprest/uri.h>
#include <cpprest/http_listener.h>
#include <cpprest/asyncrt_utils.h>
using namespace std;
using namespace web;
using namespace http;
using namespace utility;
using namespace http::experimental::listener;
class CommandHandler
{
public:
CommandHandler(utility::string_t url);
pplx::task<void> open() { return m_listener.open(); }
pplx::task<void> close() { return m_listener.close(); }
bool has_record();
private:
std::map< std::string, unsigned int > records;
void handle_get_or_post(http_request message);
http_listener m_listener;
};
bool CommandHandler::has_record()
{
return records.size() > 0 && records.find("1") != records.end();
}
CommandHandler::CommandHandler(utility::string_t url) : m_listener(url)
{
m_listener.support(methods::GET, std::bind(&CommandHandler::handle_get_or_post, this, std::placeholders::_1));
m_listener.support(methods::POST, std::bind(&CommandHandler::handle_get_or_post, this, std::placeholders::_1));
}
void CommandHandler::handle_get_or_post(http_request request)
{
if(this->has_record())
request.reply(status_codes::OK, 1);
else
request.reply(status_codes::OK, 0);
};
int main(int argc, char** argv)
{
try
{
utility::string_t address = U("http://127.0.0.1:9595");
uri_builder uri(address);
auto addr = uri.to_uri().to_string();
CommandHandler handler(addr);
if(handler.has_record())
std::cout<<"work!";
handler.open().wait();
ucout << utility::string_t(U("Listening for requests at: ")) << addr << std::endl;
ucout << U("Press ENTER key to quit...") << std::endl;
std::string line;
std::getline(std::cin, line);
handler.close().wait();
}
catch (std::exception& ex)
{
ucout << U("Exception: ") << ex.what() << std::endl;
ucout << U("Press ENTER key to quit...") << std::endl;
std::string line;
std::getline(std::cin, line);
}
return 0;
}
I found the problem but I don't understand why it throws. The problem is in this line of code:
request.reply(status_codes::OK, 0);
That 0 throws an exception