Discussion Bluetooth
Présente la communication via Bluetooth à l'aide du protocole RFCOMM.
L'exemple « Chat Bluetooth » montre comment utiliser l' Qt Bluetooth API pour communiquer avec une autre application sur un appareil distant à l'aide du protocole Bluetooth RFCOMM.

L'exemple « Bluetooth Chat » met en œuvre un programme de chat simple entre plusieurs participants. L'application joue toujours à la fois le rôle de serveur et de client, ce qui évite d'avoir à déterminer qui doit se connecter à qui.
Exécution de l'exemple
Vous pouvez exécuter l'exemple à partir de :
- Qt Creator
Ouvrez le mode « Welcome » et sélectionnez l'exemple dans « Examples ». Pour plus d'informations, consultez Qt Creator: Tutoriel : Compiler et exécuter.
- Qt Extension for Visual Studio Code
Exécutez la commande « Qt: Open Qt examples » à partir de Command Palette, puis sélectionnez l'exemple dans la liste. Pour plus d'informations, consultez Qt Extension for Visual Studio Code: Tutoriel : Compiler et exécuter.
Serveur de chat
Le serveur de chat est implémenté par la classe ` ChatServer `. La classe ` ChatServer ` est déclarée comme suit :
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;
};La première chose que le serveur de chat doit faire est de créer une instance de ` QBluetoothServer ` pour écouter les connexions Bluetooth entrantes. Le slot ` clientConnected() ` sera appelé chaque fois qu’une nouvelle connexion sera établie.
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;
}Le serveur de chat n'est utile que si les autres utilisateurs savent qu'il existe. Pour permettre à d'autres appareils de le détecter, une fiche décrivant le service doit être publiée dans la base de données SDP (Service Discovery Protocol) du système. La classe QBluetoothServiceInfo encapsule une fiche de service.
Nous allons publier une fiche de service contenant des descriptions textuelles des services, un UUID identifiant le service de manière unique, l'attribut de détectabilité et les paramètres de connexion.
La description textuelle du service est stockée dans les attributs ` ServiceName`, ` ServiceDescription` et ` ServiceProvider `.
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 utilise des UUID comme identifiants uniques. Le service de chat utilise un UUID généré aléatoirement.
static constexpr auto serviceUuid = "e8e10f95-1a70-4b27-9ccf-02010264e9c8"_L1;
serviceInfo.setServiceUuid(QBluetoothUuid(serviceUuid));Un service Bluetooth n'est détectable que s'il se trouve dans l'PublicBrowseGroup.
const auto groupUuid = QBluetoothUuid(QBluetoothUuid::ServiceClassUuid::PublicBrowseGroup);
QBluetoothServiceInfo::Sequence publicBrowse;
publicBrowse << QVariant::fromValue(groupUuid);
serviceInfo.setAttribute(QBluetoothServiceInfo::BrowseGroupList, publicBrowse);L’attribut « ProtocolDescriptorList » sert à publier les paramètres de connexion dont le périphérique distant a besoin pour se connecter à notre service. Nous y précisons que le protocole Rfcomm est utilisé et définissons le numéro de port correspondant au port sur lequel notre instance rfcommServer est à l’écoute.
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);Enfin, nous enregistrons l'enregistrement de service auprès du système.
serviceInfo.registerService(localAdapter);Comme mentionné précédemment, les connexions entrantes sont gérées dans le slot « clientConnected() », où les connexions en attente sont reliées aux signaux « readyRead() » et « disconnected() ». Ces signaux informent les autres qu’un nouveau client s’est connecté.
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());
}Le slot readSocket() est appelé dès que des données sont prêtes à être lues à partir d’un socket client. Le slot lit les lignes une à une à partir du socket, les convertit du format UTF-8, puis émet le signal messageReceived().
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()));
}
}Le slot « clientDisconnected() » est appelé chaque fois qu’un client se déconnecte du service. Ce slot émet un signal pour signaler aux autres qu’un client s’est déconnecté, puis supprime le 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();
}Le slot « sendMessage() » sert à envoyer un message à tous les clients connectés. Le message est converti au format UTF-8 et complété par un caractère de fin de ligne avant d’être envoyé à tous les clients.
void ChatServer::sendMessage(const QString &message)
{
QByteArray text = message.toUtf8() + '\n';
for (QBluetoothSocket *socket : std::as_const(clientSockets))
socket->write(text);
}Lorsque le serveur de chat est arrêté, l’enregistrement du service est supprimé de la base de données SDP du système, tous les sockets des clients connectés sont supprimés et l’instance d’ rfcommServer est supprimée.
void ChatServer::stopServer()
{
// Désenregistrer le service
serviceInfo.unregisterService();
// Fermer les sockets
qDeleteAll(clientSockets);
clientNames.clear();
// Fermer le serveur
delete rfcommServer;
rfcommServer = nullptr;
}Découverte de services
Avant de se connecter au serveur, le client doit analyser les appareils à proximité et rechercher celui qui diffuse le service de chat. Cette opération est effectuée par la classe RemoteSelector.
Pour lancer la recherche de service, la classe ` RemoteSelector ` crée une instance de ` QBluetoothServiceDiscoveryAgent ` et s’abonne à ses signaux.
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);Un filtre UUID est défini afin que la découverte de services n’affiche que les appareils qui diffusent le service requis. Ensuite, une instance de ` FullDiscovery ` est lancée :
m_discoveryAgent->setUuidFilter(uuid);
m_discoveryAgent->start(QBluetoothServiceDiscoveryAgent::FullDiscovery);Lorsqu’un service correspondant est détecté, un signal ` serviceDiscovered()` est émis avec, en paramètre, une instance de ` QBluetoothServiceInfo `. Ces informations de service sont utilisées pour extraire le nom du périphérique et celui du service, puis pour ajouter une nouvelle entrée à la liste des périphériques distants détectés :
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);Par la suite, l’utilisateur peut sélectionner l’un des appareils de la liste et tenter de s’y connecter.
Client de chat
Le client de chat est implémenté par la classe ` ChatClient `. La classe ` ChatClient ` est déclarée comme suit :
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;
};Le client crée un nouvel objet ` QBluetoothSocket ` et se connecte au service distant décrit par le paramètre ` remoteService `. Les slots sont connectés aux signaux ` readyRead()`, ` connected()` et ` disconnected()` du socket.
void ChatClient::startClient(const QBluetoothServiceInfo&remoteService)
{
if (socket)
return;
// Connexion au service
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);
}Une fois la connexion au socket établie, nous émettons un signal pour en informer les autres utilisateurs.
void ChatClient::connected()
{
emit connected(socket->peerName());
}Comme pour le serveur de chat, le slot readSocket() est appelé lorsque des données sont disponibles depuis le socket. Les lignes sont lues une par une et converties depuis le format UTF-8. Le signal messageReceived() est émis.
void ChatClient::readSocket()
{
if (!socket)
return;
while (socket->canReadLine()) {
QByteArray line = socket->readLine().trimmed();
emit messageReceived(socket->peerName(),
QString::fromUtf8(line.constData(), line.length()));
}
}Le slot « sendMessage() » sert à envoyer un message à l’appareil distant. Le message est converti au format UTF-8 et un retour à la ligne y est ajouté.
void ChatClient::sendMessage(const QString &message)
{
QByteArray text = message.toUtf8() + '\n';
socket->write(text);
}Pour se déconnecter du service de chat distant, l’instance de ` QBluetoothSocket ` est supprimée.
void ChatClient::stopClient()
{
delete socket;
socket = nullptr;
}Boîte de dialogue de chat
La fenêtre principale de cet exemple est la boîte de dialogue de chat, implémentée dans la classe ` Chat `. Cette classe affiche une session de chat entre un seul ` ChatServer ` et zéro ou plusieurs ` ChatClient`. La classe ` Chat ` est déclarée comme suit :
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;
};Nous commençons par créer l’interface utilisateur
ui->setupUi(this);
connect(ui->connectButton, &QPushButton::clicked, this, &Chat::connectClicked);
connect(ui->sendButton, &QPushButton::clicked, this, &Chat::sendClicked);Nous créons une instance de l'ChatServer et répondons à ses signaux « clientConnected() », « clientDiconnected() » et « 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();En réponse aux signaux clientConnected() et clientDisconnected() de l’ ChatServer, nous affichons les messages classiques « X a rejoint le chat » et « Y a quitté le chat » dans la session de chat.
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));
}Les messages entrants provenant des clients connectés à l'ChatServer sont traités dans le slot showMessage(). Le texte du message, accompagné du nom de l'appareil distant, s'affiche dans la session de chat.
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();
}Lorsque l’utilisateur clique sur le bouton « Se connecter », l’application lance la recherche de services et affiche une liste des services de chat détectés sur les appareils distants. L’utilisateur sélectionne alors une « ChatClient » pour le service.
void Chat::connectClicked()
{
ui->connectButton->setEnabled(false);
// recherche des services
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();
// Création du client
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);
}En réponse aux signaux « connected() » provenant de ChatClient, nous affichons le message « A rejoint le chat avec X. » dans la session de chat.
void Chat::connected(const QString &name)
{
ui->chat->insertPlainText(QString::fromLatin1("Joined chat with %1.\n").arg(name));
}Les messages sont envoyés à tous les appareils distants via les instances ` ChatServer ` et ` ChatClient ` en émettant le signal ` sendMessage() `.
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
}Nous devons nettoyer les instances ChatClient lorsque le périphérique distant force une déconnexion.
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.