@@ -57,6 +57,10 @@ public HiveMQClient(HiveMQClientOptions? options = null)
57
57
58
58
this . Options = options ;
59
59
this . cancellationTokenSource = new CancellationTokenSource ( ) ;
60
+ this . ClientReceiveSemaphore = new SemaphoreSlim ( this . Options . ClientReceiveMaximum ) ;
61
+
62
+ // Set protocol default until ConnAck is received
63
+ this . BrokerReceiveSemaphore = new SemaphoreSlim ( 65535 ) ;
60
64
}
61
65
62
66
/// <inheritdoc />
@@ -167,7 +171,9 @@ public async Task<bool> DisconnectAsync(DisconnectOptions? options = null)
167
171
168
172
try
169
173
{
170
- disconnectPacket = await taskCompletionSource . Task . WaitAsync ( TimeSpan . FromSeconds ( 60 ) ) . ConfigureAwait ( false ) ;
174
+ disconnectPacket = await taskCompletionSource . Task
175
+ . WaitAsync ( TimeSpan . FromMilliseconds ( this . Options . ResponseTimeoutInMs ) )
176
+ . ConfigureAwait ( false ) ;
171
177
}
172
178
catch ( TimeoutException )
173
179
{
@@ -179,9 +185,7 @@ public async Task<bool> DisconnectAsync(DisconnectOptions? options = null)
179
185
this . OnDisconnectSent -= eventHandler ;
180
186
}
181
187
182
- await this . HandleDisconnectionAsync ( ) . ConfigureAwait ( false ) ;
183
-
184
- return true ;
188
+ return await this . HandleDisconnectionAsync ( ) . ConfigureAwait ( false ) ;
185
189
}
186
190
187
191
/// <inheritdoc />
@@ -210,8 +214,25 @@ public async Task<PublishResult> PublishAsync(MQTT5PublishMessage message)
210
214
Logger . Trace ( $ "Queuing packet for send: { publishPacket . GetType ( ) . Name } id={ publishPacket . PacketIdentifier } ") ;
211
215
this . OutgoingPublishQueue . Enqueue ( publishPacket ) ;
212
216
213
- // Wait on the QoS 1 handshake
214
- var pubAckPacket = await publishPacket . OnPublishQoS1CompleteTCS . Task . WaitAsync ( TimeSpan . FromSeconds ( 60 ) ) . ConfigureAwait ( false ) ;
217
+ PubAckPacket pubAckPacket ;
218
+ try
219
+ {
220
+ // Wait on the QoS 1 handshake
221
+ pubAckPacket = await publishPacket . OnPublishQoS1CompleteTCS . Task
222
+ . WaitAsync ( TimeSpan . FromMilliseconds ( this . Options . ResponseTimeoutInMs ) )
223
+ . ConfigureAwait ( false ) ;
224
+ }
225
+ catch ( TimeoutException )
226
+ {
227
+ Logger . Error ( "PublishAsync: QoS 1 timeout. No PUBACK response received in time." ) ;
228
+ var disconnectOptions = new DisconnectOptions
229
+ {
230
+ ReasonCode = DisconnectReasonCode . UnspecifiedError ,
231
+ } ;
232
+ await this . DisconnectAsync ( disconnectOptions ) . ConfigureAwait ( false ) ;
233
+ throw ;
234
+ }
235
+
215
236
return new PublishResult ( publishPacket . Message , pubAckPacket ) ;
216
237
}
217
238
else if ( message . QoS == QualityOfService . ExactlyOnceDelivery )
@@ -225,24 +246,20 @@ public async Task<PublishResult> PublishAsync(MQTT5PublishMessage message)
225
246
try
226
247
{
227
248
// Wait on the QoS 2 handshake
228
- packetList = await publishPacket . OnPublishQoS2CompleteTCS . Task . WaitAsync ( TimeSpan . FromSeconds ( 60 ) ) . ConfigureAwait ( false ) ;
249
+ packetList = await publishPacket . OnPublishQoS2CompleteTCS . Task
250
+ . WaitAsync ( TimeSpan . FromMilliseconds ( this . Options . ResponseTimeoutInMs ) )
251
+ . ConfigureAwait ( false ) ;
229
252
}
230
253
catch ( TimeoutException )
231
254
{
232
255
Logger . Error ( "PublishAsync: QoS 2 timeout. No response received in time." ) ;
233
256
234
- // Remove the transaction chain
235
- if ( this . TransactionQueue . Remove ( publishPacket . PacketIdentifier , out var publishQoS2Chain ) )
257
+ var disconnectOptions = new DisconnectOptions
236
258
{
237
- Logger . Debug ( $ "PublishAsync: QoS 2 timeout. Removing transaction chain for packet identifier { publishPacket . PacketIdentifier } .") ;
238
- }
239
-
240
- // Prepare PublishResult
241
- publishResult = new PublishResult ( publishPacket . Message )
242
- {
243
- QoS2ReasonCode = null ,
259
+ ReasonCode = DisconnectReasonCode . UnspecifiedError ,
244
260
} ;
245
- return publishResult ;
261
+ await this . DisconnectAsync ( disconnectOptions ) . ConfigureAwait ( false ) ;
262
+ throw ;
246
263
}
247
264
248
265
foreach ( var packet in packetList )
@@ -331,7 +348,9 @@ public async Task<SubscribeResult> SubscribeAsync(SubscribeOptions options)
331
348
SubscribeResult subscribeResult ;
332
349
try
333
350
{
334
- subAck = await taskCompletionSource . Task . WaitAsync ( TimeSpan . FromSeconds ( 60 ) ) . ConfigureAwait ( false ) ;
351
+ subAck = await taskCompletionSource . Task
352
+ . WaitAsync ( TimeSpan . FromMilliseconds ( this . Options . ResponseTimeoutInMs ) )
353
+ . ConfigureAwait ( false ) ;
335
354
}
336
355
catch ( TimeoutException )
337
356
{
@@ -441,7 +460,9 @@ public async Task<UnsubscribeResult> UnsubscribeAsync(UnsubscribeOptions unsubOp
441
460
UnsubscribeResult unsubscribeResult ;
442
461
try
443
462
{
444
- unsubAck = await taskCompletionSource . Task . WaitAsync ( TimeSpan . FromSeconds ( 60 ) ) . ConfigureAwait ( false ) ;
463
+ unsubAck = await taskCompletionSource . Task
464
+ . WaitAsync ( TimeSpan . FromMilliseconds ( this . Options . ResponseTimeoutInMs ) )
465
+ . ConfigureAwait ( false ) ;
445
466
446
467
// FIXME: Validate that the packet identifier matches
447
468
}
@@ -488,6 +509,9 @@ private async Task<bool> HandleDisconnectionAsync(bool clean = true)
488
509
489
510
// Cancel all background tasks and close the socket
490
511
this . ConnectState = ConnectState . Disconnected ;
512
+
513
+ // Don't use CancelAsync here to maintain backwards compatibility
514
+ // with >=.net6.0. CancelAsync was introduced in .net8.0
491
515
this . cancellationTokenSource . Cancel ( ) ;
492
516
this . CloseSocket ( ) ;
493
517
0 commit comments