Bluetooth-Chat
Zeigt die Kommunikation über Bluetooth unter Verwendung des RFCOMM-Protokolls.
Das Beispiel „Bluetooth-Chat“ veranschaulicht, wie man die Qt Bluetooth API zur Kommunikation mit einer anderen Anwendung auf einem Remote-Gerät über das Bluetooth-RFCOMM-Protokoll.

Das Beispiel „Bluetooth-Chat“ implementiert ein einfaches Chat-Programm für mehrere Teilnehmer. Die Anwendung fungiert stets sowohl als Server als auch als Client, sodass nicht festgelegt werden muss, wer sich mit wem verbinden soll.
Ausführen des Beispiels
Sie können das Beispiel ausführen unter:
- Qt Creator
Öffnen Sie den Modus „ Welcome “ und wählen Sie das Beispiel unter „ Examples “ aus. Weitere Informationen finden Sie unter Qt Creator: Tutorial: Erstellen und Ausführen.
- Qt Extension for Visual Studio Code
Führen Sie den Befehl „ Qt: Open Qt examples “ aus dem Verzeichnis „ Command Palette “ aus und wählen Sie das Beispiel aus der Liste aus. Weitere Informationen finden Sie unter Qt Extension for Visual Studio Code: Tutorial: Erstellen und Ausführen.
Chat-Server
Der Chat-Server wird durch die Klasse „ ChatServer “ implementiert. Die Klasse „ ChatServer “ ist wie folgt deklariert:
class ChatServer : public QObject
{
Q_OBJECT
public:
explicit ChatServer(QObject *parent = nullptr);
~ChatServer();
void startServer(const QBluetoothAddress &localAdapter = QBluetoothAddress());
void stopServer();
public slots:
void sendMessage(const QString &message);
signals:
void messageReceived(const QString &sender, const QString &message);
void clientConnected(const QString &name);
void clientDisconnected(const QString &name);
private slots:
void clientConnected();
void clientDisconnected();
void readSocket();
private:
QBluetoothServer *rfcommServer = nullptr;
QBluetoothServiceInfo serviceInfo;
QList<QBluetoothSocket *> clientSockets;
QMap<QBluetoothSocket *, QString> clientNames;
};Als Erstes muss der Chat-Server eine Instanz von ` QBluetoothServer ` erstellen, um auf eingehende Bluetooth-Verbindungen zu warten. Der Slot ` clientConnected() ` wird aufgerufen, sobald eine neue Verbindung hergestellt wird.
rfcommServer = new QBluetoothServer(QBluetoothServiceInfo::RfcommProtocol, this);
connect(rfcommServer, &QBluetoothServer::newConnection,
this, QOverload<>::of(&ChatServer::clientConnected));
bool result = rfcommServer->listen(localAdapter);
if (!result) {
qWarning() << "Cannot bind chat server to" << localAdapter.toString();
return;
}Der Chat-Server ist nur dann von Nutzen, wenn andere wissen, dass er vorhanden ist. Damit andere Geräte ihn finden können, muss ein Eintrag, der den Dienst beschreibt, in der SDP-Datenbank (Service Discovery Protocol) des Systems veröffentlicht werden. Die Klasse „ QBluetoothServiceInfo “ kapselt einen Dienst-Eintrag.
Wir werden einen Dienstdatensatz veröffentlichen, der einige textuelle Beschreibungen der Dienste, eine UUID zur eindeutigen Identifizierung des Dienstes, das Attribut „discoverability“ sowie Verbindungsparameter enthält.
Die textuelle Beschreibung des Dienstes wird in den Attributen „ ServiceName “, „ ServiceDescription “ und „ ServiceProvider “ gespeichert.
serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceName, tr("Bt Chat Server"));
serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceDescription,
tr("Example bluetooth chat server"));
serviceInfo.setAttribute(QBluetoothServiceInfo::ServiceProvider, tr("qt-project.org"));Bluetooth verwendet UUIDs als eindeutige Identifikatoren. Der Chat-Dienst verwendet eine zufällig generierte UUID.
static constexpr auto serviceUuid = "e8e10f95-1a70-4b27-9ccf-02010264e9c8"_L1;
serviceInfo.setServiceUuid(QBluetoothUuid(serviceUuid));Ein Bluetooth-Dienst ist nur dann erkennbar, wenn er sich im „ PublicBrowseGroup “ befindet.
const auto groupUuid = QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::PublicBrowseGroup);
QBluetoothServiceInfo::Sequence publicBrowse;
publicBrowse << QVariant::fromValue(groupUuid);
serviceInfo.setAttribute(QBluetoothServiceInfo::BrowseGroupList, publicBrowse);Das Attribut „ ProtocolDescriptorList “ dient dazu, die Verbindungsparameter zu veröffentlichen, die das Remote-Gerät benötigt, um eine Verbindung zu unserem Dienst herzustellen. Hier legen wir fest, dass das Protokoll „ Rfcomm “ verwendet wird, und setzen die Portnummer auf den Port, auf dem unsere „ rfcommServer “-Instanz auf Verbindungen wartet.
QBluetoothServiceInfo::Sequence protocolDescriptorList;
QBluetoothServiceInfo::Sequence protocol;
protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ProtocolUuid::L2cap));
protocolDescriptorList.append(QVariant::fromValue(protocol));
protocol.clear();
protocol << QVariant::fromValue(QBluetoothUuid(QBluetoothUuid::ProtocolUuid::Rfcomm))
<< QVariant::fromValue(quint8(rfcommServer->serverPort()));
protocolDescriptorList.append(QVariant::fromValue(protocol));
serviceInfo.setAttribute(QBluetoothServiceInfo::ProtocolDescriptorList,
protocolDescriptorList);Abschließend registrieren wir den Dienstdatensatz im System.
serviceInfo.registerService(localAdapter);Wie bereits erwähnt, werden eingehende Verbindungen im Slot „ clientConnected() “ verarbeitet, wo ausstehende Verbindungen mit den Signalen „ readyRead()“ und „ disconnected()“ verknüpft werden. Die Signale benachrichtigen andere darüber, dass sich ein neuer Client verbunden hat.
void ChatServer::clientConnected()
{
QBluetoothSocket *socket = rfcommServer->nextPendingConnection();
if (!socket)
return;
connect(socket, &QBluetoothSocket::readyRead, this, &ChatServer::readSocket);
connect(socket, &QBluetoothSocket::disconnected,
this, QOverload<>::of(&ChatServer::clientDisconnected));
clientSockets.append(socket);
clientNames[socket] = socket->peerName();
emit clientConnected(socket->peerName());
}Der Slot „ readSocket() “ wird immer dann aufgerufen, wenn Daten von einem Client-Socket gelesen werden können. Der Slot liest einzelne Zeilen aus dem Socket, konvertiert sie aus UTF-8 und sendet das Signal „ messageReceived() “ aus.
void ChatServer::readSocket()
{
QBluetoothSocket *socket = qobject_cast<QBluetoothSocket *>(sender());
if (!socket)
return;
while (socket->canReadLine()) {
QByteArray line = socket->readLine().trimmed();
emit messageReceived(clientNames[socket],
QString::fromUtf8(line.constData(), line.length()));
}
}Der Slot „ clientDisconnected() “ wird aufgerufen, sobald ein Client die Verbindung zum Dienst trennt. Der Slot sendet ein Signal aus, um andere darüber zu informieren, dass ein Client die Verbindung getrennt hat, und löscht den Socket.
void ChatServer::clientDisconnected()
{
QBluetoothSocket *socket = qobject_cast<QBluetoothSocket *>(sender());
if (!socket)
return;
emit clientDisconnected(clientNames[socket]);
clientSockets.removeOne(socket);
clientNames.remove(socket);
socket->deleteLater();
}Der Slot „ sendMessage() “ wird verwendet, um eine Nachricht an alle verbundenen Clients zu senden. Die Nachricht wird in UTF-8 konvertiert und mit einem Zeilenumbruch ergänzt, bevor sie an alle Clients gesendet wird.
void ChatServer::sendMessage(const QString &message)
{
QByteArray text = message.toUtf8() + '\n';
for (QBluetoothSocket *socket : std::as_const(clientSockets))
socket->write(text);
}Wenn der Chat-Server gestoppt wird, wird der Service-Eintrag aus der SDP-Datenbank des Systems entfernt, alle verbundenen Client-Sockets werden gelöscht und die Instanz von „ rfcommServer “ wird gelöscht.
void ChatServer::stopServer()
{
// Dienst abmelden
serviceInfo.unregisterService();
// Sockets schließen
qDeleteAll(clientSockets);
clientNames.clear();
// Server schließen
delete rfcommServer;
rfcommServer = nullptr;
}Diensterkennung
Bevor eine Verbindung zum Server hergestellt wird, muss der Client die Geräte in der Nähe scannen und nach dem Gerät suchen, das den Chat-Dienst anbietet. Dies erfolgt über die Klasse „ RemoteSelector “.
Um die Dienstsuche zu starten, erstellt die Klasse „ RemoteSelector “ eine Instanz von „ QBluetoothServiceDiscoveryAgent “ und verbindet sich mit deren Signalen.
m_discoveryAgent = new QBluetoothServiceDiscoveryAgent(localAdapter);
connect(m_discoveryAgent, &QBluetoothServiceDiscoveryAgent::serviceDiscovered,
this, &RemoteSelector::serviceDiscovered);
connect(m_discoveryAgent, &QBluetoothServiceDiscoveryAgent::finished,
this, &RemoteSelector::discoveryFinished);
connect(m_discoveryAgent, &QBluetoothServiceDiscoveryAgent::canceled,
this, &RemoteSelector::discoveryFinished);Es wird ein UUID-Filter gesetzt, damit bei der Dienstermittlung nur die Geräte angezeigt werden, die den benötigten Dienst anbieten. Anschließend wird ein ` FullDiscovery ` gestartet:
m_discoveryAgent->setUuidFilter(uuid);
m_discoveryAgent->start(QBluetoothServiceDiscoveryAgent::FullDiscovery);Wird ein passender Dienst gefunden, wird ein Signal „ serviceDiscovered()“ mit einer Instanz von „ QBluetoothServiceInfo “ als Parameter ausgesendet. Diese Dienstinformationen werden verwendet, um den Gerätenamen und den Dienstnamen zu extrahieren und einen neuen Eintrag zur Liste der erkannten Remote-Geräte hinzuzufügen:
QString remoteName;
if (serviceInfo.device().name().isEmpty())
remoteName = address.toString();
else
remoteName = serviceInfo.device().name();
QListWidgetItem *item =
new QListWidgetItem(QString::fromLatin1("%1 %2").arg(remoteName,
serviceInfo.serviceName()));
m_discoveredServices.insert(item, serviceInfo);
ui->remoteDevices->addItem(item);Später kann der Benutzer eines der Geräte aus der Liste auswählen und versuchen, eine Verbindung dazu herzustellen.
Chat-Client
Der Chat-Client wird durch die Klasse „ ChatClient “ implementiert. Die Klasse „ ChatClient “ ist wie folgt deklariert:
class ChatClient : public QObject
{
Q_OBJECT
public:
explicit ChatClient(QObject *parent = nullptr);
~ChatClient();
void startClient(const QBluetoothServiceInfo &remoteService);
void stopClient();
public slots:
void sendMessage(const QString &message);
signals:
void messageReceived(const QString &sender, const QString &message);
void connected(const QString &name);
void disconnected();
void socketErrorOccurred(const QString &errorString);
private slots:
void readSocket();
void connected();
void onSocketErrorOccurred(QBluetoothSocket::SocketError);
private:
QBluetoothSocket *socket = nullptr;
};Der Client erstellt ein neues ` QBluetoothSocket `-Objekt und stellt eine Verbindung zu dem Remote-Dienst her, der durch den Parameter ` remoteService ` beschrieben wird. Slots werden mit den Signalen ` readyRead()`, ` connected()` und ` disconnected()` des Sockets verbunden.
void ChatClient::startClient(const QBluetoothServiceInfo&remoteService)
{
if (socket)
return;
// Verbindung zum Dienst herstellen
socket = new QBluetoothSocket(QBluetoothServiceInfo::RfcommProtocol);
qDebug() << "Create socket";
socket->connectToService(remoteService);
qDebug() << "ConnectToService done";
connect(socket, &QBluetoothSocket::readyRead, this, &ChatClient::readSocket);
connect(socket, &QBluetoothSocket::connected, this, QOverload<>::of(&ChatClient::connected));
connect(socket, &QBluetoothSocket::disconnected, this, &ChatClient::disconnected);
connect(socket, &QBluetoothSocket::errorOccurred, this, &ChatClient::onSocketErrorOccurred);
}Bei erfolgreicher Socket-Verbindung senden wir ein Signal aus, um andere Benutzer zu benachrichtigen.
void ChatClient::connected()
{
emit connected(socket->peerName());
}Ähnlich wie beim Chat-Server wird der Slot „ readSocket() “ aufgerufen, wenn Daten vom Socket verfügbar sind. Die Zeilen werden einzeln gelesen und aus UTF-8 konvertiert. Das Signal „ messageReceived() “ wird ausgelöst.
void ChatClient::readSocket()
{
if (!socket)
return;
while (socket->canReadLine()) {
QByteArray line = socket->readLine().trimmed();
emit messageReceived(socket->peerName(),
QString::fromUtf8(line.constData(), line.length()));
}
}Der Slot „ sendMessage() “ wird verwendet, um eine Nachricht an das Remote-Gerät zu senden. Die Nachricht wird in UTF-8 konvertiert und um ein Zeilenende ergänzt.
void ChatClient::sendMessage(const QString &message)
{
QByteArray text = message.toUtf8() + '\n';
socket->write(text);
}Um die Verbindung zum Remote-Chat-Dienst zu trennen, wird die Instanz von „ QBluetoothSocket “ gelöscht.
void ChatClient::stopClient()
{
delete socket;
socket = nullptr;
}Chat-Dialog
Das Hauptfenster dieses Beispiels ist der Chat-Dialog, der in der Klasse „ Chat “ implementiert ist. Diese Klasse zeigt eine Chat-Sitzung zwischen einem einzelnen „ ChatServer “ und null oder mehreren „ ChatClients“ an. Die Klasse „ Chat “ ist wie folgt deklariert:
class Chat : public QDialog
{
Q_OBJECT
public:
explicit Chat(QWidget *parent = nullptr);
~Chat();
signals:
void sendMessage(const QString &message);
private slots:
void connectClicked();
void sendClicked();
void showMessage(const QString &sender, const QString &message);
void clientConnected(const QString &name);
void clientDisconnected(const QString &name);
void clientDisconnected();
void connected(const QString &name);
void reactOnSocketError(const QString &error);
void newAdapterSelected();
void initBluetooth();
void updateIcons(Qt::ColorScheme scheme);
private:
int adapterFromUserSelection() const;
int currentAdapterIndex = 0;
Ui::Chat *ui;
ChatServer *server = nullptr;
QList<ChatClient *> clients;
QList<QBluetoothHostInfo> localAdapters;
QString localName;
};Zunächst erstellen wir die Benutzeroberfläche
ui->setupUi(this);
connect(ui->connectButton, &QPushButton::clicked, this, &Chat::connectClicked);
connect(ui->sendButton, &QPushButton::clicked, this, &Chat::sendClicked);Wir erstellen eine Instanz von „ ChatServer “ und reagieren auf die Signale „ clientConnected() “, „ clientDiconnected() “ und „ messageReceived() “.
server = new ChatServer(this);
connect(server, QOverload<const QString &>::of(&ChatServer::clientConnected),
this, &Chat::clientConnected);
connect(server, QOverload<const QString &>::of(&ChatServer::clientDisconnected),
this, QOverload<const QString &>::of(&Chat::clientDisconnected));
connect(server, &ChatServer::messageReceived,
this, &Chat::showMessage);
connect(this, &Chat::sendMessage, server, &ChatServer::sendMessage);
server->startServer();Als Reaktion auf die Signale „ clientConnected() “ und „ clientDisconnected() “ von „ ChatServer “ zeigen wir in der Chat-Sitzung die typischen Meldungen „X hat dem Chat beigetreten.“ und „Y hat den Chat verlassen.“ an.
void Chat::clientConnected(const QString &name)
{
ui->chat->insertPlainText(QString::fromLatin1("%1 has joined chat.\n").arg(name));
}
void Chat::clientDisconnected(const QString &name)
{
ui->chat->insertPlainText(QString::fromLatin1("%1 has left.\n").arg(name));
}Eingehende Nachrichten von Clients, die mit dem ChatServer verbunden sind, werden im Slot showMessage() verarbeitet. Der mit dem Namen des Remote-Geräts versehene Nachrichtentext wird in der Chat-Sitzung angezeigt.
void Chat::showMessage(const QString &sender, const QString &message)
{
ui->chat->moveCursor(QTextCursor::End);
ui->chat->insertPlainText(QString::fromLatin1("%1: %2\n").arg(sender, message));
ui->chat->ensureCursorVisible();
}Wenn die Schaltfläche „Verbinden“ angeklickt wird, startet die Anwendung die Dienstermittlung und zeigt eine Liste der auf Remote-Geräten gefundenen Chat-Dienste an. Der Nutzer wählt eine „ ChatClient “ für den Dienst aus.
void Chat::connectClicked()
{
ui->connectButton->setEnabled(false);
// nach Diensten suchen
const QBluetoothAddress adapter = localAdapters.isEmpty() ?
QBluetoothAddress() :
localAdapters.at(currentAdapterIndex).address();
RemoteSelector remoteSelector(adapter);
#ifdef Q_OS_ANDROID
// QTBUG-61392
Q_UNUSED(serviceUuid);
remoteSelector.startDiscovery(QBluetoothUuid(reverseUuid));
#else
remoteSelector.startDiscovery(QBluetoothUuid(serviceUuid));
#endif
if (remoteSelector.exec()== QDialog::Accepted) {
QBluetoothServiceInfo service = remoteSelector.service();
qDebug() << "Connecting to service" << service.serviceName()
<< "on" << service.device().name();
// Client erstellen
ChatClient *client = new ChatClient(this);
connect(client, &ChatClient::messageReceived,
this, &Chat::showMessage);
connect(client, &ChatClient::disconnected,
this, QOverload<>::of(&Chat::clientDisconnected));
connect(client, QOverload<const QString&>::of(&ChatClient::connected),
this, &Chat::connected);
connect(client, &ChatClient::socketErrorOccurred,
this, &Chat::reactOnSocketError);
connect(this, &Chat::sendMessage, client, &ChatClient::sendMessage);
client->startClient(service);
clients.append(client);
}
ui->connectButton->setEnabled(true);
}Als Reaktion auf die Signale „ connected() “ von ChatClient zeigen wir in der Chat-Sitzung die Meldung „Dem Chat mit X beigetreten.“ an.
void Chat::connected(const QString &name)
{
ui->chat->insertPlainText(QString::fromLatin1("Joined chat with %1.\n").arg(name));
}Nachrichten werden über die Instanzen „ ChatServer “ und „ ChatClient “ an alle Remote-Geräte gesendet, indem das Signal „ sendMessage() “ ausgelöst wird.
void Chat::sendClicked()
{
ui->sendButton->setEnabled(false);
ui->sendText->setEnabled(false);
showMessage(localName, ui->sendText->text());
emit sendMessage(ui->sendText->text());
ui->sendText->clear();
ui->sendText->setEnabled(true);
ui->sendButton->setEnabled(true);
#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS)
// avoid keyboard automatically popping up again on mobile devices
ui->sendButton->setFocus();
#else
ui->sendText->setFocus();
#endif
}Wir müssen die „ ChatClient “-Instanzen bereinigen, wenn das Remote-Gerät eine Trennung erzwingt.
void Chat::clientDisconnected()
{
ChatClient *client = qobject_cast<ChatClient *>(sender());
if (client) {
clients.removeOne(client);
client->deleteLater();
}
}© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.