基于QT+TCP+数据库的聊天室

代码链接:https://download.csdn.net/download/qq_53198674/88549159?spm=1001.2014.3001.5503

本次是基于Qt Creater开发的一个聊天室,因为之前用C语言写过一个聊天室,但是没有界面,就像是一个毛坯房,于是在学习了一段时间的Qt之后发现,Qt也同样能开发服务器和客户端,于是就像补一下前面的坑。

下面是展示环节👇

客户端:

服务器:

怎么样,有没有一种给毛坯房贴上瓷砖的感觉呢?

言归正传:这次的聊天室主要实现了:发消息、查看聊天记录、广播等功能(在此为喜欢二次开发的小伙伴提供思路:发送表情包、进行文件传输),运行后需选择IP以及端口号和用户名,以便区分发送者(这里附上聊天截图)

 连接前是无法点击发送和消息记录按钮的,连接后无法更改IP地址以及端口和用户名,同时可以点击发送和消息记录。而连接后服务器会提示新连接已建立,当用户断开后同样会提示已断开(这里附上图片)

连接后会自动创建库和建表(即数据库的创建操作和建表操作)每发送一条消息就会在本地数据库中进行记录(即数据库的插入操作),同时可根据下拉框选择查询方式(模糊查询)

可根据三种情况进行记录的查询

这里附上主要代码:

服务器:

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

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    setWindowFlags(Qt::WindowStaysOnTopHint);

    // 创建服务器对象
    server = new QTcpServer(this);
    // 开启监听服务
    bool result = server->listen(QHostAddress::Any,8887);
    if(result == false)
    {
        QMessageBox::critical(this,"错误",server->errorString());
        return;
    }
    else
    {
        printMessage("服务器开始监听,端口号8887");
        connect(server,SIGNAL(newConnection()),
                this,SLOT(newConnectionSlot()));
    }
}

Dialog::~Dialog()
{
    // 如果监听服务正在开启中,则关闭
    if(server->isListening())
        server->close();
    delete ui;
}

void Dialog::printMessage(QString msg)
{
    QString dt = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
    ui->textBrowser->append(dt);
    ui->textBrowser->append(msg);
    ui->textBrowser->append("");
}

void Dialog::newConnectionSlot()
{
    // 获得服务器端的连接类对象
    socket = server->nextPendingConnection();
    QTextStream output(socket);
    if(list.size() == 20)
    {
        qDebug() << "聊天室满员";
        output << QString("聊天室已达人数上限,连接失败!");
        socket->close();
        return;
    }
    list.append(socket);
    // 断开连接检测的信号槽
    connect(socket,SIGNAL(disconnected()),
            this,SLOT(disconnectedSlot()));
    // 接受消息信号槽的连接
    connect(socket,SIGNAL(readyRead()),
            this,SLOT(readyReadSlot()));

    // Qt称呼对面的绿蛋为peer(伙伴)
    // 获得peer得IP地址和Port
    QString ip = socket->peerAddress().toString();
    quint16 port = socket->peerPort();
    QString text = "新连接已建立 ";
    text = text.append('\n').append("IP:") + ip.append("  Port:") + QString::number(port);
    printMessage(text);
}

void Dialog::disconnectedSlot()
{
    // 拿到发射者
    socket = (QTcpSocket*)sender();

    for(int i = 0;i<list.size();i++)
    {
        if(socket == list.at(i))
        {
            // 获得peer得IP地址和Port
            QString ip = socket->peerAddress().toString();
            quint16 port = socket->peerPort();
            QString text = "连接已断开 ";
            text = text.append('\n').append("IP:") + ip.append("  Port:") + QString::number(port);
            printMessage(text);
        }
    }
}

void Dialog::readyReadSlot()
{


    for(int i=0;i<list.size();i++)
    {
        if(list.at(i)->isReadable() && list.at(i)->bytesAvailable()>0)
        {
            qDebug() << "第" << i << "个客户端发的消息";
            // 方法一:读取所有内容
            QByteArray buffer = list.at(i)->readAll();

            for(int m=0;m<list.size();m++)
            {
                // 转发
                list.at(m)->write(buffer);
            }
        }
    }
}

客户端:

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

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);

    // 创建播放类对象
    movie = new QMovie(":/new/prefix1/res/yes (1).gif");
    // 给组件设置播放内容
    ui->label_5->setMovie(movie);
    // 开始播放
    movie->start();

    ui->pushButtonSend->setEnabled(false);
    ui->pushButtonHistory->setEnabled(false);

    connect(ui->pushButtonHistory,SIGNAL(clicked()),
            this,SLOT(openHistorySlot()));

    connect(ui->pushButtonConn,SIGNAL(clicked()),
            this,SLOT(btnConnClickedSlot()));
    connect(ui->pushButtonSend,SIGNAL(clicked()),
            this,SLOT(btnSendClickedSlot()));

    client = new QTcpSocket(this);
    // 连接状态信号槽
    connect(client,SIGNAL(connected()),
            this,SLOT(connectedSlot()));
    connect(client,SIGNAL(disconnected()),
            this,SLOT(disconnectedSlot()));

    connectToDB();
}

Dialog::~Dialog()
{
    // 解除信号槽的连接
    disconnect(client,SIGNAL(disconnected()),
               this,SLOT(disconnectedSlot()));
    // 如果连接则断开
    if(client->isOpen())
        client->close();
    delete ui;
}

void Dialog::btnConnClickedSlot()
{
    // 先获得用户输入的IP地址和端口号Port
    QString ip = ui->lineEditIP->text();
    int port = ui->spinBoxPort->value();
    name = ui->lineEditName->text();
    // TODO 验证IP有效性
    // 验证IP有效性
    QRegularExpression ipRegex("^((25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(25[0-5]|2[0-4]\\d|[01]?\\d\\d?)$");
    QRegularExpressionMatch match = ipRegex.match(ip);
    if (!match.hasMatch()) {
        QMessageBox::warning(this, "错误", "请输入有效的IP地址");
        return;
    }
    if(name == "")
    {
        QMessageBox::warning(this,"提示","请输入用户名!");
        return;
    }
    // 接受消息信号槽的连接
    connect(client,SIGNAL(readyRead()),
            this,SLOT(readyReadSlot()));

    // 连接到服务器
    client->connectToHost(ip,port);
}

void Dialog::btnSendClickedSlot()
{
    QString dt = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
    //获得用户输入的消息
    QString msg = ui->lineEditSend->text();
    msg = dt + '\n' + name + ":" + msg + '\n';
    if(msg == "")
    {
        QMessageBox::warning(this,"提示","请输入内容!");
        return;
    }

    // 方法一:使用QByteArray
    QByteArray buffer = msg.toUtf8();
    client->write(buffer);

    // 清空输入框
    ui->lineEditSend->clear();
}

void Dialog::connectedSlot()
{
    // 屏蔽按钮和输入
    ui->pushButtonConn->setEnabled(false);
    ui->lineEditIP->setEnabled(false);
    ui->lineEditName->setEnabled(false);
    ui->spinBoxPort->setEnabled(false);
    ui->pushButtonConn->setText("已连接");
    // 恢复发送按钮
    ui->pushButtonSend->setEnabled(true);
    ui->pushButtonHistory->setEnabled(true);
}

void Dialog::disconnectedSlot()
{
    // 恢复按钮和输入
    ui->pushButtonConn->setEnabled(true);
    ui->lineEditIP->setEnabled(true);
    ui->lineEditName->setEnabled(true);
    ui->spinBoxPort->setEnabled(true);
    ui->pushButtonConn->setText("连接");
    // 屏蔽发送按钮
    ui->pushButtonSend->setEnabled(false);
    ui->pushButtonHistory->setEnabled(false);
    // 弹窗提示
    QMessageBox::warning(this,"提示",client->errorString());
}

void Dialog::readyReadSlot()
{
    // 方法一:读取所有内容
    QByteArray buf = client->readAll();
    QString text(buf); // 转换为字符串

    qDebug() << text;

    QStringList parts = text.split('\n');

    if (parts.size() >= 2) {
        QString datetime = parts[0];   // 第一部分:日期+时间
        QString nameAndMessage = parts[1];   // 第三部分:姓名和消息

        // 进一步拆分姓名和消息
        QStringList nameAndMessageParts = nameAndMessage.split(":");
        if (nameAndMessageParts.size() >= 2) {
            QString name = nameAndMessageParts[0];   // 姓名
            QString message = nameAndMessageParts[1];   // 消息

            // 存入数据库-------------------
            // 预处理的SQL语句
            QString sql = "INSERT INTO group_history VALUES(?,?,?);";
            // 先预处理SQL语句
            QSqlQuery sq;
            sq.prepare(sql);
            // 绑定参数
            sq.addBindValue(datetime);
            sq.addBindValue(name);
            sq.addBindValue(message);
            // 执行SQL语句
            if(!sq.exec())
            {
                // 获得错误信息对象
                QSqlError info = sq.lastError();
                // 获得错误信息文本
                QString text = info.text();
                QMessageBox::warning(this,"通知",text);
            }
        }
    }
    // 输出
    ui->textBrowser->append(text);
}

void Dialog::openHistorySlot()
{
    // 创建MyDialog对象
    d = new History(this);
    d->show();
    // 屏蔽按钮
    //    ui->pushButtonHistory->setEnabled(false);
}


void Dialog::connectToDB()
{
    // 获得数据库连接对象
    db = QSqlDatabase::addDatabase("QSQLITE");
    // 设置数据库名称
    db.setDatabaseName("chat_history.db");
    // 打开数据库连接
    if(db.open())
    {
        qDebug() << "数据库连接成功";
        createTable();
    }
    else
    {
        // 获得错误信息对象
        QSqlError info = db.lastError();
        // 获得错误信息文本
        QString text = info.text();
        // 展示
        QMessageBox::critical(this,"错误",text);
    }
}

void Dialog::createTable()
{
    QString sql = QString("CREATE TABLE group_history(\
                          time TEXT,\
                          name TEXT,\
                          message TEXT\
                          );");
    // 数据库操作类对象
    QSqlQuery sq;
    // 执行SQL语句
    if(sq.exec(sql))
    {
        qDebug() << "创建数据表成功";
    }
    else
    {
        // 获得错误信息对象
        QSqlError info = sq.lastError();
        // 获得错误信息文本
        QString text = info.text();
        qDebug() << "建表失败" << text;
    }
}

消息记录:

#include "history.h"
#include "ui_history.h"

History::History(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::History)
{
    ui->setupUi(this);

    connect(ui->btnSelect,SIGNAL(clicked()),
            this,SLOT(btnSelectSlot()));
}

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

void History::selectAll()
{
    QString sql = "SELECT * FROM group_history";
    QSqlQuery sq;
    if(sq.exec(sql))
    {
        // 清空上次显示
        ui->textBrowser->clear();
        while (sq.next()) // 向后移动
        {
            QString time = sq.value("time").toString(); // 按照序号取出
            QString name = sq.value("name").toString(); // 按照名字取出
            QString message = sq.value("message").toString();
            // 组装
            QString space = "  ";
            QString record = time + space + name + space + message;
            // 展示
            ui->textBrowser->append(record);
        }
    }
    else
    {
        // 获得错误信息对象
        QSqlError info = sq.lastError();
        // 获得错误信息文本
        QString text = info.text();
        QMessageBox::warning(this,"通知",text);
    }
}

void History::selectPrecise()
{
    if(ui->comboBox->currentText()== "全部")
    {
        selectAll();
    }
    else if(ui->comboBox->currentText()== "关键字")
    {
        QString word = ui->lineEdit->text();
        if(word == "")
        {
            QMessageBox::warning(this,"提示","请输入查询内容!");
            return;
        }
        QString sql = "SELECT * FROM group_history WHERE message LIKE ?";
        QSqlQuery sq;
        sq.prepare(sql);
        sq.addBindValue(word.prepend("%").append("%")); // 前后加通配符
        if(sq.exec())
        {
            // 清空上次显示
            ui->textBrowser->clear();
            while(sq.next()) // 向后移动
            {
                QString time = sq.value("time").toString(); // 按照序号取出
                QString name = sq.value("name").toString(); // 按照名字取出
                QString message = sq.value("message").toString();
                // 组装
                QString space = "  ";
                QString record = time + space + name + space + message;
                // 展示
                ui->textBrowser->append(record);
            }
        }
        else
        {
            // 获得错误信息对象
            QSqlError info = sq.lastError();
            // 获得错误信息文本
            QString text = info.text();
            QMessageBox::warning(this,"通知",text);
        }
    }
    else if(ui->comboBox->currentText()== "日期")
    {
        QString dt = ui->lineEdit->text();
        if(dt == "")
        {
            QMessageBox::warning(this,"提示","请输入查询内容!");
            return;
        }
        QString sql = "SELECT * FROM group_history WHERE time LIKE ?";
        QSqlQuery sq;
        sq.prepare(sql);
        sq.addBindValue(dt.prepend("%").append("%")); // 前后加通配符
        if(sq.exec())
        {
            // 清空上次显示
            ui->textBrowser->clear();
            while(sq.next()) // 向后移动
            {
                QString time = sq.value("time").toString(); // 按照序号取出
                QString name = sq.value("name").toString(); // 按照名字取出
                QString message = sq.value("message").toString();
                // 组装
                QString space = "  ";
                QString record = time + space + name + space + message;
                // 展示
                ui->textBrowser->append(record);
            }
        }
        else
        {
            // 获得错误信息对象
            QSqlError info = sq.lastError();
            // 获得错误信息文本
            QString text = info.text();
            QMessageBox::warning(this,"通知",text);
        }
    }
}

void History::btnSelectSlot()
{
    selectPrecise();
}

主要用到的库:

#include <QTcpSocket> // 连接类
#include <QTextStream> // 文本流类
#include <QDateTime>    // 日期时间类
#include <QRegularExpression> // 正则表达式
#include <QMovie> //播放类
#include <QMessageBox>    //弹窗类
#include <QSqlDatabase> // 连接类
#include <QSqlQuery> // 操作类
#include <QSqlError> // 错误信息类
#include <QDebug>   // 输出类
#include <QTcpServer> // 服务器类

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值