boost::asio::ip::tcp::resolver::iterator Check if values are null - c++

I've got a block of code that throws an Access Violation when executed. It is the async_connect handler for boost::asio.
//------------------------------------------------------------------------------
void MyClient::OnConnect(const boost::system::error_code & errorCode, boost::asio::ip::tcp::resolver::iterator endpoint)
{
if (errorCode || endpoint == boost::asio::ip::tcp::resolver::iterator())
{
// Error - An error occured while attempting to connect
// Most likely these error codes correspond to https://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx
std::ostringstream msg;
msg << "An error occured while attempting to connect to " << endpoint->host_name()
<< ". Error code: " << errorCode
<< ". Error Message: " << ErrorCodeToString(errorCode);
LogError(msg.str().c_str(), __FUNCSIG__, __FILE__, __LINE__);
return;
}
// We connected to an endpoint
m_connectionState |= CONNECTED;
In the debugger it looks like the problem is inside endpoint->host_name(), because it tried to get values_[0] while values_ are null.
This is a common Connection Refused scenario. I thought the handler got the endpoint so that it knew who it was trying to connect to! Is there some manner of check I can do on the iterator before I try to call a method on it?
It seems to pass and still throw access violation on
if( endpoint != boost::asio::ip::tcp::resolver::iterator() )
{
std::string blah = endpoint->host_name();
}

Probably you've figured this out by now, but I'll post a few lines in case someone else happens upon it.
This problem looks similar to one I had.
This will not work, and will give similar results to what you're describing.
void establish_connection() {
tcp::resolver resolver(get_asio_io_service()); // this is a problem
tcp::resolver::query q{server_name, "https"};
resolver.async_resolve(q,
[&](asio::error_code ec,
tcp::resolver::iterator it) {
this->handle_resolve(ec, it);
});
}
where establish_connection() is a method in the object that deals with communication to the server.
The resolver object disappears after the establish_connection() exits. You have to find a way to make it stick around. The various demos on the web have it as an attribute of the client object.
Please share your experience with this problem.

Related

Is asio::serial_port able to check physical disconnection?

I'm working with boost::asio library for serial communications, and got some problems using it. Below is my code with the problem.
std::unique_ptr<asio::serial_port> port_;
asio::io_service io_;
// Connect serial port 'COM8'
port_ = std::make_unique<asio::serial_port>(asio::serial_port(io_, "COM8"));
std::cout << port_->is_open() << std::endl; // True
Sleep(5000);
/// **Now I unplug the device connected to the COM8 port of my PC.**
std::cout << port_->is_open() << std::endl;
/// Still printed true.
/// I think the reason #asio::serial_port::is_open() returns true
/// is because I didn't called #asio::serial_port::close() before.
/// Then how can I check the physical disconnection?
After I unplugged the device, how can I know whether the device is still available in programmatically?
You can detect it when attempting a read/write operation:
E.g. from the exception overload of write_some:
boost::system::system_error
Thrown on failure. An error code of boost::asio::error::eof indicates
that the connection was closed by the peer.
Thanks to #sehe, this is my solution. (method which has port_ as its private memeber variable)
inline bool _IsConnected() {
bool connected = (port_ != nullptr && port_->is_open());
if (connected) {
try {
// Sned any string that doesn't define
// in your communication protocol.
std::string s = "0";
port_->write_some(asio::buffer("0"));
}
catch (const std::exception& err) {
connected = false;
}
}
return connected;
}

DeleteIPAddress function works, but triggers a disconnection

I have a small app that temporary adds several (3-4) IP addresses using AddIPAddress function from IP Helper. After some seconds (2-3), it deletes these addresses using DeleteIPAddress function.
The function DeleteIPAddress works and returns no error, but sometimes (once every 3 or 4 rounds of adding and deleting) it triggers a disconnection event on the interface (exactly like disconnecting and connecting the cable).
I want to avoid these disconnections as they cut any communication being done in the same interface, but I cannot see anything in the documentation regarding this behavior.
This is how I add each IP address:
auto dwRetVal = AddIPAddress(iaIPAddress, iaIPMask, _idx, &NTEContext, &NTEInstance);
if (dwRetVal == ERROR)
{
std::cout << "Error on AddIPAddress" << std::endl;
}
And this is how I delete them:
auto dwRetVal = DeleteIPAddress(ipContext);
if (dwRetVal != NO_ERROR)
{
std::cout << "Error on DeleteIPAddress" << std::endl;
}
Am I missing something?
You are not checking the return value from AddIPAddress correctly, you should test != ERROR_SUCCESS.
If AddIPAddress fails you should not call delete.

QObject setProperty in a for loop reading an asio socket

I have a Qt C++ app, and I want to update a Text QML Type text property in the QML part while reading from a socket using asio.
The following code is invoked by a QML Signal which is connected to a C++ Signal slot.
try { // try to connect to the server
asio::connect(socket, endpoint_iterator);
// listen to server
for (;;){
std::vector<char> buf(128);
asio::error_code error;
size_t len = socket.read_some(asio::buffer(buf) , error);
if (error == asio::error::eof) {
break;
} // Connection closed cleanly by peer.
else if (error) {
throw asio::system_error(error);
} // Some other error.
qDebug() << buf.data();
qDebug() << "msg len: " << len;
if (serverTextOutput){
serverTextOutput->setProperty("text", buf.data() );
}
}
where serverTextOutput is my Text QML Type object.
This code works fine when I remove the for loop so I suspect the issue is that as the function never returns the QML UI is not updated (the new text property is not displayed). How should I handle this ?

Handling "reset by peer" scenario with boost::asio

I have a server method that waits for new incoming TCP connections, for each connection I'm creating two threads (detached) for handling various tasks.
void MyClass::startServer(boost::asio::io_service& io_service, unsigned short port) {
tcp::acceptor TCPAcceptor(io_service, tcp::endpoint(tcp::v4(), port));
bool UARTToWiFiGatewayStarted = false;
for (;;) {
auto socket(std::shared_ptr<tcp::socket>(new tcp::socket(io_service)));
/*!
* Accept a new connected WiFi client.
*/
TCPAcceptor.accept(*socket);
socket->set_option( tcp::no_delay( true ) );
MyClass::enableCommunicationSession();
// start one worker thread.
std::thread(WiFiToUARTWorkerSession, socket, this->LINport, this->LINbaud).detach();
// only if this is the first connected client:
if(false == UARTToWiFiGatewayStarted) {
std::thread(UARTToWifiWorkerSession, socket, this->UARTport, this->UARTbaud).detach();
UARTToWiFiGatewayStarted = true;
}
}
}
This works fine for starting the communication, but the problem appears when the client disconnects and connects again (or at least tries to connect again).
When the current client disconnects, I stop the communication (by stopping the internal infinite loops from both functions, then they'll return).
void Gateway::WiFiToUARTWorkerSession(std::shared_ptr<tcp::socket> socket, ...) {
/*!
* various code here...
*/
try {
while(true == MyClass::communicationSessionStatus) {
/*!
* Buffer used for storing the UART-incoming data.
*/
unsigned char WiFiDataBuffer[max_incoming_wifi_data_length];
boost::system::error_code error;
/*!
* Read the WiFi-available data.
*/
size_t length = socket->read_some(boost::asio::buffer(WiFiDataBuffer), error);
/*!
* Handle possible read errors.
*/
if (error == boost::asio::error::eof) {
break; // Connection closed cleanly by peer.
}
else if (error) {
// this will cause the infinite loops from the both worker functions to stop, and when they stop the functions will return.
MyClass::disableCommunicationSession();
sleep(1);
throw boost::system::system_error(error); // Some other error.
}
uart->write(WiFiDataBuffer, length);
}
}
catch (std::exception &exception) {
std::cerr << "[APP::exception] Exception in thread: " << exception.what() << std::endl;
}
}
I expect that when I reconnect the communication should work again (the MyClass::startServer(...) will create and detach again two worker threads that will do the same things.
The problem is that when I connect the second time I get:
terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >'
what(): write: Broken pipe
From what I found about this error it seems that the server (this application) sends something via TCP to a client that was disconnected.
What I'm doing wrong?
How can I solve this problem?
A read of length 0 with no error is also an indication of eof. The boost::asio::error::eof error code is normally more useful when you're checking the result of a composed operation.
When this error condition is missed, the code as presented will call write on a socket which has now been shutdown. You have used the form of write which does not take a reference to an error_code. This form will throw if there is an error. There will be an error. The read has failed.

Excessive message size with boost asio and SSL

I am currently working on a server that uses SSL to secure the transmissions. After accepting a conenction, the server makes the hadshake and if it succeeds, it sends a packet to the client.
My code is working perfectly fine on Windows, but when I run it on Debian 7, I get an "Excessive message size" error when I try to write that packet.
I'm pretty sure it does not come from my certificates, since I tried running the boost asio SSL example with the same certificates and it works perfectly fine.
Here is the code:
Initialization of the server:
Server::Server(configuration::Configuration const & conf, utils::Logger & log): _cmds(conf, log), _conf(conf), _logger(log), _ctx(boost::asio::ssl::context::sslv23)
{
tcp::endpoint endpoint(tcp::v4(), conf.getPort());
configuration::SSL_conf ssl = conf.getSLLInfos();
try
{
this->_ctx.set_options(boost::asio::ssl::context::default_workarounds
| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::single_dh_use);
this->_ctx.set_password_callback(boost::bind(&Server::_getPass, this, _1, _2));
this->_ctx.use_certificate_file(ssl.certificate.string(), boost::asio::ssl::context_base::pem);
this->_ctx.use_private_key_file(ssl.key.string(), boost::asio::ssl::context_base::pem);
this->_ctx.use_tmp_dh_file(ssl.certificate.string());
this->_acceptor.reset(new tcp::acceptor(this->_service, endpoint));
this->_logger.logMsg(3, "Starting listen on port " + std::to_string(endpoint.port()));
for (int i = 0; i < 256; ++i)
this->_c.push_back(std::shared_ptr<Client>(new Client(this->_service, this->_cmds, conf, log, i, this->_ctx)));
this->_acceptor->async_accept(this->_c.front()->getSocket(),
boost::bind(&Server::_handleAccept, this,
this->_c.front(),
boost::asio::placeholders::error));
}
catch (boost::system::system_error & err)
{
this->_logger.logErr(err.what());
}
}
The initialization of the session:
void Client::init()
{
this->_logger.logMsg(3, "Client [" + std::to_string(this->_id) + "] initialized");
this->_isAlive = true;
this->_socket.async_handshake(boost::asio::ssl::stream_base::server,
boost::bind(&Client::handleHandshake, this,
boost::asio::placeholders::error));
}
The handhsake callback:
void Client::handleHandshake(boost::system::error_code const & err)
{
if (!err)
{
memset(&this->_header, 0, sizeof(protocol::header));
this->_keepAlive = this->_conf.getKeepAlive();
this->_header.keepAlive = 1;
this->_logger.logStruct<protocol::header>("Client [" + std::to_string(this->_id) + "] Sending handhsake: ", this->_header);
boost::asio::async_write(this->_socket, boost::asio::buffer(&this->_header, sizeof(protocol::header)),
boost::bind(&Client::handleWrite, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
this->kill();
}
And finally, the write callback where I receive the error:
void Client::handleWrite(boost::system::error_code const & err, std::size_t)
{
if (!err && this->_keepAlive)
{
clearBuff(this->_outBuff);
boost::asio::async_read(this->_socket, boost::asio::buffer(&this->_header, sizeof(protocol::header)),
boost::bind(&Client::handleHeaderRead, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
this->_logger.logErr("Client [" + std::to_string(this->_id) + "] write failed : " + err.message());
this->kill();
}
}
As I said the handleWrite callback receives an error with the value "Excessive message size". I really don't understand why since my use of boost asio is pretty standard and my certificates, that are self signed work with other applications.
Thank you for helping.
Try to configure a specific SSL protocol version (e.g. only SSLv3). This reduces the list of items to be sent.
Also, consider sending a minimized certificate bundle (in the certificate file on the server) because the client won't be interested in most root certificates in the chain, unless it already knows them anyways
From your description, the problem is not with the SSL handshake itself, but with the buffer in the call to async_write in Client::handleHandshake.
I'd recommend logging both sizeof(_header) and sizeof(protocol::header) and comparing them in both implementations.
BTW where do you allocate the memory for _header? Is it done in the Constructor? Because it's not done in Client::init()
Alright, so as said in a comment, the problem was that for some reason, letting the client and server decide which version to choose from seemed to screw things up. By forcing the use of SSLV3, it solve it. Thank you all for your help though.