多线程服务器

设计一个客户端从服务器端获取时间的程序:

服务器端使用多线程的方式,当有客户端请求到达时,服务器将启动一个新线程为它返回当前的时间,服务完后线程自动销毁,服务器端会显示连接的次数。

客户端比较简单,先搭建客户端。 

客户端的搭建 :

创建一个项目:名为:TCP_Client

pro文件添加

QT +=netwowork

ui界面添加以下控件:

dialog.h文件:

#ifndef DIALOG_H
#define DIALOG_H

#include <QDialog>
#include<QTcpSocket>
#include<QAbstractSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class Dialog; }
QT_END_NAMESPACE

class Dialog : public QDialog
{
    Q_OBJECT

public:
    Dialog(QWidget *parent = nullptr);
    ~Dialog();

private slots:
    void on_pushButton_clicked();

    void on_pushButton_2_clicked();
    void  enableBth();//设置是否可以点击获取时间
    void error_string(QAbstractSocket::SocketError);//输出错误信息

private:
    Ui::Dialog *ui;
    QTcpSocket *socket;//套接字
    uint timer;//时间
};
#endif // DIALOG_H

dialog.cpp文件:

#include "dialog.h"
#include "ui_dialog.h"

#include <QDateTime>
#include<QMessageBox>
Dialog::Dialog(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::Dialog)
{
    ui->setupUi(this);
    setWindowTitle("客户端");
    socket=new QTcpSocket(this);
    connect(ui->lineEdit,&QLineEdit::textChanged,this,&Dialog::enableBth);//当服务器名输入框内容改变时,执行该函数
    connect(ui->lineEdit_2,&QLineEdit::textChanged,this,&Dialog::enableBth);//当端口输入框内容改变时,执行该函数
    connect(socket,&QTcpSocket::readyRead,[=]()//当有可读数据时
    {
        QDataStream in(socket);//创建流
        in.setVersion(QDataStream::Qt_5_9);
        if(timer==0)
        {
            if(socket->bytesAvailable()<(int)sizeof(uint))//如果过数据太小,不接收
            {
                return;
            }
            in>>timer;//读取数据
        }
        ui->dateTimeEdit->setDateTime(QDateTime::fromTime_t(timer));//显示获取的时间
        ui->pushButton->setEnabled(true);//将获取时间的按键设为可点击
    });
//当触发error信号时,执行    connect(socket,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(error_string(QAbstractSocket::SocketError)));
    ui->lineEdit_2->setFocus();//获取焦点
}

Dialog::~Dialog()
{
    delete ui;
}


void Dialog::on_pushButton_clicked()//获取时间
{
    ui->lineEdit->setEnabled(false);
    timer=0;
    socket->abort();//断开已有连接
    socket->connectToHost(ui->lineEdit->text(),ui->lineEdit_2->text().toInt());//创建连接
}

void Dialog::on_pushButton_2_clicked()//退出
{
    close();//关闭界面
}
void Dialog::enableBth()
{
    ui->pushButton->setEnabled(!ui->lineEdit->text().isEmpty()&&!ui->lineEdit_2->text().isEmpty());//端口和主机不能为空
}
void Dialog::error_string(QAbstractSocket::SocketError socketerror)//输出错误信息
{
    switch(socketerror)
    {
    case QAbstractSocket::RemoteHostClosedError:
        break;
    case QAbstractSocket::HostNotFoundError:
        QMessageBox::information(this,"错误信息",tr("主机不可达"));
        break;
    case QAbstractSocket::ConnectionRefusedError:
        QMessageBox::information(this,"错误信息",tr("连接被拒绝"));
    default:
        QMessageBox::information(this,"错误信息",tr("%1").arg(socket->errorString()));//获取错误信息
    }
    ui->pushButton->setEnabled(true);

}

服务器的搭建:

难点在于如何搭建多线程服务器。

创建一个新项目:Server_thread

服务器的搭建需要创建3个类:

  • 主界面类:接受和显示连接次数的类:tcp_server
  • 线程类:用来通过套接字发送时间的类:timethread类
  • 服务器类:用来建立连接的类:timeserver

搭建的关键:服务器有连接时,使用线程进行执行,(所以要重写有连接时的函数)线程执行完发送信号给主类,显示数据。

每个类的搭建过程:

主页面类:

 服务器类:

 线程类:

 

目录: 

 

1.创建项目时已经创建了tcp_server类 (主界面)

pro文件中添加:

QT  +=network

2.创建 线程类:继承自QThread

timeThread.h文件

#ifndef TIMETHREAD_H
#define TIMETHREAD_H

#include <QThread>
#include<QTcpServer>
#include<QTcpSocket>
#include<QDataStream>
class timeThread : public QThread//继承自QThread
{
    Q_OBJECT
public:
    explicit timeThread(qintptr socketDescriptor,QObject *parent=nullptr);//初始时时需要传入套接字描述
    void run();//重写run函数

signals:
    void error(QTcpSocket::SocketError socketError);//出错信号


private:
      qintptr socketDescriptor;  //套接字描述

};

#endif // TIMETHREAD_H

 timethread.cpp文件

#include "timethread.h"

#include <QDateTime>

timeThread::timeThread(qintptr socketDescriptor,QObject *parent)
    : QThread(parent),socketDescriptor(socketDescriptor)//初始化套接字描述
{

}
void timeThread::run()//重写run函数,服务器有连接使,创建套接字来传输数据
{
    QTcpSocket tcpSocker;
    if(!tcpSocker.setSocketDescriptor(socketDescriptor))
    {
        emit error(tcpSocker.error());//触发错误信号
        return;
    }
    QByteArray block;
    QDataStream out(&block,QIODevice::WriteOnly);
    out.setVersion(QDataStream::Qt_5_9);//设置版本好
    uint tim2u=QDateTime::currentDateTime().toTime_t();//获取时间,转化为标准格式
    out<<tim2u;
    tcpSocker.write(block);//传回客户端
    tcpSocker.disconnectFromHost();//断开连接
    tcpSocker.waitForDisconnected();//等待返回

}

3.创建服务器类:timeserver 继承自QTcpServer

timeserver.h文件:

#ifndef TCP_SERVER_H
#define TCP_SERVER_H
#include "timeserver.h"
#include <QDialog>
#include<QTcpServer>
#include<QTcpSocket>
#include<QLabel>
#include<QPushButton>
QT_BEGIN_NAMESPACE
namespace Ui { class TCP_server; }
QT_END_NAMESPACE
class timeserver;
class TCP_server : public QDialog
{
    Q_OBJECT
public:
    TCP_server(QWidget *parent = nullptr);
    ~TCP_server();
private slots:
    void slotShow();//用来显示界面上的请求次数

private:
    Ui::TCP_server *ui;
    QLabel *lab1;//显示端口号
    QLabel *lab2;//显示已连接的个数
    QPushButton *pbt;//退出按键
    timeserver *server;//服务器端
    int count;//统计个数

};
#endif // TCP_SERVER_H

timeserver.cpp文件:

#include "timeserver.h"
#include<timethread.h>
timeserver::timeserver(QObject *parent) : QTcpServer(parent)
{
    tcp_Dia=(TCP_server *)parent;//获取创建这个timeserver对象的父类
}
void timeserver::incomingConnection(qintptr socketDescriptor)
{
    timeThread *thread=new timeThread(socketDescriptor,0);//创建一个工作线程
    connect(thread,SIGNAL(finished()),tcp_Dia,SLOT(slotShow()),Qt::QueuedConnection);//使用排队连接方式
    connect(thread,SIGNAL(finished()),thread,SLOT(deleteLater()),Qt::DirectConnection);//当线程结束时,销毁线程
    thread->start();//开启线程
}

4.Tcp_Server(主界面)

ui界面中添加以下控件: QLabel    QLabel   QPushbutton

 

tcp_server.h文件:

#ifndef TCP_SERVER_H
#define TCP_SERVER_H
#include "timeserver.h"
#include <QDialog>
#include<QTcpServer>
#include<QTcpSocket>
#include<QLabel>
#include<QPushButton>
QT_BEGIN_NAMESPACE
namespace Ui { class TCP_server; }
QT_END_NAMESPACE

class timeserver;//服务器端的前置声明
class TCP_server : public QDialog
{
    Q_OBJECT


public:
    TCP_server(QWidget *parent = nullptr);
    ~TCP_server();
private slots:
    void slotShow();//用来显示界面上的请求次数

    void on_pushButton_clicked();//退出

private:
    Ui::TCP_server *ui;
    timeserver *server;//服务器端
    int count;//统计个数

};
#endif // TCP_SERVER_H

tcp_server.cpp文件:

#include "tcp_server.h"
#include "ui_tcp_server.h"
#include <QMessageBox>
#include<QHBoxLayout>
#include<QVBoxLayout>
TCP_server::TCP_server(QWidget *parent)
    : QDialog(parent)
    , ui(new Ui::TCP_server)
{
    ui->setupUi(this);
    //控件初始化
    setWindowTitle(tr("多线程时间服务器"));
    lab1=new QLabel("服务器端口");
    lab2=new QLabel;
    pbt=new QPushButton(tr("退出"));
    QHBoxLayout *hlay=new QHBoxLayout;
    hlay->addStretch(1);
    hlay->addWidget(pbt);
    hlay->addStretch(1);
    QVBoxLayout *vlay=new QVBoxLayout(this);
    vlay->addWidget(lab1);
    vlay->addWidget(lab2);
    vlay->addLayout(hlay);


    connect(pbt,&QPushButton::clicked,this,&TCP_server::close);//点击退出关闭窗口
    count=0;
    server=new timeserver(this);
    if(!server->listen())
    {
        QMessageBox::information(this,"提示信息",tr("无法启动服务器:%1").arg(server->errorString()));

        return;
    }
    lab1->setText(tr("服务器端口:%1").arg(server->serverPort()));
}

TCP_server::~TCP_server()
{
    delete ui;
}
void TCP_server::slotShow()//用来显示界面上的请求次数
{
    lab2->setText(tr("第%1次请求完毕").arg(++count));
}

运行效果:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值