eclipse/paho.mqtt.c

'Disconnected' callback not firing

fpagliughi opened this issue · 8 comments

I'm testing a release candidate for the next version of the Paho C++ client against Paho C v1.3.13, and, with a C++ v5 subscriber sample, it seems that a 'Disconnect' packet from the server is not being processed. This is with the Async C client.

I think, maybe the events are not being ordered properly, and the C library is processing the Disconnect request packet after the socket was closed? And perhaps discarding it?

I have a "disconnected" callback registered with MQTTAsync_setDisconnected().

I also have a "connectionLost" callback registered with MQTTAsync_setConnectionLostCallback(). This one does fire.

I'm testing against Mosquitto 2.0.18, running on Ubuntu 22.04. After connecting my client, I shutdown Mosquito, and it puts a DISCONNECT(reason = 0) packet on the wire. The client seems to get the Disconnect packet, but doesn't process it.

Here are the Logs:

...
19691231 190000.000 Return code 0 from poll
19691231 190000.000 Return code 0 from poll
19691231 190000.000 Return code 0 from poll
19691231 190000.000 Return code 1 from poll
20231113 143211.115 m->c->connect_state = 0                          <- Sees socket closed?
20231113 143211.115 Error from MQTTAsync_cycle() - removing socket 3
20231113 143211.115 3 PahoCppAsyncConsumeV5 -> DISCONNECT (0)        <- Now sees the DISCONNECT packet?
20231113 143211.115 Removed socket 3                                   ...but the "disconnected" callback doesn't fire
20231113 143211.115 Removed socket 3
20231113 143211.115 Calling connectionLost for client PahoCppAsyncConsumeV5    <- Now fires connectionLost
*** Connection Lost ***

It appears that this block never runs at all:

else if (pack->header.bits.type == DISCONNECT)
{
Ack* disc = (Ack*)pack;
int discrc = 0;
discrc = disc->rc;
if (m->disconnected)
{
Log(TRACE_MIN, -1, "Calling disconnected for client %s", m->c->clientID);
(*(m->disconnected))(m->disconnected_context, &disc->properties, disc->rc);
}
rc = MQTTProtocol_handleDisconnects(pack, m->c->net.socket);
m->c->connected = 0; /* don't send disconnect packet back */
nextOrClose(m, discrc, "Received disconnect");
}

That DISCONNECT in the trace is an attempt to send one out, not one received, by the look of it.

It looks like the socket close is detected and no DISCONNECT packet.

In circumstances like this I seem to remember having to flush the TCP write before closing the socket to ensure the last data is sent. Maybe it's an issue like this on the sending side?

Maybe a wireshark trace could shed some more light.

I ran a test where I forced the Paho test broker to send a disconnect packet, and the client application saw it, and called the disconnect callback when it had been set. The client side trace on receiving the DISCONNECT packet looked like this:

 Trace : 4, 19700101 010000.000 3 paho-c-pub <- DISCONNECT (147)

I will double check my findings, but lean towards believing you :-)

I had a bug in the Rust library over the summer where I forgot to handle the on_disconnect() internally. When I went to fix the same issue in C++, I wasn't seeing the disconnect. Sounds like I ran the test differently.

Well all I can say is that I forced the situation in my broker, sending a disconnect packet, and the client application saw it and called the disconnected callback :-)

How do I get the test broker to send the disconnect packet?

I used the Python broker in paho.mqtt.testing and changed the code to force it to send a disconnect packet at some point. I think I included a small wait before closing the socket to make sure it was sent.

Oh, yes, I tried it with the 'testing' broker and it worked as expected. Apologies for the distraction.