Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to make client send a disconnect packet? #1285

Closed
jedwards1211 opened this issue Jun 4, 2021 · 9 comments
Closed

how to make client send a disconnect packet? #1285

jedwards1211 opened this issue Jun 4, 2021 · 9 comments

Comments

@jedwards1211
Copy link

jedwards1211 commented Jun 4, 2021

In some tests I'm writing I need to make clients voluntarily disconnect, and reconnect later when I tell them to, with same clientId and session resending any QoS 1/2 messages.

Oddly it seems like there's no way to do this....end() seems rather permanent and calling .reconnect() afterwards doesn't seem to do anything.

@dfeprado
Copy link

dfeprado commented Jul 22, 2021

I have a app that have to publish to everyone that it are disconnecting. This is the way I do it:

cli.publish('topic', 'disconnected', 0, {}, () => {cli.end()})

I put this snippet in an disconnect function. Is this what you need?

@redboltz
Copy link
Contributor

@jedwards1211
See the following test case:

it('should resend in-flight QoS 1 publish messages from the client if clean is false', function (done) {
var reconnect = false
var client = {}
var incomingStore = new mqtt.Store({ clean: false })
var outgoingStore = new mqtt.Store({ clean: false })
var server2 = serverBuilder(config.protocol, function (serverClient) {
serverClient.on('connect', function (packet) {
var connack = version === 5 ? { reasonCode: 0 } : { returnCode: 0 }
serverClient.connack(connack)
})
serverClient.on('publish', function (packet) {
if (reconnect) {
server2.close()
client.end(true, done)
} else {
client.end(true, () => {
client.reconnect({
incomingStore: incomingStore,
outgoingStore: outgoingStore
})
reconnect = true
})
}
})
})
server2.listen(ports.PORTAND50, function () {
client = connect({
port: ports.PORTAND50,
host: 'localhost',
clean: false,
clientId: 'cid1',
reconnectPeriod: 0,
incomingStore: incomingStore,
outgoingStore: outgoingStore
})
client.on('connect', function () {
if (!reconnect) {
client.publish('topic', 'payload', {qos: 1})
}
})
client.on('error', function () {})
})
})

incomingStore and outgoingStore can be passed from the outside of the client. And they have clean flag. If it set false, then the Store doesn't clean if the client calls the end(). So the QoS 1/2 messages are preserved after end() is called.

var incomingStore = new mqtt.Store({ clean: false })
var outgoingStore = new mqtt.Store({ clean: false })

When the first client connection, set clean: false and incomingStore and outgoingStore.

client = connect({
port: ports.PORTAND50,
host: 'localhost',
clean: false,
clientId: 'cid1',
reconnectPeriod: 0,
incomingStore: incomingStore,
outgoingStore: outgoingStore
})

When you want to disconnect from the client side using DISCONNECT packet, you can call end() as follows:

client.end(true, () => {
client.reconnect({
incomingStore: incomingStore,
outgoingStore: outgoingStore
})
reconnect = true
})

In this case, client.reconnect() is called just after end() process is finished. But you can call reconnect anywhere.

The important point is passing incomingStore and outgoingStore as the reconnect argument.

Alternative approach:
You can create the new client instead of calling reconnect().
The key is managing incomingStore and outgoingStore by yourself at the outside of the client.

@redboltz
Copy link
Contributor

The test case code set force option true when end() calling.

client.end(true, () => {

In order to send DISCONNECT packet, you need to set force flag to false.
MqttClient.prototype.end = function (force, opts, cb) {

that._cleanUp(force, () => {

MQTT.js/lib/client.js

Lines 1050 to 1074 in d8be59e

MqttClient.prototype._cleanUp = function (forced, done) {
var opts = arguments[2]
if (done) {
debug('_cleanUp :: done callback provided for on stream close')
this.stream.on('close', done)
}
debug('_cleanUp :: forced? %s', forced)
if (forced) {
if ((this.options.reconnectPeriod === 0) && this.options.clean) {
flush(this.outgoing)
}
debug('_cleanUp :: (%s) :: destroying stream', this.options.clientId)
this.stream.destroy()
} else {
var packet = xtend({ cmd: 'disconnect' }, opts)
debug('_cleanUp :: (%s) :: call _sendPacket with disconnect packet', this.options.clientId)
this._sendPacket(
packet,
setImmediate.bind(
null,
this.stream.end.bind(this.stream)
)
)
}

See also #706 and #707.

@jedwards1211
Copy link
Author

jedwards1211 commented Jul 22, 2021

@redboltz Thanks for your examples.

As I mentioned in #1286 (comment), I think it would be better not to store a clean flag on the stores and instead have the client tell the stores whether to discard state when the client is shutting down. It's counterintuitive that passing clean: false in the connect options isn't enough.

@redboltz
Copy link
Contributor

Ah, I just realized that you are the person who create the issue #1286.
I understand.
I think that the current status is we have a way to send disconnect and reconnect with preserving stores but it is not intuitive.
So there is no defect in the behavior but there is a room to improve.
Is that right?

@jedwards1211
Copy link
Author

Right, I wasn't saying there's any incorrect behavior

@YoDaMa
Copy link
Contributor

YoDaMa commented Sep 28, 2021

@redboltz do you think you could summarize this under the vNext discussion? #1324

The policy seems clunky right now, and I agree there's room for improvement.

@YoDaMa YoDaMa self-assigned this Sep 28, 2021
Copy link

github-actions bot commented May 1, 2024

This is an automated message to let you know that this issue has
gone 365 days without any activity. In order to ensure that we work
on issues that still matter, this issue will be closed in 14 days.

If this issue is still important, you can simply comment with a
"bump" to keep it open.

Thank you for your contribution.

@github-actions github-actions bot added the stale label May 1, 2024
Copy link

This issue was automatically closed due to inactivity.

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale May 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants