qt实现聊天室

项目功能简介

1) 连接

客户端,需要先连接服务器。就是输入服务器端的IP和端口连接服务器,如果连接成功,连接按钮显示文字会显示已连接,颜色变浅。

2) 注册

接下来是注册,如果申请的用户名还有人用户注册,则可以注册成功,如果之前有用户注册过之后会显示注册失败。

3)登录

在接下来是登录,如果要登录的用户名还没有注册过,会显示未注册。如果密码错误会显示密码错误。如果换设备登录会显示新设备登录。如果用户名与密码都正确,则可以成功登录。

4)聊天

登录成功后就可以进入主界面。在主界面中,有三个功能区:用户管理、聊天、文本传输。
1)
用户管理功能区,可以更换昵称、更改头像、退出登录与注销账号
2)
聊天,可以群聊、私聊、群历史、私聊历史、删除私聊历史、显示好友列表、清屏
3)
文本传输,可以获取所有聊天室的用户,上传到服务器端的文件列表。还可以上传、下载文件。

1、项目运行展示

登录界面
在这里插入图片描述

聊天界面
在这里插入图片描述

1.1连接

在这里插入图片描述
在这里插入图片描述

1.2注册

在这里插入图片描述
在这里插入图片描述

1.3登录

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.4用户管理

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.5聊天

1)群聊
在这里插入图片描述
2)私聊
在这里插入图片描述
3)关闭历史记录显示(清屏)
在这里插入图片描述
4)群聊历史查询
在这里插入图片描述
5)私聊历史
在这里插入图片描述
6)清空私聊历史记录
在这里插入图片描述
7)我的好友
在这里插入图片描述

1.6文件传输

1)获取文件列表
在这里插入图片描述

2)上传文件

在这里插入图片描述

2、项目设计流程

服务器端一开始运行就会建立一个数据库文件。里面有四张表(注册表、登录表、群历史聊天记录表、私聊历史记录表)。

2.1 连接

客户端获取用户输入的IP和密码,建立一个连接对象,连接这个IP和端口的服务器。服务器端先是创建一个连接管理类对象,监听客户端连接。如果有客户端连接,创建一个唯一与之对应的连接对象。把他添加到容器中。

2.2 注册

客户端
获取用户输入的用户名与密码,拼接好后,创建一个文本流对象,发送给服务器。
服务器端
接受到客户端发来的消息,先判断是什么类型的消息,如果是注册消息,则调用对应的注册函数。在注册函数中会先调用查询注册表的函数,判断用户注册的用户名是否可以成功注册,如果可以,调用插入注册表的函数,将该用户注册的用户名与密码保存在注册表中,给客户端发送注册成功标志,如果错误,则发送注册失败标志。
客户端
接收服务器端发送过来的信息,判断是那一种标志,
如果是注册失败标志,则弹出“注册失败”对话框
如果是注册成功标志,则弹出“注册成功”对话框

2.3 登录

客户端
获取用户输入的用户名与密码,拼接好后,创建一个文本流对象,发送给服务器。
服务器端
接受到客户端发来的消息,先判断是什么类型的消息,如果是登录消息,则调用对应的登录函数。在登录函数中会先调用查询注册表的函数,判断用户登录的用户名是否已经注册。
1)如果没有注册过,则给客户端发送未注册标志。
2)如果该用户已经注册,但是密码不匹配,则给客户端发送密码错误标志
3)如果用户名与密码都正确,则给客户端发送登录成功标志。并去调用查登录表的函数,如果该用户第一次登录,需要把该用户的用户名、登录IP和端口插入到登录表中。如果已经登录过,则检测IP是否更换,未更换的话,给客户端发送成功登录的标志。如果更换则给它发送换设备标志,并更新登录表。
客户端
接收服务器端发送过来的信息,判断是那一种标志,
如果是未注册标志,则弹出“未注册”对话框
如果是是密码错误标志,则弹出“密码错误”对话框
如果是是登录成功标志,则弹出“登录成功”对话框,并跳转到主界面,关闭登录界面
如果是换设备标志,则弹出“换新设备登录”对话框,跳转到主界面,关闭登录界面

2.4 聊天

(1)群聊
客户端
获取用户输入消息内容,前面拼接上消息的类型,创建一个文本流对象,发送给服务器。
服务器端
接受到客户端发来的消息,先判断是什么类型的消息,如果是群聊消息,则调用对应的群聊函数。
将信息发送给其他客户端,并将信息加上用户名与时间存放到群聊历史记录表中。
客户端
接收服务器端发送过来的信息,将信息显示在聊天窗口

(2)群聊历史
客户端
将查询群聊历史的命令,创建一个文本流对象,发送给服务器。
服务器端
接受到客户端发来的消息,先判断是什么类型的消息,如果是查询群聊历史消息,则调用对应的查询群聊历史函数。在该函数中查询聊天记录,发送给客户端。
客户端
接收服务器端发送过来的信息,将信息显示在聊天窗口

(3)私聊
客户端
获取用户输入消息内容与接受此消息的用户,前面拼接上消息的类型,创建一个文本流对象,发送给服务器。
服务器端
接受到客户端发来的消息,先判断是什么类型的消息,如果是私聊消息,则调用对应的私聊函数。去查询注册表,如果有此用户,再去查询登录表,找到对应的IP和端口。通过迭代器指针遍历当前连接客户端对象容器,对比IP和端口,找到相同的客户端,将信息发送过去。并将信息加上用户名与时间存放到私聊历史记录表中。
客户端
接收服务器端发送过来的信息,将信息显示在聊天窗口

(4)查私聊历史
客户端
将查询私聊历史的命令,后面拼接上本用户的用户名,创建一个文本流对象,发送给服务器。
服务器端
接受到客户端发来的消息,先判断是什么类型的消息,如果是查询私聊消息,则调用对应的私聊函数。去查询私聊历史记录表,找到发给该用户的所有私聊信息,并将信息发送过去。
客户端
接收服务器端发送过来的信息,将信息显示在聊天窗口

(5)清空私聊历史
客户端
将查询私聊历史的命令,后面拼接上本用户的用户名,创建一个文本流对象,发送给服务器。
服务器端
接受到客户端发来的消息,先判断是什么类型的消息,如果是清空私聊历史消息,则调用对应的私聊函数。去删除指定用户的私聊历史记录表。

(6)查好友历史
客户端
将查询好友的命令,创建一个文本流对象,发送给服务器。
服务器端
接受到客户端发来的消息,先判断是什么类型的消息,如果是查询好友消息,则调用对应的查询好友函数。去查询注册表,将已注册的所有用户的用户名发送给客户端。
客户端
接收服务器端发送过来的信息,将信息显示在聊天窗口。

(7)关闭历史记录显示
则调用该组件的清空函数,清空显示。

2.5 文本传输

(1)获取文件列表
客户端
将查询文件的命令,后面拼接上,服务器给该聊天室创建的文件夹路径,创建一个文本流对象,发送给服务器。
服务器端
接受到客户端发来的消息,先判断是什么类型的消息,如果是查询文件列表消息,则调用对应的查询文件列表函数。去查指定路径下的文件夹信息,发送给客户端。
客户端
接收服务器端发送过来的信息,将信息显示在聊天窗口。

(2)上传文件
客户端
弹出打开文件的弹窗,在里面选择要发送的文件。
将上传的命令,后面拼接上该文件名,创建一个文本流对象,发送给服务器。接着创建一个线程对象,定义好进度条更新的信号与槽函数连接协议,调用start()函数,执行线程中run函数,发送文件内容给服务器。
服务器端
接受到客户端发来的消息,先判断是什么类型的消息,如果是上传文件消息,则调用对应的接受上传文件的函数。先接收客户端发送过来的文件名,创建一个线程对象,并通过构找函数传参的方式将文件名传给子线程,调用start()函数。执行子线程的run()函数。在run()函数中,先判断路径下有没有该文件,没有则创建。并将接受到的文件内容填充进去

3、完整代码

3.1客户端

1).pro文件
加上network网络通信模块

#-------------------------------------------------
#
# Project created by QtCreator 2023-07-20T14:39:05
#
#-------------------------------------------------

QT       += core gui network

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = UdpChatClient
TEMPLATE = app


SOURCES += main.cpp\
        dialog.cpp \
    mydialog.cpp \
    sendthread.cpp

HEADERS  += dialog.h \
    mydialog.h \
    sendthread.h

FORMS    += dialog.ui \
    mydialog.ui

RESOURCES += \
    res.qrc

OTHER_FILES += \
    iconfig.rc
RC_FILE += iconfig.rc



2)dialog.h文件

#ifndef DIALOG_H
#define DIALOG_H



#include<mydialog.h>

#include<QMessageBox>
#include<QDebug>

#include<QTcpSocket>
#include<QTextStream>


namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();

private:
    Ui::Dialog *ui;
    QTcpSocket *client;// 连接对象
    MyDialog *md;      //聊天窗口对象

private slots:
    void btnConnectClickedSlot();  //连接的槽函数
    void btnRegisterClickedSlot();  //注册的槽函数
    void btnLogonClickedSlot();     //登录的槽函数
    void conntedSlot();       //连接成功槽函数
    void disconntedSlot();//断开连接槽函数

    void readyreadSlot();//接受服务器端发送过来的标志

    void switchInterfaceSlot(); //切换回主界面
};

#endif // DIALOG_H

3)mydialog.h文件

#ifndef MYDIALOG_H
#define MYDIALOG_H

#include <QMessageBox>
#include<QDebug>

#include<QTcpSocket>
#include<QTextStream>

#include<QDir>
#include<QFileDialog>
#include<QFileInfo>
#include<QFile>
#include"sendthread.h"

namespace Ui {
class MyDialog;
}

class MyDialog : public QDialog
{
    Q_OBJECT

public:
    explicit MyDialog(QString name,QString ip,int port,QWidget *parent = 0);
    ~MyDialog();

private:
    Ui::MyDialog *ui;
    QString name;         //用户名
    QTcpSocket *client;    //连接对象
    QString cmd;    //命令

    QString readPath; //读路径

    QList<QString> list;//声明list容器对象成员变量
    QList<QString>::iterator iter;//声明迭代器指针

    QString ip;          //获取服务器的ip和端口
     int port;
     void connntPort();   //建立本进程与服务器端的连接
     void updataPort();    //更新端口
     void creatFolder();    //创建下载目录
private slots:
    void btnSendClickedSlot();//群发
    void btnHisQClickedSlot();//群历史聊天记录查询

    void btnSendUsrClickedSlot();//私发
    void btnHisSClickedSlot();//私聊自己的历史记录

    void btnSelectChatUsrClickedSlot();//查询聊天室的成员

    void btnNoLogClickedSlot();//退出登录
    void btnNoResClickedSlot();//注销账户
    void btnClearClickedSlot();//清空历史记录
    void btnCloseDpClieckedSlot();//关闭历史记录显示

    void btnGetFileListSlot();  //获取文件列表
    void btnSendFileSlot();     //上传文件
    void btnDownloadSlot();     //下载文件

    void mydialogReadyreadSlot();//接受消息

    void btnSetnameClickedSlot();//修改昵称
    void btnSelectPtoClickedSlot();//选择头像
    void btnSurePhoClickedSlot();   //确认头像


   // void sendValueSlot(int);   //更新上传文件进度条
    void sendValueSlot(int);
    void sendFinishSlot();


signals:
    void switchInterface();//切换回主界面信号



};

#endif // MYDIALOG_H

4)sendthread.h文件(上传文件子线程)

#ifndef SENDTHREAD_H
#define SENDTHREAD_H

#include <QThread>
#include<QFile>
#include<QTextStream>
#include<QByteArray>
#include<QTcpSocket>
#include<QDebug>

class SendThread : public QThread
{
    Q_OBJECT
public:
    explicit SendThread(QTcpSocket *client,QString readpth,QObject *parent = 0);
protected:
    void run();
private:
    QString readpth;
    QString ip;   //服务器端ip
    int port;
    QTcpSocket *client;
    void threadClickedSlot();

signals:
    void sendValueSignal(int);
    void sendFinish();

public slots:


};

#endif // SENDTHREAD_H

5)dialog.cpp文件

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

Dialog::Dialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Dialog)
{
    ui->setupUi(this);
    //连接、登录、注册按钮点击时触发的信号
    connect(ui->pushButtonConn,SIGNAL(clicked()),this,SLOT(btnConnectClickedSlot()));
    connect(ui->pushButtonRegister,SIGNAL(clicked()),this,SLOT(btnRegisterClickedSlot()));
    connect(ui->pushButtonLogon,SIGNAL(clicked()),this,SLOT(btnLogonClickedSlot()));

    //创建一个TCP连接类对象
    client =new QTcpSocket(this);
    //连接状态改变时触发的信号
    connect(client,SIGNAL(connected()),this,SLOT(conntedSlot()));
    connect(client,SIGNAL(disconnected()),this,SLOT(disconntedSlot()));

    //接受到服务器发送过来的数据触发
    connect(client,SIGNAL(readyRead()),this,SLOT(readyreadSlot()));

}

Dialog::~Dialog()
{
    disconnect(client,SIGNAL(disconnected()),this,SLOT(disconntedSlot()));
    if(client->isOpen())
        client->close();
    delete ui;
}
//连接槽函数
void Dialog::btnConnectClickedSlot()
{
    //获取用户输入的ip和端口
    QString ip=ui->lineEditIP->text();
    int port=ui->spinBox->value();

    ////为了解决第一次输入错误的IP和端口不能连接的问题
    //终止当前连接
    client->abort();
    //建立新的连接对象
    client =new QTcpSocket(this);

    ////新的连接对象也需要建立这些信号的约定
    //连接状态改变时触发的信号
    connect(client,SIGNAL(connected()),this,SLOT(conntedSlot()));
    connect(client,SIGNAL(disconnected()),this,SLOT(disconntedSlot()));

    //接受到服务器发送过来的数据触发
    connect(client,SIGNAL(readyRead()),this,SLOT(readyreadSlot()));

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

    //设置标题
    setWindowTitle("聊吧");


}
//成功连接
void Dialog::conntedSlot()
{
    //屏蔽连接按钮
    ui->pushButtonConn->setEnabled(false);
    //回复登录、注册按钮
    ui->pushButtonRegister->setEnabled(true);
    ui->pushButtonLogon->setEnabled(true);
    ui->pushButtonConn->setText("已连接");
}
//连接断开
void Dialog::disconntedSlot()
{
   //恢复连接按钮
    ui->pushButtonConn->setEnabled(true);
     //屏蔽登录、注册按钮
    ui->pushButtonRegister->setEnabled(false);
    ui->pushButtonLogon->setEnabled(false);
    ui->pushButtonConn->setText("连接");
    QMessageBox::warning(this,"提示","连接已断开");
}


//接受服务器端发送过来的标志,执行相应的逻辑处理
void Dialog::readyreadSlot()
{
    //获取用户输入的ip和端口
    QString ip=ui->lineEditIP->text();
    int port=ui->spinBox->value();

    QTextStream input(client);
    QString text=input.readLine(256);
    //qDebug()<<text;
    if(text=="register sucess")
        QMessageBox::information(this,"提示","注册成功");
    else if(text=="register fause")
        QMessageBox::warning(this,"提示","注册失败,用户名已存在");
    else if(text=="nouser")
        QMessageBox::warning(this,"提示","登录失败,该用户名还没有注册,请先注册");
    else if(text=="pwderr")
        QMessageBox::warning(this,"提示","登录失败,密码错误,请重新输入");

    else if(text=="loginsuccess")
    {
        QString name=ui->lineEditUsr->text();
        md=new MyDialog(name,ip,port);
        connect(md,SIGNAL(switchInterface()),this,SLOT(switchInterfaceSlot()));//切换回主界面的连接信号槽的约定

        md->show();
        this->close();
    }
    else if(text=="deviceh")
    {
        QMessageBox::warning(this,"提示","登录成功,该账号使用新设备登录");

        QString name=ui->lineEditUsr->text();
        md=new MyDialog(name,ip,port);
        connect(md,SIGNAL(switchInterface()),this,SLOT(switchInterfaceSlot()));//切换回主界面的连接信号槽的约定
        md->show();
        this->close();
    }
}

//注册
void Dialog::btnRegisterClickedSlot()
{
    QString name=ui->lineEditUsr->text();
    if(name=="")
    {
        QMessageBox::warning(this,"提示","请输入用户名");
        return;
    }
    QString pwd=ui->lineEditPwd->text();
    if(pwd=="")
    {
        QMessageBox::warning(this,"提示","请输入密码");
        return;
    }
    QString cmd="register";

    //拼接
    QString text=cmd.append("\n").append(name).append("\n").append(pwd);
    //创建文本流类
    QTextStream output(client);
    //发送消息
    output<<text;

}
//登录
void Dialog::btnLogonClickedSlot()
{
    QString name=ui->lineEditUsr->text();
    if(name=="")
    {
        QMessageBox::warning(this,"提示","请输入用户名");
        return;
    }
    QString pwd=ui->lineEditPwd->text();
    if(pwd=="")
    {
        QMessageBox::warning(this,"提示","请输入密码");
        return;
    }
     QString cmd="login";
    //拼接
    QString text=cmd.append("\n").append(name).append("\n").append(pwd);
    //创建文本流类
    QTextStream output(client);
    //发送消息
    output<<text;
}
//切换回主界面
void Dialog::switchInterfaceSlot()
{
    Dialog *dz=new Dialog;
    dz->show();
    md->close();
}


6)mydialog.cpp文件

#include "mydialog.h"
#include "ui_mydialog.h"

MyDialog::MyDialog(QString name,QString ip,int port,QWidget *parent) :
    QDialog(parent),name(name),ip(ip),port(port),
    ui(new Ui::MyDialog)
{
    client = new QTcpSocket(this);
    ui->setupUi(this);
    qDebug()<<name;

     creatFolder();//创建目录

    //按钮触发的槽函数(群发送、群历史记录、私聊、私聊历史记录、退出登录、注销账户、清空私聊历史记录)
    connect(ui->pushButtonSend,SIGNAL(clicked()),this,SLOT(btnSendClickedSlot()));
    connect(ui->pushButtonHisQ,SIGNAL(clicked()),this,SLOT(btnHisQClickedSlot()));
    connect(ui->pushButtonSendUsr,SIGNAL(clicked()),this,SLOT(btnSendUsrClickedSlot()));
    connect(ui->pushButtonHisS,SIGNAL(clicked()),this,SLOT(btnHisSClickedSlot()));
    connect(ui->pushButtonNoLog,SIGNAL(clicked()),this,SLOT(btnNoLogClickedSlot()));
    connect(ui->pushButtonNoRes,SIGNAL(clicked()),this,SLOT(btnNoResClickedSlot()));
    connect(ui->pushButtonClear,SIGNAL(clicked()),this,SLOT(btnClearClickedSlot()));
    //关闭历史记录显示
    connect(ui->pushButtonClosedp,SIGNAL(clicked()),this,SLOT(btnCloseDpClieckedSlot()));

    //按钮触发文件上传、下载
    connect(ui->pushButtonGetFileList,SIGNAL(clicked()),this,SLOT(btnGetFileListSlot()));
    connect(ui->pushButtonSendFile,SIGNAL(clicked()),this,SLOT(btnSendFileSlot()));
    connect(ui->pushButtonDownload,SIGNAL(clicked()),this,SLOT(btnDownloadSlot()));

    //按钮触发修改头像、昵称
    connect(ui->pushButtonSelectPho,SIGNAL(clicked()),this,SLOT(btnSelectPtoClickedSlot()));
    connect(ui->pushButtonSurePho,SIGNAL(clicked()),this,SLOT(btnSurePhoClickedSlot()));
    connect(ui->pushButtonSetname,SIGNAL(clicked()),this,SLOT(btnSetnameClickedSlot()));

    //获取好友列表
    connect(ui->pushButtonSelectChatUsr,SIGNAL(clicked()),this,SLOT(btnSelectChatUsrClickedSlot()));

    //接受到消息时触发的槽函数
    connect(client,SIGNAL(readyRead()),this,SLOT(mydialogReadyreadSlot()));

    //连接服务器(因为换页面进程了,端口号换了,需要重新连接)
    connntPort();//本进程连接服务器
    updataPort();//更新登录表

    //设置头像
    list.append(":/new/prefix1/b.jpg");
    list.append(":/new/prefix1/a.jpg");
    list.append(":/new/prefix1/d.jpg");
    list.append(":/new/prefix1/c.jpg");
    list.append(":/new/prefix1/e.jpg");
    list.append(":/new/prefix1/f.jpg");
    //初始化迭代器指针
    iter=list.begin();
    //设置图片大小为适应Label
    QSize size(ui->labelPixmap->width(),ui->labelPixmap->height());
    QPixmap pic(*iter);
    pic=pic.scaled(size,Qt::KeepAspectRatioByExpanding);
    ui->labelPixmap->setPixmap(*iter);
    //设置图标
    QString icoPath=":/new/prefix1/sun.ico";
    QIcon ico(icoPath);
    setWindowIcon(ico);

}

MyDialog::~MyDialog()
{
    if(client->isOpen())
        client->close();
    delete ui;
}
//换昵称
void MyDialog::btnSetnameClickedSlot()
{
    QString username=ui->lineEditSetname->text();
    this->setWindowTitle(username);

}
//选择头像
void MyDialog::btnSelectPtoClickedSlot()
{
    //如果向上翻到最后一张,就把迭代器指针指向第一张图片
   if(*iter==":/new/prefix1/f.jpg")
   {
        qDebug()<<"6";
        iter=list.begin();
   }
   //向下,迭代器指针++
   else
   {
       ++iter;
   }
   //设置图片大小为适应Label
   QSize size(ui->labelPixmap->width(),ui->labelPixmap->height());
   QPixmap pic(*iter);
   pic=pic.scaled(size,Qt::KeepAspectRatioByExpanding);
   ui->labelPixmap->setPixmap(*iter);
}
//确认头像
void MyDialog::btnSurePhoClickedSlot()
{
    QMessageBox::information(this,"提示","确认更换");
}
void MyDialog::btnSelectChatUsrClickedSlot()
{
    cmd="selectChatUsr";
    QString text=cmd.append("\n").append(name).append("\n").append(ip);
    QTextStream output(client);
    output<<text;
}

//本进程连接服务器
void MyDialog::connntPort()
{
    client->connectToHost(ip,port);
}
//更新服务器端登录表
void MyDialog::updataPort()
{
     cmd="updataPort";
     QString text=cmd.append("\n").append(name).append("\n").append(ip);
     QTextStream output(client);
     output<<text;
}
//创建下载目录
void MyDialog::creatFolder()
{
     qDebug()<<"建目录";
    QDir dir;
    QString path="D:/XinClientFileDownLoad";
    if(dir.mkdir(path))
        qDebug()<<"创建目录成功";
    else
    {
        qDebug()<<"创建目录失败";
    }
}
//获取文件列表
void MyDialog::btnGetFileListSlot()
{
    //获取服务器端指定文件夹路径
    QString path="D:/XinServerFileDownLoad";

    cmd="getFileList";
    QString text=cmd.append("\n").append(path).append("\n").append("a");
    //创建文本流对象
    QTextStream output(client);
    output<<text;


}
void MyDialog::sendFinishSlot()
{
    qDebug()<<"写入完成";
}

//上传文件
void MyDialog::btnSendFileSlot()
{

    cmd="sendFile";
    QString fileter="所有文件(*.*);;文本文件(*.txt);;Qt(*.cpp *.h *.ui)";
    QString path=QFileDialog::getOpenFileName(this,
                                              "打开",
                                              "D:/XinServerFileDownLoad",
                                              fileter);
    if(path==""&&readPath=="")
    {
        QMessageBox::warning(this,"提示","请选择文件!");
        return;
    }
    if(path==""&&readPath!="")
        return;
    readPath=path;
    //获取文件名
    QFileInfo info(readPath);
    QString filename=info.fileName();
   // qDebug()<<cmd;

    QString text=cmd.append("\n").append(filename).append("\n").append("ip");
    //创建文本流对象
    QTextStream output(client);
    //发送
    output<<text;
    //qDebug()<<text;

    //屏蔽上传按钮
    ui->pushButtonSendFile->setEnabled(false);

   // QThread::msleep(8000);
    SendThread *st=new SendThread(client,readPath,this);
    connect(st,SIGNAL(sendValueSignal(int)),this,SLOT(sendValueSlot(int)));
    connect(st,SIGNAL(sendFinish()),this,SLOT(sendFinishSlot()));
    st->start();

//    //创建读对象
//    QFile readFile(readPath);
//    readFile.open(QIODevice::ReadOnly);
//    //获取文件总大小
//    qint64 totalSize=readFile.size();
//    qint64 hasRead=0;
//    int lastper=0;
//    QByteArray buffer;

//    while(!readFile.atEnd())
//    {
//        buffer=readFile.read(1024);
//        qDebug()<<buffer;
//       // hasRead+=client->write(buffer);

//        client->waitForBytesWritten(true);

//        int per=hasRead*100/totalSize;
//        ui->progressBarSend->setValue(per);
//    }
//    ui->pushButtonSendFile->setEnabled(true);
}
void MyDialog::sendValueSlot(int value)
{
    ui->progressBarSend->setValue(value);
    if(value==100)
        ui->pushButtonSendFile->setEnabled(true);
}

//下载文件
void MyDialog::btnDownloadSlot()
{

}
//接受消息
void MyDialog::mydialogReadyreadSlot()
{
    if(cmd=="hisq"||cmd=="hiss")
        ui->textBrowser->clear();
    QTextStream input(client);
    QString text=input.readAll();
    qDebug()<<text;
    //如果存在"end"则移除
    QString e="end";
    text.remove(e);
    ui->textBrowser->append(text);

}

//发送消息
void MyDialog::btnSendClickedSlot()
{
    cmd="chatq";
    QString msg=ui->lineEdit->text();
    if(msg=="")
    {
        QMessageBox::warning(this,"提示","请输入要发送的内容");
        return;
    }
    QString text=cmd.append("\n").append(name).append("\n").append(msg);
    //创建文本流对象
    QTextStream output(client);
    output<<text;
}
//查询历史记录
void  MyDialog::btnHisQClickedSlot()
{
    ui->textBrowser->clear();
    cmd="hisq";
    QString text=cmd.append("\n").append("a").append("\n").append("b");
    //创建文本流对象
    QTextStream output(client);
    output<<text;
}

//私发
void MyDialog::btnSendUsrClickedSlot()
{
    qDebug()<<"私发";
    cmd="chats";
    QString msg=ui->lineEdit->text();
    if(msg=="")
    {
        QMessageBox::warning(this,"提示","请输入要发送的内容");
        return;
    }
    QString resiveuser=ui->lineEditSendUser->text();
    if(resiveuser=="")
    {
        QMessageBox::warning(this,"提示","请输入指定接收者");
        return;
    }
    QString text=cmd.append("\n").append(name).append("\n").append(resiveuser).append("\n").append(msg);
    //创建文本流对象
    QTextStream output(client);
    output<<text;
}

//私聊的历史记录查询
void MyDialog::btnHisSClickedSlot()
{
    ui->textBrowser->clear();
    cmd="hiss";
    QString text=cmd.append("\n").append(name).append("\n").append("b");
    //创建文本流对象
    QTextStream output(client);
    output<<text;
}
//退出登录
void MyDialog::btnNoLogClickedSlot()
{
    cmd="nologin";
    QString text=cmd.append("\n").append(name).append("\n").append("b");
    //创建文本流对象
    QTextStream output(client);
    output<<text;

    emit switchInterface();//发送切换界面信号
}
//注销账户
void MyDialog::btnNoResClickedSlot()
{
    QMessageBox::StandardButton result=QMessageBox::question(this,"question","你是否要注销该账户");
    if(result==QMessageBox::Yes)
    {
        cmd="noregister";
        QString text=cmd.append("\n").append(name).append("\n").append("b");
        //创建文本流对象
        QTextStream output(client);
        output<<text;

        emit switchInterface();//发送切换界面信号
    }
}

//清空私聊历史记录
void MyDialog::btnClearClickedSlot()
{
    QMessageBox::StandardButton result=QMessageBox::question(this,"question","你是否要清空聊天记录");
    if(result==QMessageBox::Yes)
    {
        cmd="SClear";
        QString text=cmd.append("\n").append(name).append("\n").append("b");
        //创建文本流对象
        QTextStream output(client);
        output<<text;
        ui->textBrowser->clear();
    }
}
//关闭历史记录显示
void MyDialog::btnCloseDpClieckedSlot()
{
    ui->textBrowser->clear();
}


7)sendthread.cpp文件

#include "sendthread.h"

SendThread::SendThread(QTcpSocket *client,QString readpth,QObject *parent) :
    QThread(parent)
{
    this->client=client;
    this->readpth=readpth;
}


void SendThread::run()
{
    qDebug()<<"子线程run函数";
    QFile readFile(readpth);
    readFile.open(QIODevice::ReadOnly);

    qint64 totalSize=readFile.size();
    qint64 hasRead=0;
    int lastper=0;
    QByteArray buffer;
    while(!readFile.atEnd())
    {
        buffer=readFile.read(1024);
        //qDebug()<<buffer;
        hasRead+=client->write(buffer);
        int per=hasRead*100/totalSize;
        if(per!=lastper)
        {
            emit sendValueSignal(per);
            lastper=per;
        }
    }

    //收尾
    readFile.flush();//清空读缓存区
    readFile.close();//关闭

}

8)ui文件

登录界面设计
在这里插入图片描述

主界面
在这里插入图片描述
在这里插入图片描述

3.2服务器端

1).pro文件
一定要加上sql(数据库模块)和network(网络模块)

#-------------------------------------------------
#
# Project created by QtCreator 2023-07-22T09:02:38
#
#-------------------------------------------------

QT       += core gui network sql

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = UdpChatMyServer
TEMPLATE = app


SOURCES += main.cpp\
        dialog.cpp \
    resivethread.cpp

HEADERS  += dialog.h \
    resivethread.h

FORMS    += dialog.ui
QMAKE_CXXFLAGS += -std=c++11

RESOURCES += \
    res.qrc

OTHER_FILES += \
    icoConfig.rc
RC_FILE += icoConfig.rc

2)dialog.h

#ifndef DIALOG_H
#define DIALOG_H

#include <QMessageBox>

#include<QTcpServer>
#include<QTcpSocket>
#include<QTextStream>

#include<QList>
#include<QDebug>

#include<QSqlDatabase>
#include<QSqlError>
#include<QSqlQuery>

#include<QDateTime>

#include<QDir>
#include"resivethread.h"


#include<QByteArray>
#include<QThread>

namespace Ui {
class Dialog;
}

class Dialog : public QDialog
{
    Q_OBJECT

public:
    explicit Dialog(QWidget *parent = 0);
    ~Dialog();


private:
    Ui::Dialog *ui;


    QTcpServer *server;//服务器管理类对象
    QList<QTcpSocket*> clients;//连接类容器对象
    QTcpSocket *socket;//一个新连接对象
    QSqlDatabase db;    //数据库对象

    void connect2DB();   //连接数据库
    void createTable();   //建表
    void insertResgisterData(QString,QString);  //插入注册表
    void insertLongonData(QString,QString,quint16);     //插入登录表
    void insertHisQData(QString,QString,QString); //插入群历史聊天记录表
    void insertHisSData(QString name,QString resiveuser,QString msg,QString time);//插入私聊记录表

    void deleteRegisterUser(QString name);//删除注册信息
    void deleteHistorySChat(QString name);//删除私聊历史记录

    void creatFolder();   //创建目录
    void sendFolderContent(QString folderpath);//发送文件列表给客户端

    void resiveFile(QString writeFilename,QString ip);
    void selectChatUsr();    //查在已注册的用户



private slots:
    void newConnSlot();//新链接来了
    void disconntedSlot();//链接断开
    void readyreadSlot();//接受数据
    void readyreadResgisterSlot(QString,QString);//注册
    void readyreadLogonSlot(QString,QString);//登录
    int isDataRNamePwdExists(QString,QString);//查注册表
    int isDataLNameIpPortExists(QString,QString,quint16);//查登录表
    void updateData(QString name,QString ip,quint16 port);//更新登录表

    void  readreadChatQSlot(QString,QString);//聊天(群里)
    void readreadHisQSlot();    //查询历史记录表

    int isDataLNameIpPortExistsS(QString name,QString& ip,quint16& port);//私聊专用查登录表(需要用到参数返回接受者的ip和端口)
    void  readreadChatSSlot(QString,QString,QString);     //私聊
    void readreadHisSSlot(QString name);      //查询私聊历史记录表

   // void resiveFile(QString writeFilename,QString ip);
    void fileWriteFinishSlot();




};

#endif // DIALOG_H

3)resivethread.h

#ifndef RESIVETHREAD_H
#define RESIVETHREAD_H

#include <QThread>

#include<QTcpServer>
#include<QTcpSocket>

#include<QFile>
#include<QTextStream>
#include<QByteArray>

#include<QDebug>


#include<QDialog>

class ResiveThread : public QThread
{
    Q_OBJECT
public:
    explicit ResiveThread(QTcpSocket *client,QString,QObject *parent = 0);
protected:
    void run();
private:
    QTcpSocket *client;
    int port;
    QString resivename;    //接受文件路径


signals:
    void fileWriteFinish();

public slots:




};

#endif // RESIVETHREAD_H

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐