运用多线程知识写一个火车站售票系统

题目简介:
在现实世界中有一种数据模型,模拟生产者与消费者模型。如火车票售票系统,将票据的数量录入服务器我们称之为“生产”了若干车票。各地终端会共享这些车票信息去售票。直到车票售票完,则提示无票可卖。
本练习题,在同一台电脑上使用不同的进程(独立运行的应用程序)模拟火车票售票系统。将录入存放火车票的独立运行程序我们称之为服务端。一台电脑只能运行一个实例。其他负责售卖火车票的程序称为客户端。客户端程序,一台电脑可以运行多个实例。客户端程序主要的功能就是消费服务端的火车票。

要求:

  1. 你可以编写带界面的应用程序,也可以编写不带界面的命令行应用程序。
  2. 当客户端关闭时,服务端能够给予提示,当有客户端启动连接时,服务端给予提示。
  3. 当客户段消耗一张车票时,服务端提示信息(信息包含那个客户端,什么时候消耗一张票)。
  4. 假设每个客户端均有一个唯一的编号。
  5. 客户端是模拟买票,可以使用定时器,设置间隔周期,如2秒消耗一张票。
  6. 当客户端无票可卖时(服务器的票已经被消耗完),给予提示。
  7. 当服务器卖完所有的票时给予提示,并统计客户端的卖票情况(每个客户的售票详情,如张数,时间)。

题目难点:
主要考察的是多线程的应用,因为服务器只有一个,不同的客户端购票时间,信息都不相同

开发环境:
QT Creator

//client.h 客户端的界面
#ifndef CLIENT_H
#define CLIENT_H

#include <QWidget>
#include <QTcpSocket>
#include <QLineEdit>
#include <QTextBrowser>
#include <QLabel>

class Client : public QWidget
{
    Q_OBJECT
public:
    explicit Client(QWidget *parent = 0);
    QTcpSocket* _socket;
    QLineEdit* _lineEdit1;
    QLineEdit* _lineEdit2;
    QLineEdit* _lineEdit3;
    QLineEdit* _lineEdit4;
    QTextBrowser* _show;
    QLabel* _lable1;
    QLabel* _lable2;
    QLabel* _lable3;
    QLabel* _lable4;
    void countticket();
signals:
public slots:
    void slotButtonClick();//购票按钮响应函数
    void slotexBtnClick();//退出按钮响应
};

#endif // CLIENT_H
//client.cpp
#include "client.h"
#include "tcpsocket.h"
#include <QWidget>
#include <QGridLayout>
#include <QPushButton>
#include <QTime>

int temp;
int ticketcount = 100;
QByteArray str_data;
Client::Client(QWidget *parent) :
    QWidget(parent)
{
    _socket = new QTcpSocket(this);
    _socket->connectToHost("127.0.0.1", 9988);

    _lable1= new QLabel(this);
    _lable1->setText("购买票数:");

    _lable2= new QLabel(this);
    _lable2->setText("乘车时间:");

    _lable3= new QLabel(this);
    _lable3->setText("始发站:");

    _lable4= new QLabel(this);
    _lable4->setText("终点站:");

    QGridLayout* lay = new QGridLayout(this);
    lay->addWidget(_lable1,0,0,1,1);
    lay->addWidget(_lable2,2,0,1,1);
    lay->addWidget(_lable3,0,1,1,1);
    lay->addWidget(_lable4,2,1,1,1);

    _lineEdit1 = new QLineEdit(this);
    _lineEdit1->setPlaceholderText("请输入两位数,如02:");
    _lineEdit2 = new QLineEdit(this);
    _lineEdit2->setPlaceholderText("格式如20170724:");
    _lineEdit3 = new QLineEdit(this);
    _lineEdit3->setPlaceholderText("请输入始发站,如西安:");
    _lineEdit4 = new QLineEdit(this);
    _lineEdit4->setPlaceholderText("请输入终点站,如西安:");

    lay->addWidget(_lineEdit1,1,0,1,1);
    lay->addWidget(_lineEdit2,3,0,1,1);
    lay->addWidget(_lineEdit3,1,1,1,1);
    lay->addWidget(_lineEdit4,3,1,1,1);

    QPushButton* button = new QPushButton("购买");
    lay->addWidget(button,4,0,1,1);

    QPushButton* exBtn = new QPushButton("退出");
    lay->addWidget(exBtn,4,1,1,1);

    _show = new QTextBrowser(this);
    lay->addWidget(_show,5,0,1,2);

    connect(button, SIGNAL(clicked()), this, SLOT(slotButtonClick()));//买票
    connect(exBtn, SIGNAL(clicked()), this, SLOT(slotexBtnClick()));//退出客户端
    connect(_lineEdit4, SIGNAL(returnPressed()), this, SLOT(slotButtonClick()));//使用回车键发送
}
//买票按钮响应
void Client::slotButtonClick()
{
    QString strText1 = _lineEdit1->text();
    QString strText2 = _lineEdit2->text();
    QString strText3 = _lineEdit3->text();
    QString strText4 = _lineEdit4->text();


    if(strText1.isEmpty())
        return;
    bool ok;
    temp = strText1.toInt(&ok,10);

    QString strText = " "+strText1+tr("张")+" "+strText3+tr("-")+strText4+" "+strText2+tr("的票");
    _socket->write(strText.toUtf8());
    countticket();


    _lineEdit1->clear();
    _lineEdit2->clear();
    _lineEdit3->clear();
    _lineEdit4->clear();


}
//退出按钮响应
void Client::slotexBtnClick()
{
    //退出前给服务器发送“close”标志
    QString strclose = "close";
    _socket->write(strclose.toUtf8());
    close();
}
//买票具体情况
void Client::countticket()
{
    QTime time = QTime::currentTime();
    QString str = time.toString("H:mm:ss");
    QString strText = _lineEdit1->text();
    bool ok;
    int temp1 = strText.toInt(&ok,10);
    int temp2 = ticketcount-temp1;
    if(temp2>=0)
    {
        ticketcount= ticketcount-temp1;
        str_data=QByteArray::number(temp1,10);
         _show->append("购买成功,您购买了"+str_data+"张票"+str);
    }
    else
    {
        str_data=QByteArray::number(ticketcount,10);
        _show->append("购买失败,剩余"+str_data+"张票");
    }
}
//server.h 服务器的界面
#ifndef SERVER_H
#define SERVER_H

#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QTextBrowser>
#include <QLabel>
#include <QListWidget>

class Tcpserver;
class Server : public QWidget
{
    Q_OBJECT
public:
    explicit Server(QWidget* parent = 0);
private:
    QTcpSocket* _socket;
    QTextBrowser* _show;
    QLabel* _label;
    QList<QString> evetick;               //每个乘客买票情况
    QListWidget* list;
protected:
signals:
public slots:
    void recsell(QByteArray buf,QString byte,QString str_id);     //接收tcpserver端传来售票的数据
    void slotbuttonClick();//购票统计按钮
};

#endif // SERVER_H
//server.cpp
#include "server.h"
#include "tcpsocket.h"
#include "tcpserver.h"
#include "mainwindow.h"
#include <QTime>
#include <QWidget>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QPushButton>
#include <QDebug>

int ticket = 100;
QString strid_temp;
Server::Server(QWidget *parent) :
    QWidget(parent)
{
    // 创建服务器并监听
    Tcpserver *server = new Tcpserver();
    server->listen(QHostAddress::Any,9988);
    //接收tcpserver端发送过来的数据
    connect(server,SIGNAL(transsell(QByteArray,QString,QString)),this,SLOT(recsell(QByteArray,QString,QString)));

    _show = new QTextBrowser;
    _show->setMaximumSize(300,300);
    _show->setMinimumSize(300,300);

    QHBoxLayout* lay = new QHBoxLayout(this);
    lay->addWidget(_show,0);

    QPushButton* button = new QPushButton("售票统计");
    lay->addWidget(button,1);
    connect(button, SIGNAL(clicked()), this, SLOT(slotbuttonClick()));
}
//接收函数,接收tcpserver端发送过来的数据
void Server::recsell(QByteArray buf,QString byte,QString str_id)
{
    //如果客户端点击 退出 按钮,则断开连接
    if(buf == "close")
    {
        _show->append(str_id+"客户端连接断开......");
    }
    else
    {
        //同一客户端只显示一次连接成功
        if(strid_temp != str_id)
        {
            _show->append(str_id+"客户端连接建立成功......");
        }
        //显示当前时间
        QTime time = QTime::currentTime();
        QString str_time = time.toString("H:mm:ss");

        QString str_buf = buf.mid(1,buf.length()).left(2);
        qDebug()<<str_buf;
        int temp;
        bool ok;
        int buf1 = str_buf.toInt(&ok,10 );

        QByteArray str_data;
        temp = ticket-buf1;//计算剩余票数

        if(temp>=0)
        {
            ticket= ticket-buf1;
             str_data=QByteArray::number(ticket,10);
            _show->append(str_id+"客户端售出"+str_buf+"张票"+" "+str_time+" "+"还剩"+str_data+"张票");
            evetick.push_back(byte);//将每个客户端每次售票情况写入evetick中
        }
        else
        {
            temp = -1;
            str_data=QByteArray::number(temp,10);
            str_data=QByteArray::number(ticket,10);
            _show->append("售票失败,还剩"+str_data+"张票");
        }
    }
    strid_temp = str_id;
}
//售票统计按钮响应
void Server::slotbuttonClick()
{
    QWidget* window = new QWidget();
    window->setWindowTitle(tr("售票统计"));
    window->resize(500,500);

    list = new QListWidget(window);

    QGridLayout *layout = new QGridLayout(window);
    layout->addWidget(list);

    for(int i = 0;i < evetick.size();++i)
    {
       list->insertItem(i,evetick.at(i));
    }
    window->show();
}
//tcpserver.h 创建多线程
#ifndef TCPSERVER_H
#define TCPSERVER_H

#include <QWidget>
#include <QObject>
#include <QTcpServer>
#include <QString>
#include <QMap>
#include "server.h"

class Mytcpsocket;
class Tcpserver:public QTcpServer
{
    Q_OBJECT
public:
    Tcpserver(QWidget* parent = 0);
protected:
    void incomingConnection(qintptr socketDescriptor);

signals:
    void transsell(QByteArray,QString,QString);//接收tcpsocket传来的信号,并发射给server端,处理数据
private:
    QString id;//传送客户端编号
};

#endif // TCPSERVER_H
//tcpserver.cpp
#include "tcpserver.h"
#include "tcpsocket.h"
#include "mythread.h"

Tcpserver::Tcpserver(QWidget* parent):QTcpServer(parent)
{

}
void Tcpserver::incomingConnection(qintptr socketDescriptor)
{
    Mytcpsocket* socket = new Mytcpsocket();
    socket->tcpSocket = new QTcpSocket();
    socket->tcpSocket->setSocketDescriptor(socketDescriptor);

    QThread *thread = new QThread();

    socket->moveToThread(thread);

    connect(socket->tcpSocket,SIGNAL(readyRead()),socket,SLOT(receiveData()),Qt::QueuedConnection);

    thread->start();
    //将tcpsocket端发送的信号接收过来
    connect(socket,SIGNAL(transsell(QByteArray,QString,QString)),this, SIGNAL(transsell(QByteArray,QString,QString)));
}
//tcpsocket.h 不同线程中的socket通信
#ifndef Mytcpsocket_H
#define Mytcpsocket_H

#include <QWidget>
#include <QTcpSocket>
#include <QObject>

class Mytcpsocket:public QObject
{
    Q_OBJECT
public:
    explicit Mytcpsocket(QObject* parent=0);
signals:
    void transsell(QByteArray,QString,QString); //发射给tcpserver端
public slots:
    void receiveData();         //在子线程中处理client端传来的信息
public:
    QTcpSocket *tcpSocket;
    QString byte;
    QString str_id;
};

#endif // Mytcpsocket_H
//tcpsocket.cpp
#include "tcpsocket.h"
#include "server.h"
#include <QThread>
#include <QDebug>
#include <QTime>

Mytcpsocket::Mytcpsocket(QObject* parent):QObject(parent)
{
   tcpSocket = new QTcpSocket();
}
//将每个客户端的具体情况发送出去
void Mytcpsocket::receiveData()
{
    QByteArray receive = tcpSocket->readAll();

    QTime time = QTime::currentTime();
    QString str = time.toString("H:mm:ss");

    int id  = (int)QThread::currentThreadId();
    str_id = QString::number(id,10);
    byte = str_id + tr("客户端") +" " + str +" "+ tr("售出")+ receive ;

    emit transsell(receive,byte,str_id);
}
//mainwindow.h 主界面窗口
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QWidget>
#include <QLabel>
#include <QString>
#include "server.h"
#include "client.h"

class Mainwindow: public QWidget
{
    Q_OBJECT

public:
    Mainwindow(QWidget *parent = 0);
    QLabel* _lable;


public slots:
   void slotserBtnClick();//服务器登陆按钮
   void slotcliBtnClick();//客户端登陆按钮
private:
};

#endif // MAINWINDOW_H
//mainwindow.cpp
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QGridLayout>
#include <QPushButton>
#include <QFont>

Mainwindow::Mainwindow(QWidget *parent) :
    QWidget(parent)
{
    setWindowTitle(tr("欢迎进入火车票订票系统"));
    setMinimumSize(250,150);
    setMaximumSize(250,150);

    QFont font("楷体",20);
    _lable = new QLabel(this);
    _lable->setText("火车票订票系统");
    _lable->setFont(font);
    QPushButton* serBtn = new QPushButton("服务器登陆");
    QPushButton* cliBtn = new QPushButton("客户端登陆");

    connect(serBtn, SIGNAL(clicked()), this, SLOT(slotserBtnClick()));
    connect(cliBtn, SIGNAL(clicked()), this, SLOT(slotcliBtnClick()));   

    QGridLayout* glay = new QGridLayout(this);

    glay->addWidget(_lable,0,0,2,2);
    glay->addWidget(serBtn,2,0,2,1);
    glay->addWidget(cliBtn,2,1,2,1);

}
//服务器登陆按钮
void Mainwindow::slotserBtnClick()
{
    Server *server = new Server();
    server->show();
    server->setWindowTitle("服务器");
    server->setMinimumSize(500,500);
    server->setMaximumSize(500,500);
}
//客户端登录按钮
void Mainwindow::slotcliBtnClick()
{
    Client *client = new Client();
    client->show();
    client->setWindowTitle("客户端");
    client->setMinimumSize(500,500);
    client->setMaximumSize(500,500);
}
//main.cpp
#include <QApplication>
#include "mainwindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    Mainwindow* mainwindow = new Mainwindow;
    mainwindow->show();


    return a.exec();
}

主界面
服务器
客户端

猜你喜欢

转载自blog.csdn.net/fan_xingwang/article/details/77045411