【Qt 面试题】深入剖析QT TCP通讯流程及应用实例

简介: 【Qt 面试题】深入剖析QT TCP通讯流程及应用实例

服务端:(QTcpServer)

       ①创建QTcpServer对象

       ②监听list需要的参数是地址和端口号

       ③当有新的客户端连接成功回发送newConnect信号

       ④在newConnection信号槽函数中,调用nextPendingConnection函数获取新连接QTcpSocket对象

       ⑤连接QTcpSocket对象的readRead信号

       ⑥在readRead信号的槽函数使用read接收数据

       ⑦调用write成员函数发送数据


客户端:(QTcpSocket)

      ①创建QTcpSocket对象

      ②当对象与Server连接成功时会发送connected 信号

      ③调用成员函数connectToHost连接服务器,需要的参数是地址和端口号

      ④connected信号的槽函数开启发送数据

      ⑤使用write发送数据,read接收数据


Qt tcp服务器示例

在这个示例中,我们创建了一个名为TcpServer的类,继承自QTcpServer,重写了incomingConnection()方法来处理新的客户端连接。每个连接都会创建一个ClientHandler对象,ClientHandler继承自QRunnable,处理与客户端的通信。QThreadPool负责管理和调度这些ClientHandler对象。

#include "main.moc" 这一行是在使用Qt的moc(元对象编译器)时生成的一个包含文件。在Qt中,为了实现信号和槽、内省和动态属性等特性,需要用到Qt的元对象系统。moc的作用是在编译时解析含有Q_OBJECT宏的类,为这些类生成一个额外的C++源文件,这个文件包含了元对象信息。

在我们的示例中,TcpServer类包含了Q_OBJECT宏,所以需要使用moc处理。通常情况下,moc会生成一个名为“classname.moc”的文件。这个文件会被自动包含到构建过程中,所以我们不需要手动添加它。但在这个示例中,我们把所有代码都放在了一个文件中,所以需要显式地包含这个moc文件。如果你在一个项目中使用多个文件,你可以将类的声明和实现放在不同的文件中,并且不需要显式地包含moc文件,因为构建系统会自动处理它。

需要注意的是,如果你使用了qmake、CMake或者其他支持Qt的构建系统,它们会自动调用moc并处理这些文件,你不需要手动添加#include "main.moc"。在这个简化示例中,我们显式地包含了moc文件,但在实际项目中,你通常不需要这么做。

#include <QCoreApplication>
#include <QTcpServer>
#include <QTcpSocket>
#include <QThreadPool>
#include <QDebug>
#include <QRunnable>
 
class ClientHandler : public QRunnable
{
public:
    ClientHandler(qintptr socketDescriptor) : m_socketDescriptor(socketDescriptor) {}
 
    void run() override
    {
        QTcpSocket socket;
        if (!socket.setSocketDescriptor(m_socketDescriptor)) {
            qDebug() << "Failed to set socket descriptor.";
            return;
        }
 
        while (socket.state() == QAbstractSocket::ConnectedState) {
            if (socket.waitForReadyRead()) {
                QByteArray data = socket.readAll();
                qDebug() << "Received data:" << data;
                socket.write(data);
            }
        }
    }
 
private:
    qintptr m_socketDescriptor;
};
 
class TcpServer : public QTcpServer
{
    Q_OBJECT
public:
    TcpServer(QObject *parent = nullptr) : QTcpServer(parent) {}
 
protected:
    void incomingConnection(qintptr socketDescriptor) override
    {
        qDebug() << "New connection:" << socketDescriptor;
        auto clientHandler = new ClientHandler(socketDescriptor);
        QThreadPool::globalInstance()->start(clientHandler);
    }
};
 
int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
 
    TcpServer server;
    if (!server.listen(QHostAddress::Any, 12345)) {
        qDebug() << "Failed to start server:" << server.errorString();
        return 1;
    }
    qDebug() << "Server started on port" << server.serverPort();
 
    return app.exec();
}
 
#include "main.moc"

Qt tcp客户端示例

以下是一个简单的Qt环境下的TCP客户端示例,展示了如何使用多线程连接两个TCP服务器并进行交互。

在这个示例中,我们创建了一个名为TcpClient的类,继承自QObject。我们还创建了一个名为DataProcessor的类,同样继承自QObject。TcpClient类包含了一个QTcpSocket对象,用于连接服务器和发送/接收数据。当连接建立后,TcpClient会在一个while循环中等待服务器发送数据。每当有数据到达时,TcpClient会发出一个processData()信号,这个信号会被连接到DataProcessor类的processData()槽函数中进行处理。处理完成后,DataProcessor会发出一个sendData()信号,这个信号会被连接到TcpClient的onSendData()槽函数中,将数据发送回服务器。

在main()函数中,我们创建了两个TcpClient对象,并将它们移动到不同的线程中。每个线程都会连接一个不同的服务器,并在连接建立后发送数据。我们还创建了一个DataProcessor对象,并将它移动到另一个线程中。DataProcessor会处理收到的数据并将处理后的数据发送回TcpClient,然后TcpClient将处理后的数据发送回服务器。这个过程中,DataProcessor和TcpClient是在不同的线程中运行的,因此我们使用了信号和槽来实现跨线程通信。

需要注意的是,这个示例中的DataProcessor类只是一个简单的例子,它把收到的数据转换为大写字母并返回。在实际应用中,你可以将DataProcessor替换为一个更复杂的类,用于处理实际的业务逻辑。此外,这个示例中的TcpClient类仅支持单向通信,即只有服务器向客户端发送数据。如果你需要双向通信,你需要修改TcpClient类以便它可以接收来自服务器的数据,并在必要时发送回复。

 

#include <QCoreApplication>
#include <QThread>
#include <QTcpSocket>
#include <QDebug>
#include <QMutex>
 
class DataProcessor : public QObject
{
    Q_OBJECT
public slots:
    QByteArray processData(const QByteArray &data)
    {
        qDebug() << "Processing data:" << data;
        QByteArray processedData = data.toUpper();
        return processedData;
    }
};
 
class TcpClient : public QObject
{
    Q_OBJECT
 
public:
    TcpClient(const QString &host, int port, QObject *parent = nullptr)
        : QObject(parent), m_host(host), m_port(port), m_socket(nullptr) {}
 
public slots:
    void connectToServer()
    {
        m_socket = new QTcpSocket();
        m_socket->connectToHost(m_host, m_port);
 
        if (m_socket->waitForConnected()) {
            qDebug() << "Connected to server:" << m_host << m_port;
            sendData("Hello, server!");
 
            while (m_socket->state() == QAbstractSocket::ConnectedState) {
                if (m_socket->waitForReadyRead()) {
                    QByteArray data = m_socket->readAll();
                    qDebug() << "Received data:" << data;
 
                    QByteArray processedData = processData(data);
                    sendData(processedData);
                }
            }
        } else {
            qDebug() << "Failed to connect to server:" << m_host << m_port;
        }
 
        m_socket->deleteLater();
    }
 
signals:
    QByteArray processData(const QByteArray &data);
    void sendData(const QByteArray &data);
 
private slots:
    void onSendData(const QByteArray &data)
    {
        if (m_socket && m_socket->state() == QAbstractSocket::ConnectedState) {
            m_socket->write(data);
        }
    }
 
private:
    QString m_host;
    int m_port;
    QTcpSocket *m_socket;
};
 
int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);
 
    QThread dataProcessorThread;
    DataProcessor dataProcessor;
    dataProcessor.moveToThread(&dataProcessorThread);
    dataProcessorThread.start();
 
    QThread client1Thread;
    TcpClient client1("localhost", 12345);
    client1.moveToThread(&client1Thread);
    QObject::connect(&client1, &TcpClient::processData, &dataProcessor, &DataProcessor::processData);
    QObject::connect(&client1, &TcpClient::sendData, &client1, &TcpClient::onSendData);
    QObject::connect(&client1Thread, &QThread::started, &client1, &TcpClient::connectToServer);
    client1Thread.start();
 
    QThread client2Thread;
    TcpClient client2("localhost", 12346);
    client2.moveToThread(&client2Thread);
    QObject::connect(&client2, &TcpClient::processData, &dataProcessor, &DataProcessor::processData);
    QObject::connect(&client2, &TcpClient::sendData, &client2, &TcpClient::onSendData);
    QObject::connect(&client2Thread, &QThread::started, &client2, &TcpClient::connectToServer);
    client2Thread.start();
 
    QObject::connect(&app, &QCoreApplication::aboutToQuit, [&](){
    client1Thread.quit();
    client1Thread.wait();
    client2Thread.quit();
    client2Thread.wait();
    dataProcessorThread.quit();
    dataProcessorThread.wait();
});
 
return app.exec();
 
}
 
#include "main.moc"


目录
相关文章
|
4天前
|
监控 Java 数据库连接
Java面试题:如何诊断和解决Java应用的内存泄漏问题?
Java面试题:如何诊断和解决Java应用的内存泄漏问题?
12 2
|
5天前
|
Java API
Java面试题:说明Lambda表达式在Java中的应用,以及函数式接口的概念和作用。
Java面试题:说明Lambda表达式在Java中的应用,以及函数式接口的概念和作用。
11 0
|
5天前
|
设计模式 Java
Java面试题:描述观察者模式的工作原理及其在Java中的应用。
Java面试题:描述观察者模式的工作原理及其在Java中的应用。
10 0
|
5天前
|
监控 网络协议 Java
Java面试题:解释Java NIO与BIO的区别,以及NIO的优势和应用场景。如何在高并发应用中实现NIO?
Java面试题:解释Java NIO与BIO的区别,以及NIO的优势和应用场景。如何在高并发应用中实现NIO?
11 0
|
5天前
|
Java 数据格式
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
11 0
|
5天前
|
缓存 搜索推荐 Java
Java面试题:简述CAP理论及其在分布式系统设计中的应用。请提供一个具体的例子,说明在系统设计中如何取舍一致性和可用性
Java面试题:简述CAP理论及其在分布式系统设计中的应用。请提供一个具体的例子,说明在系统设计中如何取舍一致性和可用性
11 0
|
5天前
|
设计模式 存储 缓存
Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
Java面试题:结合设计模式与并发工具包实现高效缓存;多线程与内存管理优化实践;并发框架与设计模式在复杂系统中的应用
8 0
|
5天前
|
设计模式 缓存 安全
Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
Java面试题:设计模式在并发编程中的创新应用,Java内存管理与多线程工具类的综合应用,Java并发工具包与并发框架的创新应用
7 0
|
5天前
|
并行计算 安全 算法
Java面试题:Java内存管理与多线程并发处理,设计一个Java应用,该应用需要处理大量并发用户请求,同时要求对内存使用进行优化,如何通过垃圾回收机制优化内存使用?
Java面试题:Java内存管理与多线程并发处理,设计一个Java应用,该应用需要处理大量并发用户请求,同时要求对内存使用进行优化,如何通过垃圾回收机制优化内存使用?
12 0
|
5天前
|
存储 安全 Java
Java面试题:如何在Java应用中实现有效的内存优化?在多线程环境下,如何确保数据的线程安全?如何设计并实现一个基于ExecutorService的任务处理流程?
Java面试题:如何在Java应用中实现有效的内存优化?在多线程环境下,如何确保数据的线程安全?如何设计并实现一个基于ExecutorService的任务处理流程?
10 0