上一篇博客我们通过boost.asio搭建了一个简单的异步服务器,但是那是基于命令行的,所有用起来还是相当枯燥的,这次我们配合Qt实现一个简陋的前端页面来控制后端mysql数据库中的表,实现添加密钥的功能(本次博客使用的boost版本是1.84.0,mysql版本是8,Qt版本是6,并且本次博客主要展示代码和效果,不做代码讲解,因为本次博客所使用的代码都是之前博客中所使用过的,大家可以去我的主页学习)。
服务器端
main.cpp
#include <QtWidgets/QApplication>
#include<QtSql/QSqlDatabase>
#include<QtSql/QSqlError>
#include<QtSql/QSqlQuery>
#include<random>
#include<iostream>
#include"Server.h"
int main(int argc, char *argv[])
{
QSqlDatabase db = QSqlDatabase::addDatabase("QODBC");
try
{
db.setPort(3306);
db.setHostName("localhost");
db.setDatabaseName("booksystem");
db.setUserName("root");
db.setPassword("2145");
if (!db.open())
{
throw QSqlError("连接失败:", db.lastError().text());
}
}
catch (const QSqlError &e)
{
qDebug() << "数据库连接出错了:" << e.text();
}
boost::asio::io_context ioContext;
Server s(ioContext, 10240);
ioContext.run();
return 0;
}
Server.h
#pragma once
#include"Session.h"
class Server
{
public:
Server(boost::asio::io_context& ioc, int port);
void start_accept();
void handle_accept(Session* s,const boost::system::error_code& e);
private:
boost::asio::io_context &ioc;
boost::asio::ip::tcp::acceptor act;
};
Server.cpp
#include"Server.h"
Server::Server(boost::asio::io_context& ioc, int port) :ioc(ioc), act(ioc,boost::asio::ip::tcp::endpoint(boost::asio::ip::address_v4::any(), port))
{
start_accept();
}
void Server::start_accept()
{
Session* s = new Session(ioc);
act.async_accept(s->get_socket(), std::bind(&Server::handle_accept, this, s, std::placeholders::_1));
}
void Server::handle_accept(Session* s, const boost::system::error_code& e)
{
if (!e)
{
s->Start();
}
else
{
delete s;
}
start_accept();
}
Session.h
#pragma once
#include<boost/asio.hpp>
class Session
{
public:
Session(boost::asio::io_context& ioc);
void Start();
void handle_recive(const::boost::system::error_code &e, int recived_len);
void handle_send(const::boost::system:: error_code &e);
boost::asio::ip::tcp::socket &get_socket();
private:
boost::asio::ip::tcp::socket soc;
char data[1024];
int max_len = 1024;
};
Session.cpp
#include"Session.h"
#include<iostream>
#include<QtSql/QSqlQuery>
#include<QtSql/QSqlError>
Session::Session(boost::asio::io_context& ioc) :soc(ioc)
{
}
boost::asio::ip::tcp::socket & Session::get_socket()
{
return this->soc;
}
void Session::Start()
{
memset(data, '\0', max_len);
soc.async_receive(boost::asio::buffer(data, max_len), std::bind(&Session::handle_recive, this, std::placeholders::_1, std::placeholders::_2));
}
void Session::handle_recive(const::boost::system::error_code& e, int recived_len)
{
if (!e)
{
std::cout << "recived:" << data<<std::endl;
QSqlQuery q;
QString sql = "insert into booksystem.secret (secrets)values(?)";
q.prepare(sql);
q.addBindValue(QString(data));
if (!q.exec())
{
std::cout << "添加失败" << q.lastError().text().toStdString() << std::endl;
}
q.clear();//这个必须要放在async_send函数的前面,不然handle_recive函数结束的时候就会出错
soc.async_send(boost::asio::buffer(data,max_len), std::bind(&Session::handle_send, this, std::placeholders::_1));
}
else
{
delete this;
}
}
void Session::handle_send(const::boost::system::error_code& e)
{
if (!e)
{
memset(data, '\0', max_len);
soc.async_receive(boost::asio::buffer(data, max_len), std::bind(&Session::handle_recive, this, std::placeholders::_1, std::placeholders::_2));
}
else
{
delete this;
}
}
客户端
由于我们是使用VS2022作为开发平台所有并没有使用Qt Creator会使用的qmake,因此也没有对应的.pro文件,这样的好处就是,我们不必在.pro文件中配置项目的文件和第三方库了。
main.cpp
#include "QtClient.h"
#include <QtWidgets/QApplication>
#include<boost/asio.hpp>
#include<iostream>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
boost::asio::io_context ioContext;
QtClient w(a.activeWindow(), &ioContext);
try
{
// 创建socket对象并连接到服务器
boost::asio::ip::tcp::socket socket(ioContext);
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 10240);
socket.connect(endpoint);
}
catch (boost::system::system_error& e)
{
std::cout<< "Exception: " << e.what() << std::endl;
}
w.show();
return a.exec();
}
QtClient.h
#pragma once
#include <QtWidgets/QWidget>
#include "ui_QtClient.h"
#include<boost/asio.hpp>
class QtClient : public QWidget
{
Q_OBJECT
public:
QtClient(QWidget *parent = nullptr,boost::asio::io_context *ioc=nullptr);
~QtClient();
private slots:
void add_button_click();
private:
Ui::QtClientClass ui;
boost::asio::ip::tcp::socket soc;
};
QtClient.cpp
#include "QtClient.h"
#include<iostream>
QtClient::QtClient(QWidget *parent,boost::asio::io_context *ioc):soc(*ioc),QWidget(parent)
{
ui.setupUi(this);
ui.secretText->setAlignment(Qt::AlignCenter);
soc.connect(boost::asio::ip::tcp::endpoint(boost::asio::ip::address::from_string("127.0.0.1"), 10240));
connect(ui.addButton, &QPushButton::clicked, this, &QtClient::add_button_click);
}
QtClient::~QtClient()
{}
void QtClient::add_button_click()
{
std::cout << "secret get: " << ui.secretText->text().toUtf8().toStdString()<< std::endl;
QByteArray d = ui.secretText->text().toUtf8();
char data[1024] = "";
qstrcpy(data, d.data());
soc.send(boost::asio::buffer(data));
char rec[1024] = "";
soc.receive(boost::asio::buffer(rec, 1024));
std::cout << "reply:" << rec << std::endl;
}
ui_QtClient.h
/********************************************************************************
** Form generated from reading UI file 'QtClient.ui'
**
** Created by: Qt User Interface Compiler version 6.6.2
**
** WARNING! All changes made in this file will be lost when recompiling UI file!
********************************************************************************/
#ifndef UI_QTCLIENT_H
#define UI_QTCLIENT_H
#include <QtCore/QVariant>
#include <QtWidgets/QApplication>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QWidget>
QT_BEGIN_NAMESPACE
class Ui_QtClientClass
{
public:
QPushButton *addButton;
QLineEdit *secretText;
void setupUi(QWidget *QtClientClass)
{
if (QtClientClass->objectName().isEmpty())
QtClientClass->setObjectName("QtClientClass");
QtClientClass->resize(600, 400);
QtClientClass->setStyleSheet(QString::fromUtf8("background-color: rgb(255, 170, 255);"));
addButton = new QPushButton(QtClientClass);
addButton->setObjectName("addButton");
addButton->setGeometry(QRect(220, 220, 151, 51));
addButton->setStyleSheet(QString::fromUtf8("background-color: rgb(85, 255, 255);\n"
"font: 12pt \"Microsoft YaHei UI\";\n"
"border: 3 solid rgb(0, 0, 255);"));
secretText = new QLineEdit(QtClientClass);
secretText->setObjectName("secretText");
secretText->setGeometry(QRect(80, 110, 441, 71));
secretText->setStyleSheet(QString::fromUtf8("font: 16pt \"Microsoft YaHei UI\";\n"
"background-color: rgb(0, 255, 127);\n"
"border:3 solid rgb(0, 170, 0);"));
retranslateUi(QtClientClass);
QMetaObject::connectSlotsByName(QtClientClass);
} // setupUi
void retranslateUi(QWidget *QtClientClass)
{
QtClientClass->setWindowTitle(QCoreApplication::translate("QtClientClass", "QtClient", nullptr));
addButton->setText(QCoreApplication::translate("QtClientClass", "\346\267\273\345\212\240\345\257\206\351\222\245", nullptr));
secretText->setPlaceholderText(QCoreApplication::translate("QtClientClass", "\350\257\267\350\276\223\345\205\245\345\257\206\351\222\245", nullptr));
} // retranslateUi
};
namespace Ui {
class QtClientClass: public Ui_QtClientClass {};
} // namespace Ui
QT_END_NAMESPACE
#endif // UI_QTCLIENT_H
运行效果
显然左边的窗口是服务器窗口,右边两个窗口是客户端的界面窗口和控制台窗口,有的人因为项目属性配置的问题,可能只有界面窗口,不过没什么影响啦。
输入我们要添加的密钥后,点击添加按钮,发现服务器成功添加了密钥,并且向客户端发送了响应,并且客户端也接收到了响应。
打开mysql里面的secret表,发现我们的密钥添加进去了。