368 lines
8.5 KiB
Markdown
368 lines
8.5 KiB
Markdown
# 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
|
|
|
|
```dart
|
|
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!)
|
|
|
|
```dart
|
|
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
|
|
|
|
```dart
|
|
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
|
|
|
|
```dart
|
|
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!)
|
|
|
|
```dart
|
|
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
|
|
|
|
```dart
|
|
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
|
|
```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
|
|
|
|
1. **Eindeutige Client-IDs**: Verwenden Sie stabile, geräte-spezifische IDs
|
|
2. **Fehlerbehandlung**: Implementieren Sie Retry-Logik für fehlgeschlagene Verbindungen
|
|
3. **ACK-Timeout**: Warten Sie maximal 30 Sekunden auf ACKs
|
|
4. **Message-Expiry**: Prüfen Sie `expiresAt` vor der Verarbeitung
|
|
5. **Logging**: Loggen Sie alle Verbindungsereignisse für Debugging
|
|
|
|
## 🆘 Support
|
|
|
|
Bei Problemen:
|
|
1. Logs auf Client- und Server-Seite prüfen
|
|
2. Netzwerk-Konnektivität testen (Port 42099)
|
|
3. MQTT-Client-Tool verwenden (z.B. MQTT Explorer)
|
|
4. Dokumentation konsultieren
|
|
|