源码: 点击此处
一 多线程应用
- 实现一个多线程的网络时间服务器,利用多线程功能的技术,为每个客户端返回当前的时间,并且在返回后自动退出。同时,服务器也会记录当前受到的请求次数。
- 其实这相当于一个ntp时间服务器
二 服务器实现
2.1 创建服务器UI
- 首先我们值得注意的是,这些new出来的对象都没有进行释放,是因为对于qt来说他的布局管理器会接管该布局上的控件或布局的所有权,并且我们在vLayout布局上设置了父类指针this,此时当这个类被释放时,vLayout和其上面的控件都会被释放。
#include <QDialog>class QLabel;
class QPushButton;
class MainWindow : public QDialog
{Q_OBJECTpublic: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 服务器类
- 创建了一个继承自QTcpServer的类,
- 在成员函数中创建了一个之前创建的MainWindow也就是服务器的ui类
- 并且在构造函数中直接用传入的parent给他转换,这里首先要理解,对于C++继承来说,必须构造父类的构造函数才能构造成功,而QTcpServer就是构造父类,而(parent)的作用只是传递当前类的父对象,告诉qt的内存管理机制。本类的父对象是哪个。
- 然后在incoming里面进行的socket的处理,使用socket描述符创建对应的线程来处理。
- 在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给当前类设置了父对象。