[qt] 多线程应用01

源码: 点击此处

一 多线程应用

  • 实现一个多线程的网络时间服务器,利用多线程功能的技术,为每个客户端返回当前的时间,并且在返回后自动退出。同时,服务器也会记录当前受到的请求次数。
  • 其实这相当于一个ntp时间服务器

二 服务器实现

2.1 创建服务器UI

  • 首先我们值得注意的是,这些new出来的对象都没有进行释放,是因为对于qt来说他的布局管理器会接管该布局上的控件或布局的所有权,并且我们在vLayout布局上设置了父类指针this,此时当这个类被释放时,vLayout和其上面的控件都会被释放。

#include <QDialog>

class QLabel;
class QPushButton;
class MainWindow : public QDialog
{
    Q_OBJECT

public:
    MainWindow(QDialog *parent = nullptr);
    ~MainWindow();

private:
    QLabel *m_label1;    // 显示监听端口
    QLabel *m_label2;    // 显示请求次数
    QPushButton *m_btn;  // 退出按钮
};


MainWindow::MainWindow(QDialog *parent)
    : QDialog(parent)
{
    setWindowTitle("多线程时间服务器");
    m_label1 = new QLabel(tr("服务器端口"));
    m_label2 = new QLabel;
    m_btn    =  new QPushButton(tr("退出"));
    QHBoxLayout *hLayout = new QHBoxLayout;
    hLayout->addStretch(1);
    hLayout->addWidget(m_btn);
    hLayout->addStretch(1);
    QVBoxLayout *vLayout = new QVBoxLayout(this);
    vLayout->addWidget(m_label1);
    vLayout->addWidget(m_label2);
    vLayout->addLayout(hLayout);
    connect(m_btn,&QPushButton::clicked,this,&MainWindow::close);

    m_count = 0;
    m_server = new CTcperver(this);
    if(!m_server->listen())
    {
        QMessageBox::critical(this,tr("多线程时间服务器"),tr("无法启动服务器: %1").arg(m_server->errorString()));
        close();
        return;
    }
    m_label1->setText(tr("服务器端口: %1").arg(m_server->serverPort()));
}

2.2 创建服务器处理socket

  • 首先我们继承QThread并重写run方法,但是要切记的是,这里的构造函数要传入socket描述符。
  • tcpSocket.setSocketDescriptor(m_sockerIntptr):用socket描述来构造出一个socket连接。
  • 这里使用数据流的方式写入数据,并且设置了数据流版本,这很重要,能够保证写入和读取的版本兼容性,因为当您设置数据流版本时,QDataStream会对数据进行特定版本的序列化处理。这意味着在序列化过程中,数据会被按照特定版本的要求进行格式化。
#include <QThread>
#include <QTcpSocket>

class CTimeThread : public QThread
{
    Q_OBJECT
public:
    explicit CTimeThread(int socketDescriptor,QObject *parent = nullptr);
protected:
    void run() override;

signals:
    void error(QTcpSocket::SocketError errStr);

private:
    int m_sockerIntptr;
};
CTimeThread::CTimeThread(int socketDescriptor,QObject *parent)
    :socketDescriptor(m_sockerIntptr),QObject{parent}
{}

void CTimeThread::run()
{
    QTcpSocket tcpSocket;
    if(!tcpSocket.setSocketDescriptor(m_sockerIntptr))
    {
        emit error(tcpSocket.error());`
            return;
    }
    QByteArray block;
    QDataStream out(&block,QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_5_12);
    uint time = QDateTime::currentDateTime().toSecsSinceEpoch();
    out<<time;
    tcpSocket.write(block);  // 将获取的当前时间传回客户端
    tcpSocket.disconnectFromHost(); // 断开连接
    tcpSocket.waitForDisconnected(); // 等待返回
}

2.3 服务器类

  1. 创建了一个继承自QTcpServer的类,
  2. 在成员函数中创建了一个之前创建的MainWindow也就是服务器的ui类
  3. 并且在构造函数中直接用传入的parent给他转换,这里首先要理解,对于C++继承来说,必须构造父类的构造函数才能构造成功,而QTcpServer就是构造父类,而(parent)的作用只是传递当前类的父对象,告诉qt的内存管理机制。本类的父对象是哪个。
  4. 然后在incoming里面进行的socket的处理,使用socket描述符创建对应的线程来处理。
  5. 在connect函数中处理了socket的线程的资源释放。
#include <QTcpServer>

class MainWindow;
class CTcperver : public QTcpServer
{
    Q_OBJECT
public:
    explicit CTcperver(QObject *parent = nullptr);
protected:
    void incomingConnection(int sockerDescriptor);
private:
    MainWindow *dlg;
};


CTcperver::CTcperver(QObject *parent)
    : QTcpServer(parent)
{
    dlg = (MainWindow *)parent;
}

void CTcperver::incomingConnection(int sockerDescriptor)
{
    CTimeThread *thread = new CTimeThread(sockerDescriptor,0);
    connect(thread,&CTimeThread::finished,dlg,&MainWindow::slotShow);
    connect(thread,&CTimeThread::finished,thread,&CTimeThread::deleteLater,Qt::DirectConnection);
    thread->start();
}

2.4 调用

    MainWindow w;
    w.show();

三 继承问题

  • 对于C++的子类继承父类必须要构造父类的构造函数
  • 在qt中使用构造函数比如上面的QTcpServer(parent),其实包含两个作用,第一完成基类的构造函数,而将传入的parent给当前类设置了父对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值