鱼弦:CSDN内容合伙人、CSDN新星导师、51CTO(Top红人+专家博主) 、github开源爱好者(go-zero源码二次开发、游戏后端架构 https://github.com/Peakchen)
Qt聊天室是一个基于网络通信的实时聊天软件,它通常由客户端和服务器端两部分组成。在本文中,我们将详细介绍Qt聊天室的底层架构、原理和实现方法。
Qt聊天室的底层架构由以下几个部分组成:
- QTcpServer
QTcpServer是Qt中用于实现TCP服务器的类,它可以监听指定的端口,等待客户端的连接请求。在Qt聊天室中,我们可以使用QTcpServer来监听客户端的连接请求。
- QTcpSocket
QTcpSocket是Qt中用于实现TCP客户端的类,它可以连接到指定的服务器,并发送和接收数据。在Qt聊天室中,我们可以使用QTcpSocket来连接到服务器,并发送和接收聊天消息。
- QThread
QThread是Qt中用于实现线程的类,它可以将任务分配到不同的线程中执行。在Qt聊天室中,我们可以使用QThread将网络通信和UI更新分别执行,以避免UI线程被阻塞。
- QDataStream
QDataStream是Qt中用于实现数据流的类,它可以将数据序列化为二进制流,也可以将二进制流反序列化为数据。在Qt聊天室中,我们可以使用QDataStream将聊天消息序列化为二进制流,并发送给服务器或其他客户端。
下面是一个简单的基于 TCP 的聊天室的实现架构图:
+-----------------+
| ServerSocket |
+-----------------+
| - tcpServer |
| - clientSockets |
+-----------------+
/_\
|
| 继承
|
+-----------------+
| QTcpServer |
+-----------------+
/_\
|
| 继承
|
+-----------------+
| QTcpSocket |
+-----------------+
在这个架构中,ServerSocket 类继承自 QTcpServer 类,它负责监听客户端的连接请求并创建对应的客户端套接字。当有新的客户端连接时,ServerSocket 会创建一个新的 ClientSocket 对象(继承自 QTcpSocket 类)来处理与客户端的通信。ClientSocket 类中的槽函数用于接收客户端发送的消息并广播给其他客户端。
在Qt聊天室中,我们可以使用以下方法来实现聊天功能:
- QTcpServer::listen()
listen()方法用于开始监听指定的端口,它接受一个整数类型的参数,表示要监听的端口号。例如:
QTcpServer server;
server.listen(QHostAddress::Any, 1234);
- QTcpServer::newConnection()
newConnection()信号在有新的客户端连接请求时触发,我们可以在该信号的槽函数中创建新的QTcpSocket对象,并将其连接到客户端。例如:
QTcpServer server;
server.listen(QHostAddress::Any, 1234);
connect(&server, &QTcpServer::newConnection, [&]() {
QTcpSocket *clientSocket = server.nextPendingConnection();
// 将clientSocket添加到客户端列表中
});
- QTcpSocket::connectToHost()
connectToHost()方法用于连接到指定的服务器,它接受一个字符串类型的参数,表示要连接的服务器的主机名或IP地址,以及一个整数类型的参数,表示要连接的服务器的端口号。例如:
QTcpSocket *socket = new QTcpSocket;
socket->connectToHost("127.0.0.1", 1234);
- QTcpSocket::connected()
connected()信号在成功连接到服务器时触发,我们可以在该信号的槽函数中发送登录消息给服务器。例如:
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()
readyRead()信号在有数据可读时触发,我们可以在该信号的槽函数中接收聊天消息,并将其显示在UI上。例如:
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()
disconnected()信号在与服务器的连接断开时触发,我们可以在该信号的槽函数中清理相关资源。例如:
QTcpSocket *socket = new QTcpSocket;
socket->connectToHost("127.0.0.1", 1234);
connect(socket, &QTcpSocket::disconnected, [&]() {
// 清理相关资源
});
- QThread::start()
start()方法用于启动线程,它会自动调用线程的run()方法。例如:
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<<() 和 QDataStream::operator>>()
operator<<()方法用于将数据序列化为二进制流,operator>>()方法用于将二进制流反序列化为数据。例如:
QByteArray username = "username";
QByteArray password = "password";
QDataStream stream(&buffer, QIODevice::WriteOnly);
stream << username << password;
// ...
QDataStream stream(socket);
QString message;
stream >> message;
下面是一个简单的Qt聊天室的实现示例,其中包含了上述方法的使用:
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();
}
在这个实现示例中,服务器和客户端分别由ChatServer类和ChatClient类