Yuxian: parceiro de conteúdo da CSDN, novo mentor estrela da CSDN, 51CTO (principal celebridade + blogueiro especialista), entusiasta de código aberto do github (desenvolvimento secundário de código-fonte go-zero, arquitetura de back-end de jogos https://github.com/Peakchen)
Qt chat room é um software de chat em tempo real baseado em comunicação em rede, que geralmente consiste em duas partes: cliente e servidor. Neste artigo, apresentaremos detalhadamente a arquitetura subjacente, os princípios e os métodos de implementação da sala de chat Qt.
A arquitetura subjacente da sala de chat do Qt consiste nas seguintes partes:
- Servidor QTcp
QTcpServer é uma classe usada para implementar um servidor TCP em Qt. Ele pode escutar uma porta especificada e aguardar uma solicitação de conexão de um cliente. Na sala de chat do Qt, podemos usar o QTcpServer para monitorar a solicitação de conexão do cliente.
- QTcpSocket
QTcpSocket é uma classe usada para implementar um cliente TCP no Qt, que pode se conectar a um servidor especificado e enviar e receber dados. Na sala de chat do Qt, podemos usar QTcpSocket para conectar-se ao servidor e enviar e receber mensagens de chat.
- QThread
QThread é uma classe usada para implementar threads em Qt, que pode atribuir tarefas a diferentes threads para execução. Na sala de bate-papo do Qt, podemos usar QThread para realizar comunicação de rede e atualização da IU separadamente para evitar o bloqueio do thread da IU.
- QDataStream
QDataStream é uma classe usada para implementar fluxos de dados no Qt. Ele pode serializar dados em fluxos binários e desserializar fluxos binários em dados. Na sala de chat do Qt, podemos usar QDataStream para serializar a mensagem de chat em um fluxo binário e enviá-la ao servidor ou outros clientes.
A seguir está um diagrama de arquitetura de implementação de uma sala de bate-papo simples baseada em TCP:
+-----------------+
| ServerSocket |
+-----------------+
| - tcpServer |
| - clientSockets |
+-----------------+
/_\
|
| 继承
|
+-----------------+
| QTcpServer |
+-----------------+
/_\
|
| 继承
|
+-----------------+
| QTcpSocket |
+-----------------+
Neste framework, a classe ServerSocket herda da classe QTcpServer, que é responsável por monitorar a solicitação de conexão do cliente e criar o soquete do cliente correspondente. Quando um novo cliente se conecta, ServerSocket criará um novo objeto ClientSocket (herdado da classe QTcpSocket) para lidar com a comunicação com o cliente. A função slot na classe ClientSocket é usada para receber a mensagem enviada pelo cliente e transmiti-la para outros clientes.
Na sala de chat do Qt, podemos usar os seguintes métodos para implementar a função de chat:
- QTcpServer::ouvir()
O método listen() é usado para iniciar a escuta na porta especificada e aceita um parâmetro do tipo inteiro indicando o número da porta a ser escutada. Por exemplo:
QTcpServer server;
server.listen(QHostAddress::Any, 1234);
- QTcpServer::newConnection()
O sinal newConnection() é acionado quando há uma nova solicitação de conexão do cliente. Podemos criar um novo objeto QTcpSocket na função slot do sinal e conectá-lo ao cliente. Por exemplo:
QTcpServer server;
server.listen(QHostAddress::Any, 1234);
connect(&server, &QTcpServer::newConnection, [&]() {
QTcpSocket *clientSocket = server.nextPendingConnection();
// 将clientSocket添加到客户端列表中
});
- QTcpSocket::connectToHost()
O método connectToHost() é usado para conectar-se ao servidor especificado. Ele aceita um parâmetro do tipo string, indicando o nome do host ou endereço IP do servidor a ser conectado, e um parâmetro do tipo inteiro, indicando o número da porta do servidor. estar conectado. Por exemplo:
QTcpSocket *socket = new QTcpSocket;
socket->connectToHost("127.0.0.1", 1234);
- QTcpSocket::conectado()
O sinal conectado() é acionado quando conectado com sucesso ao servidor, e podemos enviar uma mensagem de login ao servidor na função slot do sinal. Por exemplo:
QTcpSocket *socket = new QTcpSocket;
socket->connectToHost("127.0.0.1", 1234);
connect(socket, &QTcpSocket::connected, [&]() {
QByteArray username = "username";
QByteArray password = "password";
QDataStream stream(socket);
stream << username << password;
});
- QTcpSocket::readyRead()
O sinal readyRead() é acionado quando há dados para ler, e podemos receber mensagens de chat na função slot do sinal e exibi-las na UI. Por exemplo:
QTcpSocket *socket = new QTcpSocket;
socket->connectToHost("127.0.0.1", 1234);
connect(socket, &QTcpSocket::readyRead, [&]() {
QDataStream stream(socket);
QString message;
stream >> message;
// 将message显示在UI上
});
- QTcpSocket::disconnected()
O sinal desconectado() é acionado quando a conexão com o servidor é desconectada e podemos limpar os recursos relacionados na função de slot do sinal. Por exemplo:
QTcpSocket *socket = new QTcpSocket;
socket->connectToHost("127.0.0.1", 1234);
connect(socket, &QTcpSocket::disconnected, [&]() {
// 清理相关资源
});
- QThread::start()
O método start() é usado para iniciar o thread e chamará automaticamente o método run() do thread. Por exemplo:
QThread *thread = new QThread;
MyNetworkWorker *worker = new MyNetworkWorker;
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &MyNetworkWorker::run);
connect(worker, &MyNetworkWorker::finished, thread, &QThread::quit);
connect(worker, &MyNetworkWorker::finished, worker, &MyNetworkWorker::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
- QDataStream::operator<<() e QDataStream::operator>>()
O método operador<<() é usado para serializar dados em um fluxo binário, e o método operador>>() é usado para desserializar um fluxo binário em dados. Por exemplo:
QByteArray username = "username";
QByteArray password = "password";
QDataStream stream(&buffer, QIODevice::WriteOnly);
stream << username << password;
// ...
QDataStream stream(socket);
QString message;
stream >> message;
A seguir está um exemplo de implementação de uma sala de chat Qt simples, que inclui o uso dos métodos acima:
class ChatServer : public QObject
{
Q_OBJECT
public:
ChatServer(QObject *parent = nullptr) : QObject(parent)
{
server = new QTcpServer(this);
connect(server, &QTcpServer::newConnection, this, &ChatServer::onNewConnection);
server->listen(QHostAddress::Any, 1234);
qInfo() << "Server started";
}
signals:
void messageReceived(QString message);
public slots:
void onNewConnection()
{
QTcpSocket *clientSocket = server->nextPendingConnection();
connect(clientSocket, &QTcpSocket::disconnected, clientSocket, &QTcpSocket::deleteLater);
connect(clientSocket, &QTcpSocket::readyRead, this, &ChatServer::onMessageReceived);
clients.append(clientSocket);
}
void onMessageReceived()
{
QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
QDataStream stream(clientSocket);
QString message;
stream >> message;
emit messageReceived(message);
}
void sendMessage(QString message)
{
QByteArray buffer;
QDataStream stream(&buffer, QIODevice::WriteOnly);
stream << message;
for (QTcpSocket *clientSocket : clients) {
clientSocket->write(buffer);
}
}
private:
QTcpServer *server;
QList<QTcpSocket*> clients;
};
class ChatClient : public QObject
{
Q_OBJECT
public:
ChatClient(QObject *parent = nullptr) : QObject(parent)
{
socket = new QTcpSocket(this);
connect(socket, &QTcpSocket::connected, this, &ChatClient::onConnected);
connect(socket, &QTcpSocket::disconnected, this, &ChatClient::onDisconnected);
connect(socket, &QTcpSocket::readyRead, this, &ChatClient::onMessageReceived);
socket->connectToHost("127.0.0.1", 1234);
qInfo() << "Client started";
}
public slots:
void onConnected()
{
QByteArray username = "username";
QByteArray password = "password";
QDataStream stream(socket);
stream << username << password;
}
void onDisconnected()
{
qInfo() << "Disconnected from server";
}
void onMessageReceived()
{
QDataStream stream(socket);
QString message;
stream >> message;
qInfo() << "Received message:" << message;
}
void sendMessage(QString message)
{
QByteArray buffer;
QDataStream stream(&buffer, QIODevice::WriteOnly);
stream << message;
socket->write(buffer);
}
private:
QTcpSocket *socket;
};
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
ChatServer server;
ChatClient client;
QObject::connect(&server, &ChatServer::messageReceived, &client, &ChatClient::sendMessage);
QObject::connect(&client, &ChatClient::messageReceived, &server, &ChatServer::sendMessage);
return app.exec();
}
Neste exemplo de implementação, o servidor e o cliente são representados pela classe ChatServer e pela classe ChatClient respectivamente