1.要求
我们需要实现一个多线程的网络实际服务器:每当有新的客户请求到达时,服务器将启动一个新的线程为它返回当前的时间,服务完毕之后,这个线程将会自动退出。同时,用户界面会显示当前已经接收请求的次数。
2.执行效果
3.代码实现
1.服务端代码
创建 Qt
项目名为 TimeServer
,定义服务器端界面类 Dialog
继承自 QDialog
。
在 TimeServer.pro
中添加 network
:
dialog.h
:
#ifndef DIALOG_H
#define DIALOG_H
#include <QDialog>
#include <QLabel>
#include <QPushButton>
class TimeServer;
class Dialog : public QDialog
{
Q_OBJECT
public:
Dialog(QWidget *parent = nullptr);
~Dialog();
//监听端口
QLabel * port_label;
//请求次数
QLabel * request_count_label;
//退出按钮
QPushButton * quitBtn;
public slots:
//用于界面上显示的请求次数
void slotShow();
private:
TimeServer * timeServer;
int count;
};
#endif // DIALOG_H
dialog.cpp
:
#include "dialog.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QMessageBox>
#include "timeserver.h"
Dialog::Dialog(QWidget *parent)
: QDialog(parent)
{
setWindowTitle(tr("多线程时间服务器"));
port_label = new QLabel(tr("服务器端口:"));
request_count_label = new QLabel;
quitBtn = new QPushButton(tr("退出"));
count = 0;
timeServer = new TimeServer(this);
QHBoxLayout * btnLayout = new QHBoxLayout;
btnLayout->addStretch(1);
btnLayout->addWidget(quitBtn);
btnLayout->addStretch(1);
QVBoxLayout *mainLayout = new QVBoxLayout(this);
mainLayout->addWidget(port_label);
mainLayout->addWidget(request_count_label);
mainLayout->addLayout(btnLayout);
if(!timeServer->listen()){
QMessageBox::critical(this,tr("多线程时间服务器"),
tr("无法启动服务器: %1. ").arg(timeServer->errorString()));
close();
return;
}
port_label->setText(tr("服务器端口 : %1.").arg(timeServer->serverPort()));
//点击退出按钮 退出
connect(quitBtn,SIGNAL(clicked()),this,SLOT(close()));
}
void Dialog::slotShow(){
request_count_label->setText(tr("第 %1 次请求完毕.").arg(++count));
}
Dialog::~Dialog()
{
}
timethread.h
:
#ifndef TIMETHREAD_H
#define TIMETHREAD_H
#include <QThread>
#include <QtNetwork>
#include <QTcpSocket>
class TimeThread : public QThread
{
Q_OBJECT
public:
TimeThread(int socketDescriptor,QObject * parent = 0);
void run() override;
signals:
void error(QTcpSocket::SocketError socketError);
private:
int socketDescriptor;
};
#endif // TIMETHREAD_H
timethread.cpp
:
#include "timethread.h"
TimeThread::TimeThread(int socketDescriptor,QObject * parent):
QThread(parent),socketDescriptor(socketDescriptor)
{
}
void TimeThread::run(){
QTcpSocket tcpSocket;
//连接失败
if(!tcpSocket.setSocketDescriptor(socketDescriptor)){
emit error(tcpSocket.error());
return;
}
QByteArray block;
QDataStream out(&block , QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_9);
//获取当前时间的秒数
uint time2u = QDateTime::currentDateTime().toTime_t();
out<<time2u;
tcpSocket.write(block);
//断开连接
tcpSocket.disconnectFromHost();
//等待返回
tcpSocket.waitForDisconnected();
}
timeserver.h
:
#ifndef TIMESERVER_H
#define TIMESERVER_H
#include <QTcpServer>
class Dialog; //服务端的声明
class TimeServer : public QTcpServer
{
Q_OBJECT
public:
TimeServer(QObject * parent = 0);
protected:
void incomingConnection(qintptr socketDescriptor) override;
private:
Dialog * dlg;
};
#endif // TIMESERVER_H
timeserver.cpp
:
#include "timeserver.h"
#include "timethread.h"
#include "dialog.h"
TimeServer::TimeServer(QObject * parent) : QTcpServer(parent)
{
dlg = (Dialog*) parent;
}
void TimeServer::incomingConnection(qintptr socketDescriptor){
TimeThread * thread = new TimeThread(socketDescriptor,0);
connect(thread,SIGNAL(finished()),dlg,SLOT(slotShow()));
connect(thread,SIGNAL(finished()),thread,SLOT(deleteLater()),
Qt::DirectConnection);
thread->start();
}
main.cpp
:
#include "dialog.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Dialog w;
w.show();
return a.exec();
}
2. 客户端代码
创建 Qt
项目名为 TimeClient
,定义服务器端界面类 TimeClient
继承自 QDialog
。
在 TimeClient.pro
中添加 network
:
timeclient.h
:
#ifndef TIMECLIENT_H
#define TIMECLIENT_H
#include <QDialog>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QDateTimeEdit>
#include <QTcpSocket>
#include <QAbstractSocket>
class TimeClient : public QDialog
{
Q_OBJECT
public:
TimeClient(QWidget *parent = nullptr);
~TimeClient();
public slots:
void enableGetBtn();
void getTime();
void readTime();
void showError(QAbstractSocket::SocketError socketError) ;
private:
QLabel * serverNameLabel;
QLineEdit * serverNameEdit;
QLabel * portLabel;
QLineEdit * portLineEdit;
QDateTimeEdit * dateTimeEdit;
QLabel * stateLabel;
QPushButton * getBtn;
QPushButton * quitBtn;
uint time2u;
QTcpSocket * tcpSocket;
};
#endif // TIMECLIENT_H
timeclient.cpp
:
#include "timeclient.h"
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QGridLayout>
#include <QDataStream>
#include <QMessageBox>
TimeClient::TimeClient(QWidget *parent)
: QDialog(parent)
{
setWindowTitle((tr("多线程时间服务客户端")));
serverNameLabel = new QLabel(tr("服务器名:"));
serverNameEdit = new QLineEdit(tr("Localhost"));
portLabel = new QLabel(tr("端口:"));
portLineEdit = new QLineEdit;
QGridLayout * layout = new QGridLayout;
layout->addWidget(serverNameLabel,0,0);
layout->addWidget(serverNameEdit,0,1);
layout->addWidget(portLabel,1,0);
layout->addWidget(portLineEdit,1,1);
dateTimeEdit = new QDateTimeEdit(this);
QHBoxLayout *layout1 = new QHBoxLayout;
layout1->addWidget(dateTimeEdit);
stateLabel = new QLabel(tr("获取时间"));
QHBoxLayout * layout2 = new QHBoxLayout;
layout2->addWidget(stateLabel);
getBtn = new QPushButton(tr("获取时间"));
getBtn->setDefault(true);
getBtn->setEnabled(false);
quitBtn = new QPushButton(tr("退出"));
QHBoxLayout * layout3 = new QHBoxLayout;
layout3->addStretch();
layout3->addWidget(getBtn);
layout3->addWidget(quitBtn);
QVBoxLayout * mainLayout = new QVBoxLayout(this);
mainLayout->addLayout(layout);
mainLayout->addLayout(layout1);
mainLayout->addLayout(layout2);
mainLayout->addLayout(layout3);
connect(serverNameEdit,SIGNAL(textChanged(QString)),
this,SLOT(enableGetBtn()));
connect(portLineEdit,SIGNAL(textChanged(QString)),
this,SLOT(enableGetBtn()));
connect(getBtn,SIGNAL(clicked()),this,SLOT(getTime()));
connect(quitBtn,SIGNAL(clicked()),this,SLOT(close()));
tcpSocket = new QTcpSocket(this);
connect(tcpSocket,SIGNAL(readyRead()),this,SLOT(readTime()));
connect(tcpSocket,SIGNAL(error(QAbstractSocket::SocketError)),
this,SLOT(showError(QAbstractSocket::SocketError)));
portLineEdit->setFocus();
}
void TimeClient::enableGetBtn(){
//如果 serverName 和 port 都不为空,说明已经获取到了
//即可以再次点击获取下一次的数据,故为 true
getBtn->setEnabled(!serverNameEdit->text().isEmpty() && !portLineEdit->text().isEmpty());
}
void TimeClient::getTime(){
getBtn->setEnabled(false);
time2u = 0;
tcpSocket->abort();
//Tcp 连接
tcpSocket->connectToHost(serverNameEdit->text(),portLineEdit->text().toInt());
}
void TimeClient::readTime(){
QDataStream in(tcpSocket);
in.setVersion(QDataStream::Qt_5_9);
if(time2u == 0){
if(tcpSocket->bytesAvailable() < (int) sizeof(uint)) return;
in >> time2u;
}
dateTimeEdit->setDateTime(QDateTime::fromTime_t(time2u));
getBtn->setEnabled(true);
}
void TimeClient::showError(QAbstractSocket::SocketError socketError){
switch(socketError){
case QAbstractSocket::RemoteHostClosedError:{
break;
}
case QAbstractSocket::HostNotFoundError:{
QMessageBox::information(this,tr("时间服务客户端"),tr("主机不可达!"));
break;
}
case QAbstractSocket::ConnectionRefusedError:{
QMessageBox::information(this,tr("时间服务客户端"),tr("连接被拒绝!"));
break;
}
default:{
QMessageBox::information(this,tr("时间服务客户端"),
tr("产生如下错误: %1 .").arg(tcpSocket->errorString()));
}
}
getBtn->setEnabled(true);
}
TimeClient::~TimeClient()
{
}
main.cpp
:
#include "timeclient.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TimeClient w;
w.show();
return a.exec();
}