问题
I am working on a Network Application using ASIO
and have referred Chat-Server/Client
I have asked similar question Here
To explain better I am adding more code here:
My Cserver Class
class CServer {
private:
mutable tcp::acceptor acceptor_; // only in the listener
asio::io_service& io_;
CSerSessionsManager mng_;
std::string ip_;
std::string port_;
public:
CServer::CServer(asio::io_service& io_service, const std::string IP, const std::string port) : io_(io_service), acceptor_(io_service)
, ip_(IP), port_(port)
{
DEBUG_MSG("Listener Created");
}
~CServer()
{
DEBUG_MSG("Listener Destroyed");
acceptor_.close();
}
void initProtocol()
{
DEBUG_MSG(" Protocol Initiated");
std::array<unsigned char, 4> ip;
std::string delimiter = ".";
//Parse the IP String
size_t pos = 0;
auto i = 0;
std::string token;
while ((pos = ip_.find(delimiter)) != std::string::npos) {
token = ip_.substr(0, pos);
ip[i] = std::stoi(token);//what if stoi fails
i++;
ip_.erase(0, pos + delimiter.length());
}
ip[i] = std::stoi(ip_);
asio::ip::address_v4 address(ip);
tcp::endpoint ep(address, std::stoi(port_));
static std::mutex m;
std::unique_lock<std::mutex> lck(m, std::defer_lock);
//Critical Section start
lck.lock();
acceptor_ = tcp::acceptor(io_, ep);//Creating IOService
lck.unlock();
//Critical Section End
listen();
}
void listen()
{
DEBUG_MSG("!==============================================================!");
////Critical Section
static std::mutex m;
std::lock_guard<std::mutex> lock(m);
sessionPtr newSession = std::make_shared<CSerSession>(io_, mng_);
try
{
acceptor_.async_accept(newSession->socket(), std::bind(&CServer::handle_accept, /*shared_from_this()*/ this, newSession,
std::placeholders::_1));
///*asio::error_code ec;
//pSocket_->shutdown(asio::ip::tcp::socket::shutdown_send, ec);*/
}
catch (const std::bad_weak_ptr& e)
{
DEBUG_MSG(e.what());
throw e;
}
DEBUG_MSG("Listen Activated");
}
void handle_accept(sessionPtr newSession, const asio::error_code& error)
{
if (!acceptor_.is_open())
{
return;
}
if (!error)
{
DEBUG_MSG("Incoming Session accepted");
//Do I need a Lock here?
//Critical Section
static std::mutex m;
std::lock_guard<std::mutex> lock(m);
newSession->startSession();
listen();
}
else
{
DEBUG_MSG("Listen_Error");
// //throw ASIOError(Listen_Error);
DEBUG_MSG(error.message());
return;
}
}
};
My CSerSessionsManager Class
class CSerSessionsManager{
private:
std::set<sessionPtr> sessions_; //Active Sessions : Online Info
public:
CSerSessionsManager();
~CSerSessionsManager();
void addSession(sessionPtr session);
void dropSession(sessionPtr session);
};
CSerSessionsManager::CSerSessionsManager()
{
DEBUG_MSG("Construction");
}
CSerSessionsManager::~CSerSessionsManager()
{
DEBUG_MSG("Destruction");
}
void CSerSessionsManager::addSession(sessionPtr session)
{
DEBUG_MSG("Incoming Session Entry saved");
//Critical Section
static std::mutex m;
std::lock_guard<std::mutex> lock(m);
sessions_.insert(session);
}
void CSerSessionsManager::dropSession(sessionPtr session)
{
//Properly handle Existing connections first shutdown sockets
DEBUG_MSG("Session dropped");
//Critical Section
static std::mutex m;
std::lock_guard<std::mutex> lock(m);
std::set<sessionPtr>::iterator it;
for (it = sessions_.begin(); it != sessions_.end(); ++it)
{
if ((*it) == session)
{
sessions_.erase(session);
return;
}
}
//throw ASIOError(Session_Not_Found);
}
And my CSerSession Class
class CSerSession : public std::enable_shared_from_this < CSerSession > {
private:
mutable tcp::socket socket_; // client connection
CSerSessionsManager& manager_;
std::string ip_;
std::string port_;
CBuffer msg_;
public:
CSerSession(asio::io_service& io_service, CSerSessionsManager& mng) :
manager_(mng), socket_(io_service)
{
DEBUG_MSG("Server Session Created");
}
~CSerSession()
{
DEBUG_MSG("Server Session Destroyed");
}
void startSession()
{
DEBUG_MSG("Server Session Started");
//Critical Section
static std::mutex m;
std::lock_guard<std::mutex> lock(m);
manager_.addSession(shared_from_this());//Multiple threads should not try adding section
read(msg_);
}
void handle_read(const asio::error_code& error /*error*/, size_t bytes_transferred /*bytes_transferred*/)
{
if (!error)
{
DEBUG_MSG("Read");
//Critical Section
static std::mutex m;
std::lock_guard<std::mutex> lock(m);
read(msg_);
}
else
{
DEBUG_MSG("Read Error Detected : " << error.message());
//Check If shared_from_this() is valid or not
try
{
//Check if session was already dropped e.g. server object destroying
//i.e. if session object exists
DEBUG_MSG("Dropping Session");
//if (error == asio::error::operation_aborted)
manager_.dropSession(shared_from_this());
}
catch (const std::bad_weak_ptr& e)
{
DEBUG_MSG(e.what());
throw e;
}
return;
}
}
void read(CBuffer & buff)
{
DEBUG_MSG("Read");
asio::async_read(socket_, asio::buffer(const_cast<char *> (buff.getReceived()), buff.buffsize),
std::bind(&CSerSession::handle_read, shared_from_this(),
std::placeholders::_1, std::placeholders::_2));
}
tcp::socket& socket()
{
//Critical Section
static std::mutex m;
std::lock_guard<std::mutex> lock(m);
return socket_;
}
};
I create the CServer Object
in main as below:
void main()
{
try
{
asio::io_service io_service;
//CServer server(io_service, "Default", "127.0.0.1", "8000");
auto sPtr = std::make_shared<CServer>(io_service, "127.0.0.1", "8000");
sPtr->initProtocol();
//server.initProtocol();
asio::thread t(boost::bind(&asio::io_service::run, &io_service));
}
catch (...)
{
}
system("Pause");
}
The Output Log I get as below:
CSerSessionsManager::CSerSessionsManager : 183 : Construction
CServer::CServer : 239 : Listener Created
CServer::initProtocol : 250 : Protocol Initiated
CServer::listen : 288 : !==============================================================!
CSerSession::CSerSession : 108 : Server Session Created
CServer::listen : 309 : Listen Activated
CServer::~CServer : 244 : Listener Destroyed
CSerSessionsManager::~CSerSessionsManager : 188 : Destruction
CSerSession::~CSerSession : 113 : Server Session Destroyed
When CServer Object
destroys associated CSerSession Object
also destroys
, so while returning from ~CSerSession()
It throws exception boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<std::system_error> > at memory location 0x0277F19C.
at below lines of code:
#ifndef BOOST_EXCEPTION_DISABLE
throw enable_current_exception(enable_error_info(e));
#else
throw e;
#endif
}
I tried to debug a lot and tried using signal mechanism
also as discussed in HTTP Server, but I am stuck here and not able to proceed further.
The complete code can be checked here: MyCode
How do I resolve it?
回答1:
From a fixed version of the linked code: Live On Coliru I get
CSerSessionsManager : 184 : Construction
CServer : 240 : Listener Created
initProtocol : 251 : Protocol Initiated
~CServer : 245 : Listener Destroyed
~CSerSessionsManager : 189 : Destruction
NOTE: this was because I already had something listening on port 8000 (yay for error reporting!)
Did the initialization order of the fields fix it? Or is there something not running at all on my system (because of a race condition on my faster machine?).
Looks like the latter becuase on Coliru I got
CSerSessionsManager : 184 : Construction
CServer : 240 : Listener Created
initProtocol : 251 : Protocol Initiated
listen : 289 : !===================================!
CSerSession : 109 : Server Session Created
listen : 310 : Listen Activated
~CServer : 245 : Listener Destroyed
~CSerSessionsManager : 189 : Destruction
~CSerSession : 114 : Server Session Destroyed
So, let's have a closer look:
why are you parsing the IP string? That's what
address_v4
is for. Andip::tcp::resolver
.DEBUG_MSG(" Protocol Initiated"); asio::ip::address_v4 address = asio::ip::address_v4::from_string(ip_); tcp::endpoint ep(address, std::stoi(port_));
using a
static mutex
is rarely useful. Did you mean to synchronize access to shared resources? Then you need a shared mutex toowhy are you using defer-lock? Use scopes
{ //Critical Section start std::lock_guard<std::mutex> lck(mutex_); acceptor_ = tcp::acceptor(io_, ep);//Creating IOService //Critical Section End }
the main thread just exits, never joining the io thread. At least join. Or make it properly shutdown before terminating the program:
t.join();
hungarian naming is really useless here.
sPtr
doesn't tell me anything.server
or, if you insist,server_ptr
is what you need to know.you have out-of-bounds write here:
received_[str.size()] = '\0';
you wanted
received_[len] = '\0';
your
empty
doesn't need to loopbool empty() const { return !received_[0]; }
why are you looping to find stuff in an ordered set?
std::set<sessionPtr>::iterator it; for (it = sessions_.begin(); it != sessions_.end(); ++it) { if ((*it) == session) { sessions_.erase(session); return; } }
should be
sessions_.erase(session);
addSession/dropSession are internally locking; you don't need to put access to them in a critical section
throw e
is an antipattern; justthrow;
is re-throwyou have redundant tracing almost everywhere (this is what debuggers are for). E.g.
DEBUG_MSG("Read")
Locking here is bogus:
tcp::socket& socket() { // Critical Section std::lock_guard<std::mutex> lock(mutex_); return socket_; }
The reference returned will not be protected anyways, and socket is only initialized once.
all the thread locking seems redundant since there is only one service thread
CBuffer msg
is a bogus parameter toread()
as the same buffer is passed always. This could be plenty ok (it's in the same session), so, just use it.this
acceptor_ = tcp::acceptor(io_, ep);
should be
acceptor_.bind(ep);
and not in a critical section (server is only created once); Hence the
initProtocol
function can bevoid initProtocol() { acceptor_.bind(tcp::endpoint(asio::ip::address_v4::from_string(ip_), std::stoi(port_))); listen(); }
in
listen
you're catching bad_weak_ptr which can't even occurhere:
//Do I need a Lock here? //Critical Section std::lock_guard<std::mutex> lock(mutex_); newSession->startSession();
you don't need the lock.
newSession
was bound from a local variable. It's impossible for it to be shared unless you copied the completion handler (you didn't).
Here's a more fixed up version:
Live On Coliru
#include <iostream>
#include <boost/asio.hpp>
#include <memory>
#include <deque>
#include <set>
#include <iomanip>
#include <mutex>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#define DEBUG ON
#ifdef DEBUG
#define DEBUG_MSG(str) do {std::cout << std::setw(75) << std::left << __FUNCTION__ \
<< std::setw(3) << std::left << ":" << std::setw(5) << std::left << __LINE__ \
<< std::setw(5) << std::left << ":"\
<< std::left << str \
<< std::endl;} while( false )
#else
#define DEBUG_MSG(str) do { } while ( false )
#endif
namespace asio = boost::asio;
using asio::ip::tcp;
class CSerSession;
using sessionPtr = std::shared_ptr<CSerSession>;
class CSerSessionsManager {
private:
mutable std::mutex mutex_;
std::set<sessionPtr> sessions_; // Active Sessions : Online Info
public:
CSerSessionsManager();
~CSerSessionsManager();
void addSession(sessionPtr session);
void dropSession(sessionPtr session);
};
class CBuffer {
public:
enum { buffsize = 32 };
private:
char received_[buffsize];
public:
CBuffer() : received_{} {}
CBuffer(const std::string str)
{
// Truncate if Overflow
auto len = str.size();
if (len >= buffsize) {
len = buffsize - 1;
}
std::copy(str.begin(), str.begin() + len, received_);
received_[len] = '\0';
}
bool empty() const
{
return !received_[0];
}
const std::string getString() const { return std::string(received_); }
const char* getReceived() const { return received_; }
};
class CSerSession : public std::enable_shared_from_this<CSerSession> {
private:
mutable std::mutex mutex_;
mutable tcp::socket socket_; // client connection
CSerSessionsManager& manager_;
std::string ip_;
std::string port_;
CBuffer msg_;
public:
CSerSession(asio::io_service& io_service, CSerSessionsManager& mng) : socket_(io_service), manager_(mng)
{
DEBUG_MSG("Server Session Created");
}
~CSerSession() { DEBUG_MSG("Server Session Destroyed"); }
void startSession()
{
DEBUG_MSG("Server Session Started");
manager_.addSession(shared_from_this()); // Multiple threads should not try adding section
read();
}
tcp::socket& socket() { return socket_; }
private:
void handle_read(const boost::system::error_code& error /*error*/, size_t /*bytes_transferred*/)
{
if (!error) {
read();
} else {
DEBUG_MSG("Read Error Detected : " << error.message());
manager_.dropSession(shared_from_this()); // might throw
}
}
void read()
{
std::lock_guard<std::mutex> lock(mutex_);
DEBUG_MSG("Read");
asio::async_read(socket_, asio::buffer(const_cast<char*>(msg_.getReceived()), msg_.buffsize),
std::bind(&CSerSession::handle_read, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
}
};
CSerSessionsManager::CSerSessionsManager()
{
DEBUG_MSG("Construction");
}
CSerSessionsManager::~CSerSessionsManager()
{
DEBUG_MSG("Destruction");
}
void CSerSessionsManager::addSession(sessionPtr session)
{
std::lock_guard<std::mutex> lock(mutex_);
DEBUG_MSG("Incoming Session Entry saved");
sessions_.insert(session);
}
void CSerSessionsManager::dropSession(sessionPtr session)
{
std::lock_guard<std::mutex> lock(mutex_);
DEBUG_MSG("Session dropped");
sessions_.erase(session);
}
class CServer {
private:
mutable std::mutex mutex_;
asio::io_service& io_;
mutable tcp::acceptor acceptor_; // only in the listener
CSerSessionsManager mng_;
public:
CServer(asio::io_service& io_service, const std::string& IP, int port)
: io_(io_service), acceptor_(io_, tcp::endpoint(asio::ip::address::from_string(IP), port))
{
DEBUG_MSG("Listener Created");
}
~CServer()
{
DEBUG_MSG("Listener Destroyed");
acceptor_.close(); // likely to be redundant
}
void initProtocol()
{
listen();
}
private:
void listen()
{
DEBUG_MSG("!==============================================================!");
sessionPtr newSession = std::make_shared<CSerSession>(io_, mng_);
std::lock_guard<std::mutex> lock(mutex_);
acceptor_.async_accept(newSession->socket(), std::bind(&CServer::handle_accept, this, newSession,
std::placeholders::_1));
}
void handle_accept(sessionPtr newSession, const boost::system::error_code& error)
{
if (error || !acceptor_.is_open()) {
DEBUG_MSG("Listen_Error");
DEBUG_MSG(error.message());
return;
}
DEBUG_MSG("Incoming Session accepted");
newSession->startSession();
listen();
}
};
int main()
{
try
{
asio::io_service io_service;
auto server = std::make_shared<CServer>(io_service, "127.0.0.1", 8973);
server->initProtocol();
boost::thread t(boost::bind(&asio::io_service::run, &io_service));
boost::this_thread::sleep_for(boost::chrono::seconds(3));
t.join();
}
catch (...)
{
}
}
Prints (for a single connection):
CSerSessionsManager : 123 : Construction
CServer : 156 : Listener Created
listen : 173 : !==============================================================!
CSerSession : 86 : Server Session Created
handle_accept : 190 : Incoming Session accepted
startSession : 93 : Server Session Started
addSession : 134 : Incoming Session Entry saved
read : 114 : Read
listen : 173 : !==============================================================!
CSerSession : 86 : Server Session Created
handle_read : 106 : Read Error Detected : End of file
dropSession : 141 : Session dropped
~CSerSession : 89 : Server Session Destroyed
来源:https://stackoverflow.com/questions/28564029/asio-chat-session-class-throws-exception-on-destruction-c-asio