【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"


目录
相关文章
|
6月前
|
网络协议 容器
【qt】 TCP编程小项目
【qt】 TCP编程小项目
106 0
|
2月前
|
算法 测试技术 持续交付
面试的流程,面试的重点
本文介绍了面试流程及各轮面试的重点。通常面试为1-5轮,首轮关注技术实力与项目经验,次轮深入考察技术细节,第三轮侧重项目协调、创新及价值观等软性问题,如职业规划和沟通能力。面试题分为开放型(如项目经验、解决问题思路)和非开放型(如技术细节、手撕算法),需提前准备。测试类问题涉及自动化测试、持续集成等实际应用。
|
4天前
|
存储 NoSQL 前端开发
美团面试:手机扫描PC二维码登录,底层原理和完整流程是什么?
45岁老架构师尼恩详细梳理了手机扫码登录的完整流程,帮助大家在面试中脱颖而出。该过程分为三个阶段:待扫描阶段、已扫描待确认阶段和已确认阶段。更多技术圣经系列PDF及详细内容,请关注【技术自由圈】获取。
|
6月前
|
网络协议
【qt】TCP客户端信息的接受和发送
【qt】TCP客户端信息的接受和发送
44 0
|
3月前
|
缓存 负载均衡 网络协议
面试:TCP、UDP如何解决丢包问题
TCP、UDP如何解决丢包问题。TCP:基于数据块传输/数据分片、对失序数据包重新排序以及去重、流量控制(滑动窗口)、拥塞控制、自主重传ARQ;UDP:程序执行后马上开始监听、控制报文大小、每个分割块的长度小于MTU
|
6月前
|
JavaScript
【Vue面试题四】、Vue实例挂载的过程中发生了什么?
文章详细分析了Vue实例挂载的过程,包括Vue构造函数的执行、初始化方法`_init`的调用,以及Vue实例从创建到挂载的各个阶段。文章提到了Vue实例初始化过程中的多个关键步骤,如合并选项、初始化数据、事件、生命周期、渲染方法等。同时,还解释了Vue如何处理模板和生成渲染函数,以及如何将虚拟DOM转换为真实DOM并进行页面渲染。最后,文章通过流程图总结了Vue实例挂载的整个过程。
【Vue面试题四】、Vue实例挂载的过程中发生了什么?
|
3月前
|
网络协议 算法 网络性能优化
计算机网络常见面试题(一):TCP/IP五层模型、TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议
计算机网络常见面试题(一):TCP/IP五层模型、应用层常见的协议、TCP与UDP的区别,TCP三次握手、四次挥手,TCP传输可靠性保障、ARQ协议、ARP协议
|
4月前
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
111 6
|
4月前
|
XML 前端开发 Android开发
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
Android面试高频知识点(3) 详解Android View的绘制流程
|
4月前
|
消息中间件 Android开发 索引
Android面试高频知识点(4) 详解Activity的启动流程
Android面试高频知识点(4) 详解Activity的启动流程
41 3

热门文章

最新文章