相关知识:QT,数据库,TCP/IP,Socket;
1.登陆界面
包含登陆和注册两种功能,思路如下:
难点:建立服务器和数据库,数据库保存数据,服务器与数据库产生联系
解决:数据库与服务器放在同一个类中,登陆和注册时,客户端与服务端连接,传输数据给服务端,然后通过数据库来处理数据。
大致步骤:创建ui界面 => {connect(登陆按钮,客户端主动连接服务器),connect(服务器得到新连接,connect(客户端readReady,服务器读取数据))}
=> 客户端写入数据 ->服务器读取数据 => 使用数据库 => 根据数据输出信息/注册账号。
2.实现聊天
如下为基本功能和实现思路:
参考博文:
- https://blog.csdn.net/weixin_40011728/article/details/77924196
- https://www.cnblogs.com/yuweifeng/p/9382841.html
- https://www.cnblogs.com/lifexy/p/10921958.html
上述博文的内容比我的硬核多了…
新发现一个难点:一个服务器连接多个客户端时,要想清楚怎么去确定不同的客户端,之前的错误代码使得一个客户端登陆,结果包括没连接的客户端都登陆了。
效果展示:
数据库展示:
可以说就实现了正常软件基本功能中的一小部分,后续检查发
现注册信息为空没有设置…
如果要优化界面应该不会比做这些功能难,拿QPaint画画,搞些和文件相关的连接应该就可以。
上面两条应该就是体力活了,但是聊天软件本来应该像QQ一样,最起码能加好友,但是我目前没有想好怎么做,这类知识全是第一次接触,可以说大半部分照着参考博文抄来的,查了很多资料去了解各种函数,有时候数据库的操作失败也是莫名其妙的,再继续下去不如系统地学点知识再来做一回。
全部代码如下:
mylogin.h
#ifndef MYLOGIN_H
#define MYLOGIN_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QMessageBox>
#include "myregister.h"
#include "server_sqlite.h"
namespace Ui {
class MyLogin;
}
class MyLogin : public QWidget
{
Q_OBJECT
public:
explicit MyLogin(QWidget *parent = nullptr);
~MyLogin();
void clientSend(QString message);
signals:
void toSignUp();
void sendContent(QString content);
void createUi(QString);
void refreshList(QString);
private slots:
void signIn();
void clientRead();
void updateSign();
private:
Ui::MyLogin *ui;
QTcpSocket *clientTcp;
};
#endif // MYLOGIN_H
mylogin.cpp
#include "mylogin.h"
#include "ui_mylogin.h"
MyLogin::MyLogin(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyLogin)
{
ui->setupUi(this);
connect(ui->pushButton_Login,&QPushButton::clicked,this,&MyLogin::signIn);
connect(ui->pushButton_Register,&QPushButton::clicked,this,&MyLogin::toSignUp);
}
MyLogin::~MyLogin()
{
clientTcp->close();
delete ui;
}
void MyLogin::signIn(){
clientTcp = new QTcpSocket(this);
clientTcp->connectToHost("127.0.0.1",8888);
if(!clientTcp->waitForConnected(20000)){
QMessageBox::warning(this,"错误","连接超时");
return;
}
// qDebug()<<"连接服务器成功!";
QString name = ui->lineEdit_Name->text();
QString password = ui->lineEdit_Password->text();
if(""==name||""==password){
QMessageBox::information(this,"提示","请输入完整登陆信息!");
return;
}
QString tmp = name+";"+password;
// qDebug()<<"tmp:"<<tmp;
clientTcp->write(tmp.toUtf8());//将完整信息写入传递给服务器
connect(clientTcp,&QTcpSocket::readyRead,this,&MyLogin::updateSign);
// //登陆成功后连接聊天的信号
// connect(clientTcp,&QTcpSocket::readyRead,[=](){
// emit
// });
// //connect(clientTcp,&QTcpSocket::readyRead,this,&MyLogin::clientRead);
}
void MyLogin::clientSend(QString message){
//qDebug()<<"clientSend:"<<message;
clientTcp->write(message.toUtf8());
}
void MyLogin::clientRead(){
QTcpSocket *client = qobject_cast<QTcpSocket *>(sender());
QString content = client->readAll();
QStringList analysis = content.split("|");
if("refreshFriendList"==analysis.at(0)){
emit refreshList(analysis.at(1));
return;
}
//qDebug()<<"emit:"<<content;
emit sendContent(content);
}
void MyLogin::updateSign(){
disconnect(clientTcp,&QTcpSocket::readyRead,this,&MyLogin::updateSign);
connect(clientTcp,&QTcpSocket::readyRead,this,&MyLogin::clientRead);
QString tmp = clientTcp->readAll();
QStringList analysis = tmp.split("|");
emit createUi(analysis.at(1));
}
myregister.h
#ifndef MYREGISTER_H
#define MYREGISTER_H
#include <QWidget>
#include <QTcpSocket>
#include <QTcpServer>
#include <QMessageBox>
#include <QDebug>
namespace Ui {
class MyRegister;
}
class MyRegister : public QWidget
{
Q_OBJECT
public:
explicit MyRegister(QWidget *parent = nullptr);
~MyRegister();
private slots:
void signUp();
private:
Ui::MyRegister *ui;
QTcpSocket *myReg;
};
#endif // MYREGISTER_H
myregister.cpp
#include "myregister.h"
#include "ui_myregister.h"
MyRegister::MyRegister(QWidget *parent) :
QWidget(parent),
ui(new Ui::MyRegister)
{
ui->setupUi(this);
connect(ui->pushButton_Sure,&QPushButton::clicked,this,&MyRegister::signUp);
}
MyRegister::~MyRegister()
{
myReg->close();
delete ui;
}
void MyRegister::signUp(){
myReg = new QTcpSocket;
myReg->connectToHost("127.0.0.1",8888);
if(!myReg->waitForConnected(20000)){
QMessageBox::warning(this,"错误","连接超时");
return;
}
qDebug()<<"连接服务器成功!";
QString name = ui->lineEdit_Name->text();
QString pas1 = ui->lineEdit_Password->text();
QString pas2 = ui->lineEdit_PasswordAgain->text();
if(pas1 != pas2){
QMessageBox::warning(this,"警告","两次输入密码不一致!");
return;
}
QString temp = name+";"+pas1+";0";
myReg->write(temp.toUtf8());
}
server_sqlite.h //服务器&数据库
#ifndef SERVER_SQLITE_H
#define SERVER_SQLITE_H
#include <QSqlDatabase>
#include <QSqlRecord>
#include <QSqlQuery>
#include <QSqlError>
#include <QTcpServer>
#include <QTcpSocket>
#include <QMessageBox>
#include <QApplication>
#include <QObject>
#include <QDebug>
class Server_Sqlite:public QObject
{
Q_OBJECT
public:
Server_Sqlite();
~Server_Sqlite();
void dataInit();
void getChartRecord();
QList<QTcpSocket *>onlineClients;//在线用户表
QList<QString>onlineName;//在线用户昵称
private slots:
void newClient();
void serverRead();
void serverDisconnect();
void sendMessage();
private:
QSqlDatabase db;
QTcpServer *myServer;
};
#endif // SERVER_SQLITE_H
server_sqlite.cpp
#include "server_sqlite.h"
Server_Sqlite::Server_Sqlite()
{
myServer = new QTcpServer;
//监听,端口号:8888
bool isOk = myServer->listen(QHostAddress::Any,8888);
//监听失败
if(false == isOk){
QMessageBox::warning(NULL,"监听","8888监听失败!");
return;
}else{
qDebug()<<"监听成功!";
}
//当有客户端连接时候,触发信号:newConnection()
connect(myServer,&QTcpServer::newConnection,this,&Server_Sqlite::newClient);
}
Server_Sqlite::~Server_Sqlite(){
db.close();
myServer->close();
}
void Server_Sqlite::dataInit(){
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName(QApplication::applicationDirPath()+"/scooters.dat"); //在本目录下生成
db.open();
QSqlQuery query;
// query.exec("DROP TABLE clients"); //先清空一下表
// query.exec("DROP TABLE chartRecord");
if(!query.exec("CREATE TABLE clients ("
"name VARCHAR(40) NOT NULL, "
"password VARCHAR(40) NOT NULL,"
"score INTEGER NOT NULL)")){
qDebug()<<"clients已被创建或出错!";
}
//创建一个clients表
query.exec("CREATE TABLE chartRecord ("
"message VARCHAR(200) NOT NULL)");
}
void Server_Sqlite::newClient(){
QTcpSocket *clientPort;
clientPort = myServer->nextPendingConnection();
//与对应的客户端产生连接
connect(clientPort,&QTcpSocket::readyRead,this,&Server_Sqlite::serverRead);
connect(clientPort,&QTcpSocket::disconnected,this,&Server_Sqlite::serverDisconnect);
onlineClients.push_back(clientPort);
}
void Server_Sqlite::serverRead(){
QTcpSocket *clientPort =qobject_cast<QTcpSocket *>(sender());
bool flag = false;
QList<QTcpSocket *>::iterator it;
for(it = onlineClients.begin();it != onlineClients.end();it++){
if((*it) == clientPort){
flag = true; break;
}
}
if(!flag)return;
disconnect(clientPort,&QTcpSocket::readyRead,this,&Server_Sqlite::serverRead);
//获取数据,查询数据库
QString str = clientPort->readAll();
qDebug()<<"读入数据"<<str;
QStringList analysis = str.split(";");//str:name;password
QString tmp = QString("select * from clients where name = '%1'").arg(analysis.at(0));
qDebug()<<tmp;
qDebug()<<"length="<<analysis.length();
QSqlQuery query;
query.exec(tmp);
if(!query.next()){//在数据库中没找到该用户名
qDebug()<<"没找到!";
if(analysis.length() == 3){
query.prepare("INSERT INTO clients (name,password,score) "
"VALUES(:name, :password, :score)");
QString myName = analysis.at(0);
QString myPassword = analysis.at(1);
qDebug()<<myName<<myPassword;
query.prepare("INSERT INTO clients (name, password, score) "
"VALUES (:name, :password, :score)");
//为每一列标题添加绑定值
query.bindValue(":name", myName);
query.bindValue(":password", myPassword);
query.bindValue(":score", 0 );
query.exec(); //加入库中
QMessageBox::information(NULL,"提示","注册成功!");
return;
}
QMessageBox::information(NULL,"提示","该用户尚未注册!");
return;
}else{//找到该用户名
qDebug()<<"找到了!";
if(analysis.length() == 3){
QMessageBox::warning(NULL,"警告","该用户名已被注册!");
return;
}
if(analysis.at(1) != query.value(1)){//0:name 1:password 2:score
qDebug()<<query.value(1).toString();
QMessageBox::warning(NULL,"提示","密码输入错误!");
return;
}else{
int newScore =query.value(2).toInt()+1;
tmp = QString("update clients set score = %1 where name = '%2'").arg(newScore).arg(analysis.at(0));
query.exec(tmp);
}
}
// qDebug()<<"来新客人了";
onlineName.push_back(analysis.at(0));
tmp.clear();
QList<QString>::iterator it2;
for(it2 = onlineName.begin(); it2 != onlineName.end(); ++it2){
tmp+=";"+(*it2);
}
for(it = onlineClients.begin();it != onlineClients.end();it++){
(*it)->write("refreshFriendList|"+tmp.toUtf8());
}
connect(clientPort,&QTcpSocket::readyRead,this,&Server_Sqlite::sendMessage);
}
void Server_Sqlite::sendMessage(){
//哪一个QTcpSocketc对象可读就会发出readyRead()信号,通过信号的发出者找到相应的对象
QTcpSocket *client = qobject_cast<QTcpSocket *>(sender());
QString str = client->readAll();
//群发
QList<QTcpSocket *>::iterator it;
for(it = onlineClients.begin();it != onlineClients.end();it++){
qDebug()<<"群发:"<<str;
(*it)->write(str.toUtf8());
}
QSqlQuery query;
QString tmp = QString("insert into chartRecord (message) values ('%1')").arg(str);
query.exec(tmp);
}
void Server_Sqlite::serverDisconnect(){//断开连接删去该用户
QTcpSocket *client = qobject_cast<QTcpSocket *>(sender());
onlineClients.removeOne(client);
qDebug()<<"断开连接";
}
widgt.h//聊天窗口
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QObject>
//主窗口用户列表使用
#include <QTextBrowser>
#include <QLabel>
#include "server_sqlite.h"
#include "mylogin.h"
#include "myregister.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void mainCreadUi(QString name);
void refreshChart(QString content);
void refreshList(QString names);
private:
Ui::Widget *ui;
QTextBrowser *xin;
QLabel *record;
Server_Sqlite *serSql;
MyLogin *log;
MyRegister *reg;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
// serSql = new Server_Sqlite;
// serSql->dataInit();
this->setWindowTitle("群聊窗口");
log = new MyLogin;
connect(log,SIGNAL(createUi(QString)),this,SLOT(mainCreadUi(QString)));
connect(log,&MyLogin::toSignUp,[=](){
reg = new MyRegister;
reg->show();
});
//发送消息写入聊天框
connect(ui->pushButton_Send,&QPushButton::clicked,[=](){
QString fasoner = ui->label_Name->text();
QString message = ui->textEdit_Send->toPlainText();
qDebug()<<fasoner<<message;
log->clientSend(fasoner+":\n"+message);
});
connect(log,&MyLogin::sendContent,this,&Widget::refreshChart);
//查看历史记录
record = new QLabel(this);
record->setText("消息记录:");
record->move(565,40);
xin = new QTextBrowser(this);
xin->move(565,60);
xin->setFixedSize(325,330);
connect(ui->pushButton_ChartRecord,&QPushButton::clicked,[=](){
if(ui->pushButton_ChartRecord->text()!="关闭记录"){
this->setFixedSize(900,405);
record->show();
xin->show();
ui->pushButton_ChartRecord->setText("关闭记录");
qDebug()<<"数据库数据:";
QSqlQuery query;
query.exec("select * from chartRecord");
while(query.next()){
QString message = query.value(0).toString();
xin->append(message);
}
}else{
ui->pushButton_ChartRecord->setText("历史记录");
record->close();
xin->close();
this->setFixedSize(565,405);
}
});
//更新在线列表
connect(log,SIGNAL(refreshList(QString)),this,SLOT(refreshList(QString)));
log->show();
}
Widget::~Widget()
{
qDebug()<<"数据库数据:";
QSqlQuery query;
query.exec("select * from chartRecord");
while(query.next()){
QString message = query.value(0).toString();
qDebug()<<message;
}
delete ui;
}
void Widget::mainCreadUi(QString names){
ui->textEdit_Friends->setReadOnly(true);
QStringList analysis = names.split(";");
ui->label_Name->setText(analysis.back());
for(int i=0;i<analysis.length();i++){
QString tmp = QString("select * from clients where name = '%1'").arg(analysis.at(i));
int score;
QSqlQuery query;
query.exec(tmp);
while(query.next()){
score = query.value(2).toInt();//name;passowrd;score
tmp.sprintf("%-15s%-5d",analysis.at(i).toStdString().data(),score);
ui->textEdit_Friends->append(QString(tmp));
}
}
QString name = analysis.back();
this->show();
log->close();
}
void Widget::refreshChart(QString content){
ui->textEdit_Send->clear();
ui->textBrowser_Chart->append(content);
}
void Widget::refreshList(QString names){
ui->textEdit_Friends->clear();
QStringList analysis = names.split(";");
for(int i=0;i<analysis.length();i++){
QString tmp = QString("select * from clients where name = '%1'").arg(analysis.at(i));
int score;
QSqlQuery query;
query.exec(tmp);
while(query.next()){
score = query.value(2).toInt();//name;passowrd;score
tmp.sprintf("%-15s%-5d",analysis.at(i).toStdString().data(),score);
ui->textEdit_Friends->append(QString(tmp));
}
}
}
最后是main.cpp
#include "widget.h"
#include "server_sqlite.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Server_Sqlite tmp;
tmp.dataInit();
Widget w,w2,w3;
//w.show();
return a.exec();
}
转载:https://blog.csdn.net/TK_wang_/article/details/106617083