Boost Client getting stuck - c++

http://www.boost.org/doc/libs/1_46_0/doc/html/boost_asio/example/chat/chat_client.cpp
I am working on client application based on he example above.
I wanted to do the client connection in separte thread so that UI doesnot get stuck.Here UI is getiing stuck.
1. Can you tell me how to acheive this?
2. what is the meaning of the this line?
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
t.join();
Is this line create the separate thread for connection?
client::client(boost::asio::io_service& io_service, tcp::resolver::iterator endpoint_iterator)
: io_service_(io_service),
resolver_(io_service),
socket_(io_service_)
{
tcp::endpoint endpoint = *endpoint_iterator;
socket_.async_connect( endpoint,
boost::bind(&client::handle_connect, this,boost::asio::placeholders::error,
++endpoint_iterator));
}
void client::handle_connect(const boost::system::error_code& error,
tcp::resolver::iterator endpoint_iterator)
{
strcpy(data_,"Hello");
if (!error)
{
/*boost::asio::async_read(socket_,
boost::asio::buffer(data_, MAX_PATH),
boost::bind(&client::handle_read, this,
boost::asio::placeholders::error));*/
boost::asio::async_write(socket_, boost::asio::buffer(data_, MAX_PATH),
boost::bind(&client::handle_read, this,
boost::asio::placeholders::error));
}
else if (endpoint_iterator != tcp::resolver::iterator())
{
socket_.close();
tcp::endpoint endpoint = *endpoint_iterator;
socket_.async_connect( endpoint,
boost::bind(&client::handle_connect, this,
boost::asio::placeholders::error, ++endpoint_iterator));
}
}
void client::handle_read(const boost::system::error_code& error)
{
if (!error)
{
memset(data_,0,MAX_PATH);
boost::asio::async_read( socket_,
boost::asio::buffer(data_, MAX_PATH),
boost::bind(&client::handle_read, this,
boost::asio::placeholders::error));
if (strcmp(data_,"Hello Response")==0)
{
MessageBox(NULL,_T("Regd Done"),_T("Vue"),1);
// return ;
}
}
}
CConnectionMgr::CConnectionMgr(void)
{
}
void CConnectionMgr::Connect()
{
try
{
char* host = "192.168.4.84";
char* port = "55555";
boost::asio::io_service io_service;
tcp::resolver resolver(io_service);
tcp::resolver::query query(tcp::v4(),host , port);
tcp::resolver::iterator iterator = resolver.resolve(query);
c = new client(io_service, iterator);
//boost::thread thrd(boost::bind(&boost::asio::io_service::run, &io_service));
boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
t.join();
// MessageBox(NULL,_T("Join"),_T("ff"),1);
}
catch (std::exception& e)
{
CString csMsg(e.what());
MessageBox(NULL,csMsg,_T("ff"),1);
}
}

The "t.join()" waits for the thread 't' to exit. Thread 't' is running the run() method on io_service and will exit when there is no remaining I/O to complete.
So, your Connect() method will block until all the I/O is finished, which is clear not what you want. If you are going to do asynchronous I/O so that your client doesn't block, you need to design a way for your I/O context to communicate with our UI context. It won't happen by magic.

Can you tell me how to acheive this?
You could launch the boost thread in your main i.e. somewhere before you enter the UI event loop (the blocking call) but don't do a join.
Have a look at the HTTP server examples: In one of them it's shown how you can start your io_service in the main and stop it via CTRL-c. In your case you would probably do this using a GUI button or event. Once you call the io_service stop method, you can then do the join on the thread.
what is the meaning of the this line? boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service));
Runs the io_service::run in a new thread
t.join();
Waits for the thread to finish like janm stated which will happen once the io_service runs out of work or once the io_service::stop method is called

Related

QT Connect Slot / Signal not working

I am having trouble to connect a Signal to a Slot in the following code:
#include "myserver.h"
MyServer::MyServer(QObject *parent) :
QTcpServer(parent)
{
}
void MyServer::StartServer()
{
if(listen(QHostAddress::Any, 45451))
{
qDebug() << "Server: started";
emit servComando("Server: started");
}
else
{
qDebug() << "Server: not started!";
emit servComando("Server: not started!");
}
}
void MyServer::incomingConnection(int handle)
{
emit servComando("server: incoming connection, make a client...");
// at the incoming connection, make a client
MyClient *client = new MyClient(this);
client->SetSocket(handle);
//clientes.append(client);
//clientes << client;
connect(client, SIGNAL(cliComando(const QString&)),this, SLOT(servProcesarComando(const QString&)));
// para probar
emit client->cliComando("prueba");
}
void MyServer::servProcesarComando(const QString& texto)
{
emit servComando(texto);
}
The emit client->cliComando("prueba"); works, but the real "emits" don't.
The console does not show any connection error, and the QDebug texts shows everything works well.
Original code was copied from http://www.bogotobogo.com/cplusplus/sockets_server_client_QT.php
I found the problem, Im sending a signal BEFORE connecting:
client->SetSocket(handle);
sends the signal, and Im CONNECTing after it... Now it is:
// at the incoming connection, make a client
MyClient *client = new MyClient(this);
connect(client, SIGNAL(cliComando(const QString&)),this, SLOT(servProcesarComando(const QString&)));
client->SetSocket(handle);
And it works. I noticed it after read the following:
13. Put all connect statements before functions calls that may fire their signals, to ensure that the connections are made before the signals are fired. For example:
_myObj = new MyClass();
connect(_myObj, SIGNAL(somethingHappend()), SLOT(doSomething()));
_myObj->init();
not
_myObj = new MyClass();
_myObj->init();
connect(_myObj, SIGNAL(somethingHappend()), SLOT(doSomething()));
I found it at https://samdutton.wordpress.com/2008/10/03/debugging-signals-and-slots-in-qt/
Anyway, thanks for your answers!

Boost asio::deadline_timer is resetting before timeout

I am using boost::asio::deadline_timer to add socket timeout option. I have implemented Asynchronous HTTP read, and I start the deadline_timer when I start connecting with the server and on every callback I reset the deadline_timer with function deadline_timer::expires_from_now. In the error handler of the deadline_timer I am clearly checking if the timeout was actual or operation_aborted. But almost always I receive actual timeout even before expected timeout. Please have a look at my given code. I don't understand in every callback I am resetting the timer then why I am getting this timeout error.
#define TCP_SOCKET_TIMEOUT 10
Http::AsyncDownload::AsyncDownload(boost::asio::io_service& io_service,
const std::string &protocol,
const std::string &serverip,
const std::string &port,
const std::string &path,
const std::string &filename,
const std::string &localFilePath,
const std::string &username,
const std::string &password) :
resolver_(io_service),
socket_(io_service),
timer_(io_service, boost::posix_time::seconds(TCP_SOCKET_TIMEOUT)),
localFilePath_(localFilePath),
downloadFile_(filename),
protocol_(protocol)
{
........
// Start TCP Socket Timer
start_socket_timer();
// Start an asynchronous resolve to translate the server and service names
// into a list of endpoints.
tcp::resolver::query query(serverip, port);
resolver_.async_resolve(query, boost::bind(&AsyncDownload::resolve, this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator)
);
}
void Http::AsyncDownload::resolve(const boost::system::error_code &err,
tcp::resolver::iterator endpoint_iterator)
{
// Ok, we have received one packet, so refresh the socket timer
refresh_socket_timer();
if ( !err ) {
.........
boost::asio::async_connect(ssocket_->lowest_layer(), endpoint_iterator, boost::bind(&AsyncDownload::connect, this, boost::asio::placeholders::error));
} else {
// Error handling here
}
}
void Http::AsyncDownload::connect(const boost::system::error_code& err)
{
// Ok, we have received one packet, so refresh the socket timer
refresh_socket_timer();
if ( !err ) {
.........
boost::asio::async_write(socket_, request_,
boost::bind(&AsyncDownload::write_request, this, boost::asio::placeholders::error));
}
else {
// Error handling here
}
}
void Http::AsyncDownload::hand_shake(const boost::system::error_code& err)
{
// Ok, we have received one packet, so refresh the socket timer
refresh_socket_timer();
if ( !err ) {
.........
boost::asio::async_write(*ssocket_, request_,
boost::bind(&AsyncDownload::write_request, this,
boost::asio::placeholders::error));
} else {
// Error handling here.
}
}
void Http::AsyncDownload::write_request(const boost::system::error_code& err)
{
// Ok, we have received one packet, so refresh the socket timer
refresh_socket_timer();
if ( !err ) {
.............
boost::asio::async_read_until(*ssocket_, response_, "\r\n",
boost::bind(&AsyncDownload::read_status_line, this,
boost::asio::placeholders::error));
} else {
// Error handling here
}
}
void Http::AsyncDownload::read_status_line(const boost::system::error_code& err)
{
// Ok, we have received one packet, so refresh the socket timer
refresh_socket_timer();
if ( !err ) {
..........
boost::asio::async_read_until(*ssocket_, response_, "\r\n\r\n",
boost::bind(&AsyncDownload::read_headers, this,
boost::asio::placeholders::error));
} else {
// Error handling here.
}
}
void Http::AsyncDownload::read_headers(const boost::system::error_code& err)
{
refresh_socket_timer();
if ( !err ) {
..............
boost::asio::async_read(*ssocket_, response_,
boost::asio::transfer_at_least(1),
boost::bind(&AsyncDownload::read_content, this,
boost::asio::placeholders::error)
);
} else {
// Error handling here
}
}
void Http::AsyncDownload::read_content(const boost::system::error_code& err)
{
// Ok, we have received one packet, so refresh the socket timer
refresh_socket_timer();
if ( !err ) {
boost::asio::async_read(*ssocket_, response_,
boost::asio::transfer_at_least(1),
boost::bind(&AsyncDownload::read_content, this,
boost::asio::placeholders::error)
);
} else if ( err != boost::asio::error::eof ) {
// Error handling here.
} else {
// We have an EOF
}
}
void Http::AsyncDownload::start_socket_timer()
{
timer_.async_wait(boost::bind(&Http::AsyncDownload::socket_timeout, this,
boost::asio::placeholders::error));
}
void Http::AsyncDownload::refresh_socket_timer()
{
timer_.expires_from_now(boost::posix_time::seconds(TCP_SOCKET_TIMEOUT));
timer_.async_wait(boost::bind(&Http::AsyncDownload::socket_timeout, this, boost::asio::placeholders::error));
}
void Http::AsyncDownload::socket_timeout(const boost::system::error_code &error_)
{
// operation_aborted error is thrown whenever we cancel the timer or
// we reset the timer using expires_from_now function,
if ( error_ != boost::asio::error::operation_aborted ) {
csputils::Utils::DEBUG_MSG(downloadFile_, __LINE__, " ------------> TCP Connection Timeout. Broken Connection Found. Abort Operation ");
// Ok, our TCP connection is broken, we will cancel all asynchronous
// operations of our sockets.
ssocket_->shutdown(); // For Secure Socket & socket.close(); for normal socket.
} else {
// Ok, we have reset the timer, please continue...
}
}
Ok. In the above code you will notice I am starting the timer in the constructor, and once I receive one packet I am refreshing the timer with expries_from_now function call. This call will call error handler (socket_timeout) with the error code operation_aborted, but for every actual timeout this function will be called without operation_aborted and you can see I am checking operation_aborted explicitly, but still as per my expectation I am receiving timeout early, though I am refreshing the timer on every packet I receive but I am sure it is being expired before 10 seconds. I also tried with timeout value = 60 but same effect. Any thoughts.
UPDATE
Updated my code with error handling I have used in my actual code. I have trimmed down my actual code for the sake of simplicity. You can notice in timer timeout handler, I am checking if the timeout was not explicit (i.e operation_aborted), then close the socket. Once the socket will be closed I will get an exception in my socket data handler (mostly read_content function). In that function when I receive exception my socket will exit calling the AsyncDownload destructor where I am doing some more cleaning. My download code works perfect if I remove deadline_timer. I have added it here to detect unforeseen TCP connection drops. I am running this code on embedded Linux on ARM architecture, and my sockets are secure but as I have mentioned my code works perfect without timer so I don't think the problem has something to do with sockets.
Okay, so, I've played with your example hands on.
I can see an infinite loop happening when the timeout has expired, because there is a lack of error handling, and the read_content loop simply continues despite the fact that the timeout had been reached.
Note:
the first read_until_async suggests that it will only read the statusline. This is simply not the case!
See boost read_until does not stop at delimiter.
It will read the first "swoop" of data that includes a minimum of \r\n once. In practice, many servers send the headers in the same packet. (In fact, effective behaviour might depend on proxies and possibly hardware). So, it's not safe to assume that you should always require the second read_until_async to read the headers. And since another "\r\n\r\n" might never happen, it's easy to get into failure mode (when e.g. end-of-stream is reached).
If you don't carefully watch the flow, it's possible to conclude that "the timer fires too early" (because you land in refresh_socket_timer, for example). However, what is happening in my error situation is that read_until_async simply immediately returns, which is erronously treated as if a packet was returned.
So I propose something like
void read_content(const boost::system::error_code& err)
{
if (err && socket_.is_open())
{
DEBUG_TRACE();
boost::asio::async_read(*ssocket_, response_,
boost::asio::transfer_at_least(1),
boost::bind(&AsyncDownload::read_content, this,
boost::asio::placeholders::error)
);
refresh_socket_timer();
}
else
std::cerr << "Error '" << err.message() << "'\n";
}
So we avoid the infinite loop on connection dropped.
void refresh_socket_timer()
{
if (socket_.is_open())
{
DEBUG_TRACE();
timer_.expires_from_now(boost::posix_time::seconds(TCP_SOCKET_TIMEOUT));
timer_.async_wait(boost::bind(&AsyncDownload::socket_timeout, this, boost::asio::placeholders::error));
}
}
So we avoid refreshing the timer after the socket has been closed.
void socket_timeout(const boost::system::error_code &error_)
{
// operation_aborted error is thrown whenever we cancel the timer or
// we reset the timer using expires_from_now function,
if ( error_ != boost::asio::error::operation_aborted ) {
DEBUG_TRACE();
std::cout << " ------------> TCP Connection Timeout. Broken Connection Found. Abort Operation\n";
// Ok, our TCP connection is broken, we will cancel all asynchronous
// operations of our sockets.
socket_.close();
}
}
So we actively close the socket on timeout.
If you comment the if(...) conditionals out in the above, you'd see the failure mode I was describing.
Here's the full example I used to test:
#include <boost/asio.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/bind.hpp>
using tcp = boost::asio::ip::tcp;
#define TCP_SOCKET_TIMEOUT 2
#define DEBUG_TRACE() do { std::cout << __FILE__ << ':' << __LINE__ << "\t" << __FUNCTION__ << "\n"; } while(false)
struct AsyncDownload
{
tcp::resolver resolver_;
tcp::socket socket_;
tcp::socket* ssocket_ = &socket_;
boost::asio::deadline_timer timer_;
std::string localFilePath_;
boost::asio::streambuf response_;
AsyncDownload(boost::asio::io_service& io_service,
const std::string &protocol,
const std::string &serverip,
const std::string &port) :
resolver_(io_service),
socket_(io_service),
timer_(io_service, boost::posix_time::seconds(TCP_SOCKET_TIMEOUT))
{
DEBUG_TRACE();
// Start TCP Socket Timer
start_socket_timer();
// Start an asynchronous resolve to translate the server and service names
// into a list of endpoints.
tcp::resolver::query query(serverip, port);
resolver_.async_resolve(query, boost::bind(&AsyncDownload::resolve, this,
boost::asio::placeholders::error,
boost::asio::placeholders::iterator)
);
}
void resolve(const boost::system::error_code &err, tcp::resolver::iterator endpoint_iterator)
{
DEBUG_TRACE();
boost::asio::async_connect(ssocket_->lowest_layer(), endpoint_iterator, boost::bind(&AsyncDownload::connect, this, boost::asio::placeholders::error));
refresh_socket_timer();
}
void connect(const boost::system::error_code& err)
{
DEBUG_TRACE();
std::string const request_ = "GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n";
boost::asio::async_write(socket_, boost::asio::buffer(request_),
boost::bind(&AsyncDownload::write_request, this, boost::asio::placeholders::error));
refresh_socket_timer();
}
void write_request(const boost::system::error_code& err)
{
DEBUG_TRACE();
boost::asio::async_read_until(*ssocket_, response_, "\r\n",
boost::bind(&AsyncDownload::read_status_line, this,
boost::asio::placeholders::error));
refresh_socket_timer();
}
void read_status_line(const boost::system::error_code& err)
{
DEBUG_TRACE();
std::cout << "read_status_line: " << &response_ << "\n";
boost::asio::async_read_until(*ssocket_, response_, "\r\n\r\n",
boost::bind(&AsyncDownload::read_headers, this,
boost::asio::placeholders::error));
refresh_socket_timer();
}
void read_headers(const boost::system::error_code& err)
{
DEBUG_TRACE();
// ..............
boost::asio::async_read(*ssocket_, response_,
boost::asio::transfer_at_least(1),
boost::bind(&AsyncDownload::read_content, this,
boost::asio::placeholders::error)
);
refresh_socket_timer();
}
void read_content(const boost::system::error_code& err)
{
if (err && socket_.is_open())
{
DEBUG_TRACE();
boost::asio::async_read(*ssocket_, response_,
boost::asio::transfer_at_least(1),
boost::bind(&AsyncDownload::read_content, this,
boost::asio::placeholders::error)
);
refresh_socket_timer();
}
else
std::cerr << "Error '" << err.message() << "'\n";
}
void start_socket_timer()
{
DEBUG_TRACE();
timer_.async_wait(boost::bind(&AsyncDownload::socket_timeout, this, boost::asio::placeholders::error));
}
void refresh_socket_timer()
{
if (socket_.is_open())
{
DEBUG_TRACE();
timer_.expires_from_now(boost::posix_time::seconds(TCP_SOCKET_TIMEOUT));
timer_.async_wait(boost::bind(&AsyncDownload::socket_timeout, this, boost::asio::placeholders::error));
}
}
void socket_timeout(const boost::system::error_code &error_)
{
// operation_aborted error is thrown whenever we cancel the timer or
// we reset the timer using expires_from_now function,
if ( error_ != boost::asio::error::operation_aborted ) {
DEBUG_TRACE();
std::cout << " ------------> TCP Connection Timeout. Broken Connection Found. Abort Operation\n";
// Ok, our TCP connection is broken, we will cancel all asynchronous
// operations of our sockets.
socket_.close();
}
}
};
int main()
{
DEBUG_TRACE();
boost::asio::io_service io_service;
AsyncDownload download(
io_service,
"http",
"www.google.com",
"80");
io_service.run();
}

QTcpServer not receiving any data when working in QRunnable

What I'm having is a strange problem in typical scenario: QTcpServer's method incomingConnection is overrided in custom class, and any received connection is planned for processing in separate thread on QThreadPool.
Server:
void FooS::incomingConnection(qintptr socketDescriptor)
{
QThreadPool *thread_pool = QThreadPool::globalInstance();
FooSocket *fs = new FooSocket();
fs->setSocket(socketDescriptor);
thread_pool->start(fs);
}
Task:
class FooSocket: public QObject, public QRunnable;
...
private slots:
void connectionIncomingData();
...
void FooSocket::run() {
QTcpSocket *socket = new QTcpSocket();
qDebug() << "SD: " << socketDescriptor; // is correct
if (!socket->setSocketDescriptor(socketDescriptor)) {
qDebug() << "Can't set socket descriptor";
emit error(socket->error());
return;
}
// -- had no effect here
// socket->moveToThread(QThread::currentThread());
connect(socket, SIGNAL(readyRead()), this, SLOT(connectionIncomingData()));
connect(socket, SIGNAL(disconnected()), this, SLOT(connectionClosed()));
}
readyRead signal doesn't gets triggered, but socket client is confirmed (tcpdump) to send data..
After making QRunnable to spawn a QThread object with socket logics inside, and toying with setAutoDelete, moveToThread - still no effect.
In order to process events in a QRunnable, a thread needs to have its own event loop, it mustn't rely on the one from the main thread. From what you've shown in your code, your thread quickly starts, then exits without running a loop.
Try adding
QEventLoop loop;
// connect a signal to the event loop's quit() slot
loop.exec();

Socket closed but the Session reference is not be properly destroyed

I'm writing a multi-thread server using the boost::asio. There is a pool with X threads, using asynchronous reads and writes (base on this example).
The server structure seems like that:
Server
Manage the threads of program and start the async_accept creating new Session for each new client.
Session
Represents the client itself. Have the socket ref and manage the async_reads and async_writes from her socket(client). Also have the timeout management.
Sometimes the client (a hardware device) freezes and my server don't have answer from him. To solve this I read about using the async_wait with deadline_timer (like this example) and I applied it to my software, but something strange happened:
When a normal disconnection happens the async operations are cancelled (reaching a operation_aborted error) and the Session object is destroyed. But when the device freezes, the socket is closed but the Session object isn't destroyed and his instance remains in memory, even the socket.close() already called.
I simplified the code and put below:
server.h
class Server
{
private:
boost::asio::io_service& _io_service;
boost::asio::ip::tcp::acceptor* _acceptor;
boost::asio::ip::tcp::endpoint* _endpoint;
boost::asio::signal_set _signals;
Session_SP _session;
public:
Server(boost::asio::io_service& io_service);
~Server();
/**
* Queues async accept action.
*/
virtual void queueAccept();
/**
* Async accept handler.
*/
virtual void handleAccept(const boost::system::error_code& error);
/**
* Start the server
*/
virtual void run();
boost::asio::io_service& getIOService();
/**
* Shutdown the service
*/
virtual void shutdown();
};
server.cpp
#include "server.hpp"
Server::Server(boost::asio::io_service& io_service):
_io_service(io_service), _signals(io_service)
{
this->_endpoint = new boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), config.getServer().port);
this->_acceptor = new boost::asio::ip::tcp::acceptor(io_service);
this->_acceptor->open(boost::asio::ip::tcp::v4());
this->_acceptor->bind(*this->_endpoint);
this->_acceptor->listen();
this->_signals.add(SIGINT);
this->_signals.add(SIGTERM);
#if defined(SIGQUIT)
this->_signals_.add(SIGQUIT);
#endif // defined(SIGQUIT)
this->_signals.async_wait(boost::bind(&Server::shutdown, this));
this->queueAccept();
}
Server::~Server()
{
delete this->_acceptor;
delete this->_endpoint;
}
void Server::queueAccept()
{
this->_session.reset(new Session(*this));
_acceptor->async_accept(
this->_session->getSocket(),
boost::bind(
&Server::handleAccept,
this,
boost::asio::placeholders::error
)
);
}
void Server::handleAccept(const boost::system::error_code& error)
{
if (!error)
this->_session->start();
this->queueAccept();
}
boost::asio::io_service& Server::getIOService()
{
return this->_io_service;
}
void Server::shutdown()
{
this->_io_service.stop();
}
session.h
class Session:
public boost::enable_shared_from_this<Session>
{
public:
Session(Server& server);
~Session();
bool stopped() const;
virtual void start();
virtual boost::asio::ip::tcp::socket& getSocket();
virtual void disconnect();
/**
* Async read handler
*/
void handleRead(const boost::system::error_code& error, size_t bytesTransfered);
/**
* Async write handler
*/
void handleWrite(const boost::system::error_code& error);
/**
* Queues write action.
*/
void queueWrite();
/**
* Push a packet to be sent on queue end
*/
void pushPacket(protocols::SendPacket &packet);
void handleDeadlineAsyncWait(boost::asio::deadline_timer* deadline);
void handleDeadlineAsyncWaitKillConnection(boost::asio::deadline_timer* deadline);
private:
Server& _server;
boost::asio::ip::tcp::socket _socket;
boost::asio::io_service* _ioService;
boost::asio::io_service::strand _strand;
boost::asio::deadline_timer _input_deadline;
boost::asio::deadline_timer _non_empty_output_queue;
/**
* Queue that stores the packet to be sent.
*/
protocols::SendPacketQueue _writeQueue;
/**
* Referência do pacote que será atualizado.
*/
Protocol* _protocol;
/**
* Queues the async_read acction.
*/
virtual void queueRead();
virtual void _pushPacket(protocols::SendPacket &packet);
};
typedef boost::shared_ptr<Session> Session_SP;
session.cpp
#include "session.hpp"
Session::Session(Server& server):
_server(server), _socket(server.getIOService()), _protocol(NULL),
_ioService(&server.getIOService()), _strand(server.getIOService()),
_input_deadline(server.getIOService()),
_non_empty_output_queue(server.getIOService())
{
this->_input_deadline.expires_at(boost::posix_time::pos_infin);
this->_non_empty_output_queue.expires_at(boost::posix_time::pos_infin);
}
Session::~Session()
{
}
bool Session::stopped() const
{
return !_socket.is_open();
}
boost::asio::ip::tcp::socket& Session::getSocket()
{
return this->_socket;
}
void Session::disconnect()
{
this->_input_deadline.cancel();
this->_non_empty_output_queue.cancel();
try
{
this->getSocket().close();
LOG("Session::disconnect : close successful!");
}
catch (void* e)
{
// Never reached here!!
}
}
void Session::queueRead()
{
this->_input_deadline.expires_from_now(boost::posix_time::seconds(30));
boost::asio::async_read_until(
_socket,
_buffer,
"\x004", // Just a test
this->_strand.wrap(boost::bind(
&Session::handleRead,
this->shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
))
);
}
void Session::start()
{
this->queueRead();
this->_input_deadline.async_wait(
this->_strand.wrap(boost::bind(
&Session::handleDeadlineAsyncWait,
shared_from_this(),
&this->_input_deadline
))
);
this->queueWrite();
}
void Session::handleRead(const boost::system::error_code& error, size_t bytesTransfered)
{
if (this->stopped())
return;
if (!error)
{
// ... a lot of code here, but isn't important
}
else if (error != boost::asio::error::operation_aborted)
this->disconnect();
}
void Session::handleWrite(const boost::system::error_code& error)
{
if (this->stopped())
return;
if (!error)
{
this->_writeQueue.pop_front(); // Dequeue
this->queueWrite();
}
else
{
if (error != boost::asio::error::operation_aborted)
this->disconnect();
}
}
void Session::queueWrite()
{
if (this->stopped())
return;
if (this->_writeQueue.empty())
{
this->_non_empty_output_queue.expires_at(boost::posix_time::pos_infin);
this->_non_empty_output_queue.async_wait(
boost::bind(&Session::queueWrite, shared_from_this())
);
}
else
{
this->_input_deadline.expires_from_now(boost::posix_time::seconds(this->_server.getConfig().getServer().timeout));
boost::asio::async_write(
this->getSocket(),
boost::asio::buffer(
this->_writeQueue.front().getData(),
this->_writeQueue.front().getDataSize()
),
this->_strand.wrap(boost::bind(
&Session::handleWrite,
this,
boost::asio::placeholders::error
))
);
}
}
void Session::handleDeadlineAsyncWait(boost::asio::deadline_timer* deadline)
{
if (this->stopped())
return;
if (deadline->expires_at() <= boost::asio::deadline_timer::traits_type::now())
{
boost::system::error_code sdEc;
this->getSocket().shutdown(boost::asio::ip::tcp::socket::shutdown_send, sdEc);
deadline->expires_from_now(boost::posix_time::seconds(15));
deadline->async_wait(
this->_strand.wrap(boost::bind(
&Session::handleDeadlineAsyncWaitKillConnection,
shared_from_this(),
deadline
))
);
}
else
{
deadline->async_wait(
this->_strand.wrap(boost::bind(
&Session::handleDeadlineAsyncWait,
shared_from_this(),
deadline
))
);
}
}
void Session::handleDeadlineAsyncWaitKillConnection(boost::asio::deadline_timer* deadline)
{
this->disconnect();
}
Your async_wait timeout handler should cancel the outstanding async_read() instead of just shutdown the socket, otherwise the socket will remain open.
void Session::handleDeadlineAsyncWait(boost::asio::deadline_timer* deadline)
{
if (this->stopped())
return;
if (deadline->expires_at() <= boost::asio::deadline_timer::traits_type::now())
{
boost::system::error_code sdEc;
this->getSocket().shutdown(boost::asio::ip::tcp::socket::shutdown_send, sdEc);
this->getSocket().cancel(); // <-- add this
}
else
{
deadline->async_wait(
this->_strand.wrap(boost::bind(
&Session::handleDeadlineAsyncWait,
shared_from_this(),
deadline
))
);
}
}
additionally, in your Session::handleRead() handler, you should detect the boost::asio::error::operation_aborted error since that means the read was canceled.
I just found the problem.
The error is in the function Session::quereWrite.
session.cpp
void Session::queueWrite()
{
if (this->stopped())
return;
if (!this->_writeQueue.empty())
{
boost::asio::async_write(
this->getSocket(),
boost::asio::buffer(
this->_writeQueue.front().getData(),
this->_writeQueue.front().getDataSize()
),
this->_strand.wrap(boost::bind(
&Session::handleWrite,
this,
boost::asio::placeholders::error
))
);
}
}
I remove the _non_empty_output_queue and use another way to do the same thing, using the pushPack method.
The problem was that async_wait calls the queueWrite method and he called another async_wait to himself, but with a expired deadline timer causing a overhead on processor and preveting the Session self destruction.
Thank you #Sam Miller by your great contribution.

Send UDP packet to a public IP using Boost.asio

I want to send UDP packets to my static IP 122.***.***.*** where an UDP server listens on port 1213.
udp::resolver resolver(io_serviceSend);
udp::resolver::query query(udp::v4(),"122.***.***.***","1213");
udp::endpoint receiver_endpoint = *resolver.resolve(query);
udp::socket socket(io_serviceSend);
socket.open(udp::v4());
boost::shared_ptr<std::string> message(new std::string("Transfer"));
socket.send_to(boost::asio::buffer(*message), receiver_endpoint);
but this fails. Is it because i am sending it to the private port rather than sending it to the NAT's port. but for TCP it works perfectly.
Can some one explain me the theory or post a good link.
and my server code is
UDPTunnel::UDPTunnel(boost::asio::io_service& io_service)
:socket_(io_service, udp::endpoint(udp::v4(), 1213))
{
start_receive();
}
void UDPTunnel::start_receive()
{
socket_.async_receive_from(
boost::asio::buffer(recv_buffer_), remote_endpoint_,
boost::bind(&UDPTunnel::handle_receive, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
cout << "\nUDP Started To Listening";
}
void UDPTunnel::handle_receive(const boost::system::error_code& error,std::size_t)
{
cout << "\nUDP Recieved Message";
if (!error || error == boost::asio::error::message_size)
{
cout << "\nRecieved Message";
}
}