8.5 KiB
8.5 KiB
MQTT Quick Reference - Client Migration
🚨 Kritische Änderungen
Port-Änderung (WICHTIG!)
Alt: mqtt-2.assecutor.de:1883
Neu: mqtt-2.assecutor.de:42099
Authentifizierung (NEU!)
Username: app
Password: apppwd
Timeout-Erhöhung
Connection Timeout: 60 Sekunden (vorher 30s)
Keep-Alive: 60 Sekunden (vorher 30s)
📋 Flutter/Dart Code-Snippets
1. Verbindung herstellen
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_server_client.dart';
Future<MqttServerClient> connectToMqtt(String clientId) async {
final client = MqttServerClient.withPort(
'mqtt-2.assecutor.de',
clientId,
42099, // NEUER PORT!
);
client.keepAlivePeriod = 60;
client.connectTimeoutPeriod = 60000;
client.autoReconnect = true;
client.setProtocolV311();
final connMessage = MqttConnectMessage()
.withClientIdentifier(clientId)
.authenticateAs('app', 'apppwd') // NEU: Authentifizierung
.withWillQos(MqttQos.atLeastOnce)
.startClean()
.keepAliveFor(60);
client.connectionMessage = connMessage;
await client.connect();
return client;
}
2. Message Envelope (NEU!)
class MessageEnvelope {
final String messageId;
final DateTime timestamp;
final String topic;
final Map<String, dynamic> payload;
final bool requiresAck;
final int retryCount;
final DateTime? expiresAt;
MessageEnvelope({
required this.messageId,
required this.timestamp,
required this.topic,
required this.payload,
this.requiresAck = true,
this.retryCount = 0,
this.expiresAt,
});
factory MessageEnvelope.fromJson(Map<String, dynamic> json) {
return MessageEnvelope(
messageId: json['messageId'],
timestamp: DateTime.parse(json['timestamp']),
topic: json['topic'],
payload: json['payload'],
requiresAck: json['requiresAck'] ?? true,
retryCount: json['retryCount'] ?? 0,
expiresAt: json['expiresAt'] != null
? DateTime.parse(json['expiresAt'])
: null,
);
}
Map<String, dynamic> toJson() => {
'messageId': messageId,
'timestamp': timestamp.toIso8601String(),
'topic': topic,
'payload': payload,
'requiresAck': requiresAck,
'retryCount': retryCount,
if (expiresAt != null) 'expiresAt': expiresAt!.toIso8601String(),
};
}
3. Nachricht senden
import 'package:uuid/uuid.dart';
import 'dart:convert';
Future<void> sendMessage(
MqttServerClient client,
String clientId,
String messageType,
Map<String, dynamic> payload,
) async {
final envelope = MessageEnvelope(
messageId: Uuid().v4(),
timestamp: DateTime.now(),
topic: '/server/$clientId/$messageType',
payload: payload,
requiresAck: true,
);
final builder = MqttClientPayloadBuilder();
builder.addString(jsonEncode(envelope.toJson()));
client.publishMessage(
envelope.topic,
MqttQos.exactlyOnce,
builder.payload!,
);
}
// Beispiel-Verwendung:
await sendMessage(
client,
'app-user-123',
'task_completed',
{
'taskId': '456',
'status': 'completed',
'completedAt': DateTime.now().toIso8601String(),
},
);
4. Nachricht empfangen
void setupMessageListener(MqttServerClient client, String clientId) {
// Subscribe zu allen relevanten Topics
client.subscribe('/client/$clientId/#', MqttQos.exactlyOnce);
client.subscribe('/ack/client/$clientId/#', MqttQos.exactlyOnce);
client.updates!.listen((List<MqttReceivedMessage<MqttMessage>> messages) {
for (var message in messages) {
final topic = message.topic;
final payloadString = MqttPublishPayload.bytesToStringAsString(
(message.payload as MqttPublishMessage).payload.message,
);
try {
final json = jsonDecode(payloadString);
// Prüfen ob Envelope-Format
if (json.containsKey('messageId') && json.containsKey('payload')) {
final envelope = MessageEnvelope.fromJson(json);
// Nachricht verarbeiten
handleEnvelopeMessage(envelope);
// ACK senden wenn erforderlich
if (envelope.requiresAck) {
sendAck(client, envelope.messageId, true);
}
} else {
// Legacy-Format
handleLegacyMessage(topic, json);
}
} catch (e) {
print('Error processing message: $e');
}
}
});
}
5. Acknowledgment senden (NEU!)
class AcknowledgmentMessage {
final String messageId;
final DateTime timestamp;
final String status;
final String? errorMessage;
AcknowledgmentMessage({
required this.messageId,
required this.timestamp,
required this.status,
this.errorMessage,
});
Map<String, dynamic> toJson() => {
'messageId': messageId,
'timestamp': timestamp.toIso8601String(),
'status': status,
if (errorMessage != null) 'errorMessage': errorMessage,
};
}
void sendAck(
MqttServerClient client,
String messageId,
bool success,
[String? errorMessage]
) {
final ack = AcknowledgmentMessage(
messageId: messageId,
timestamp: DateTime.now(),
status: success ? 'SUCCESS' : 'FAILED',
errorMessage: errorMessage,
);
final topic = '/ack/server/$messageId';
final builder = MqttClientPayloadBuilder();
builder.addString(jsonEncode(ack.toJson()));
client.publishMessage(
topic,
MqttQos.exactlyOnce,
builder.payload!,
);
}
📊 Topic-Struktur (unverändert)
Client → Server
/server/{clientId}/task_completed
/server/{clientId}/login
/server/{clientId}/message
/server/{clientId}/jobs/assigned
Server → Client
/client/{clientId}/jobs/assigned
/client/{clientId}/message
/client/{clientId}/task_update
Acknowledgments
Client → Server: /ack/server/{messageId}
Server → Client: /ack/client/{clientId}/{messageId}
🔍 Debugging
Verbindung testen
Future<void> testConnection() async {
try {
final client = await connectToMqtt('test-${Uuid().v4()}');
if (client.connectionStatus!.state == MqttConnectionState.connected) {
print('✅ Connection successful!');
// Test-Nachricht senden
await sendMessage(
client,
'test-client',
'test',
{'message': 'Hello from client'},
);
print('✅ Test message sent!');
} else {
print('❌ Connection failed: ${client.connectionStatus}');
}
} catch (e) {
print('❌ Error: $e');
}
}
Häufige Fehler
1. Connection Timeout
Fehler: TimeoutException after 60 seconds
Lösung:
- Prüfen Sie, ob Port 42099 erreichbar ist
- Firewall-Einstellungen überprüfen
- Netzwerkverbindung testen
2. Authentication Failed
Fehler: Connection refused
Lösung:
- Username: 'app'
- Password: 'apppwd'
- Stellen Sie sicher, dass authenticateAs() aufgerufen wird
3. Message not received
Fehler: Keine Nachrichten empfangen
Lösung:
- Subscription überprüfen: client.subscribe('/client/$clientId/#', ...)
- Listener registriert: client.updates!.listen(...)
- Topic-Format korrekt: /client/{clientId}/{messageType}
📦 Dependencies
pubspec.yaml
dependencies:
mqtt_client: ^10.2.0
uuid: ^4.0.0
✅ Migration Checklist
- Port auf 42099 geändert
- Authentifizierung hinzugefügt (app/apppwd)
- Connection-Timeout auf 60s erhöht
- Keep-Alive auf 60s gesetzt
- MessageEnvelope-Klasse implementiert
- AcknowledgmentMessage-Klasse implementiert
- Envelope-basiertes Senden implementiert
- Envelope-basiertes Empfangen implementiert
- ACK-Handling implementiert
- Fehlerbehandlung für ACKs implementiert
- Verbindung getestet
- Nachrichtenversand getestet
- Nachrichtenempfang getestet
🔗 Weitere Dokumentation
- Detaillierte Migration:
MQTT_MIGRATION_GUIDE.md - Changelog:
CHANGELOG_MQTT.md - Architektur:
MESSAGING_LAYER.md - API-Referenz:
MQTT_README.md
💡 Best Practices
- Eindeutige Client-IDs: Verwenden Sie stabile, geräte-spezifische IDs
- Fehlerbehandlung: Implementieren Sie Retry-Logik für fehlgeschlagene Verbindungen
- ACK-Timeout: Warten Sie maximal 30 Sekunden auf ACKs
- Message-Expiry: Prüfen Sie
expiresAtvor der Verarbeitung - Logging: Loggen Sie alle Verbindungsereignisse für Debugging
🆘 Support
Bei Problemen:
- Logs auf Client- und Server-Seite prüfen
- Netzwerk-Konnektivität testen (Port 42099)
- MQTT-Client-Tool verwenden (z.B. MQTT Explorer)
- Dokumentation konsultieren