QT实现聊天室

2023-11-08

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

QT实现聊天室 的相关文章

  • 通过CMake实现Qt项目

    我正在尝试通过 Cmake 构建并运行非常简单且基本的 Qt 示例 删除 pro 文件 以下是Qt项目的代码 自动生成的Qt项目的目录结构为 Cmake my project name headers mainwindow h source
  • 如何在qt中进行异步文件io?

    我想知道如何在qt中实现异步文件io 这在普通的 qt 中是否可以实现 或者有人需要使用另一个库 例如 libuv 来实现这样的事情 我正在查看 QDataStream 但即使它是一个 流 它也不是非阻塞的 我想一种解决方案是制作一个在内部
  • 具有多个父项的 Qt 树模型

    我想构建一棵树 其中一个元素可以引用另一个元素 我想要构建的树是 像这样的东西 A B C D E F P this is a pointer to C D first child of C E second child of C I fo
  • 如何为 qmlRegisterSingletonType 实现单例提供程序?

    我想在 QML 中使用 C 类作为 Singleton 实例 并认为我必须使用 qmlRegisterSingletonType 注册它们 此函数需要一个提供已注册 C 类的实例的函数 我在 Windows 上使用最新的 Qt 5 3 1
  • 如何使 Python、QT 和 Webkit 在无头服务器上工作?

    我有 Debian Linux 服务器 我用它来做各种事情 我希望它能够完成一些我需要定期完成的网络抓取工作 这段代码可以是在这里找到 http bit ly QeqvzX import sys from PyQt4 QtGui impor
  • 如何使用 Qt DOM 通过此语法获取 xml 属性

    我正在使用 Qt DOM XML 解析器 并且遇到了如下属性定义的问题
  • 在 Qt 服务器上验证用户身份

    我正在尝试使用 C QtTcpSocket 为个人项目 多人国际象棋游戏 实现身份验证系统 我的朋友建议了一种验证用户的方法 但我想问是否有更简单或更好的方法 来自 Python 背景 做这个项目主要是为了加深对 C 的理解 我将发布我朋友
  • 如何在QT上暂停和重新启动Qtimer

    我有 Ubuntu 我正在使用 IDEQT on C 我将暂停和恢复计时器 例如 void Ordonnancer les taches on pushButton clicked connect dataTimer SIGNAL time
  • QMainWindow 上的 Qt 布局

    我设计了一个QMainWindow with QtCreator s设计师 它由默认的中央小部件 aQWidget 其中包含一个QVBoxLayout以及其中的所有其他小部件 现在我想要的一切就是QVBoxLayout自动占据整个中央小部件
  • 在 Qt 中使用多个不同的流读取同一文件

    使用 Qt 是否可以使用多个流读取文件以同时访问其中的不同数据部分 请注意 Qt 中的流 QTextStream QDataStream 不处理底层设备中的位置 流类只是一个包装器 用于更轻松地解析设备 QFile 实例 内的二进制数据 因
  • QTableView 并双击一个单元格

    我正在开发测试用例编辑器 该编辑器包含 USART 传输和接收数据包格式 编辑器是一个表格视图 发送和接收数据包的长度为八个字节 例如 0x01 0x02 0x03 0x08 它在我的第五和第六栏中 现在 我希望此列中的单元格为只读 但是当
  • 如何doxygen注释Qt属性?

    我想将 Doxygen 注释附加到我的 Q PROPERTY 例如 song h class Song public QObject Q OBJECT private Q PROPERTY QString title READ title
  • Qt中用于线程间通信的类设计

    问题陈述 用相机跟踪物体并相应地移动相机的方位角和仰角 Process 相机获取物体的图像 处理相机的每一帧以查找物体 应该被跟踪 并将每帧中生成的信息传递给机械设备 万向节 以平移和倾斜方式移动摄像机 Design 主 Gui 在一个线程
  • 针对初学者的 QT 商业许可证与非商业许可证 [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 QT 许可似乎非常反学习 因为据我所知 用它开发的任何东西都只能是商业的当且仅当 its entire开发是在使用商业许可证的情况下完成的
  • 覆盖 QWebView 中的页面回复

    我试图在 Qt 的 QWebView 中拦截页面 表单请求 并在某些情况下使用替代内容进行响应 QNetworkReply ngcBrowser createRequest Operation operation const QNetwor
  • 连接到 QNetworkReply::error 信号

    我正在使用 Qt5 的新连接语法 QNetworkReply 有一个名为error http qt project org doc qt 5 0 qtnetwork qnetworkreply html error 2还有一个函数叫做err
  • 无法运行 Qt 应用程序:找不到版本“Qt_5”

    我运行 Ubuntu 16 04 LTS 我的问题是我无法运行可以编译的 Qt5 应用程序 这是我尝试运行它时得到的结果 home user Desktop sconfig dist Release GNU Linux SCongif us
  • QFileSystemModel setRootPath

    我正在尝试创建一个 Qt 应用程序来显示文件夹 Mac OS 中的 Users 文件夹 的内容 这是代码 QFileSystemModel dirModel new QFileSystemModel dirModel gt setRootP
  • Qt(在 Windows 上)将权限级别设置为“requireAdministrator”

    我正在使用 Qt Creator 并努力制作 exe文件默认以管理员身份运行 在线阅读所有解决方案我试图将这一行放入我的 pro file QMAKE LFLAGS MANIFESTUAC level requireAdministrato
  • 为什么 QT 设计器重新调整大小或不允许我缩小或展开小部件或按钮?

    很多时候 在使用 QT 设计器时 我发现自己需要通过缩小或扩展来调整事物的大小 每当我尝试这样做时 程序都不允许我这样做 而只是恢复到将对象放置在窗口中时给我的原始默认大小 无论我的布局如何 为什么要这样做 是否有可能改变这一点 以便我可以

随机推荐

  • 【数据异常校验】肖维勒准则(Chauvenet Criterion)处理异常数据

    介绍 在统计理论中 肖维勒准则 以William Chauvenet命名 是评估一组实验数据 一组异常值 是否可能是虚假的一种手段 肖维勒准则背后的想法是找到一个以正态分布的均值为中心的概率带 它应该合理地包含数据集的所有n个样本 通过这样
  • 在ios系统上实现更改IP地址

    在当今的互联网环境中 我们经常需要更改手机的IP地址来避免一些限制或保护我们的隐私 然而 在iOS系统上 更改IP地址并不像在其他平台上那么容易 因此 本文将分享一种简单的方法 帮助您在iOS系统上免费更改手机的IP地址 在iOS系统上 我
  • html账号不能为空,在HTML5 中,( )属性用于规定输入框填写的内容不能为空,否则不允许用户提交表单。...

    AT89C51单片机串行口的4种工作方式中 中则和的波特率是可调的 与定时器 计数器T1的溢出率有关 另外两种方式的波特率是固定的 协助扩散就是协同运输 属性输入是物质从高浓度侧转运到低浓度侧 不需要消耗能量 用于允许用户Task 2 Fi
  • stm32 hal库接收不定长数据程序

    协议层 串口通讯的数据包由发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口 在串口通讯的协议层中 规定了数据包的内容 它由启始位 主体数据 校验位以及停止位组成 通讯双方的数据包格式要约定一致才能正常收发数据 其组成见图 波特
  • HashMap和HashTable的区别(面试题)

    HashMap和HashTable的区别 面试题 概念 HashMap HashTable 对比 总结 概念 HashMap 基于哈希表的 Map 接口的实现 此实现提供所有可选的映射操作 并允许使用 null 值和 null 键 除了非同
  • MySQL创建数据库和创建数据表

    MySQL 创建数据库和创建数据表 MySQL 是最常用的数据库 在数据库操作中 基本都是增删改查操作 简称CRUD 在这之前 需要先安装好 MySQL 然后创建好数据库 数据表 操作用户 一 数据库操作语言 数据库在操作时 需要使用专门的
  • python中time方法,生成当前时间年月日时分秒

    在Python中 可以使用time模块中的strftime 方法结合时间格式化字符串来生成当前的年月日时分秒 下面是一个详细解释的示例代码 import time 获取当前时间的时间戳 current timestamp time time
  • png在ai转为路径_ai怎么把png转换为路径

    1 png格式转为ai格式 解决如何将png图像转换成清晰的ai或者cdr矢量图的步骤如下 1 打开PS软件 打开JPG的图片文件 文件 打开 选择图片存放的路径 找到文件 打开 png 429 230 60238 0 gt 2 这是一个非
  • jvm的启动过程

    一 JVM的装入环境和配置 在学习这个之前 我们需要了解一件事情 就是JDK和JRE的区别 JDK是面向开发人员使用的SDK 它提供了Java的开发环境和运行环境 JDK中包含了JRE JRE是Java的运行环境 是面向所有Java程序的使
  • elasticsearch得分设置以及分词器不同层次定义

    GET cat indices GET hotel search GET search query constant score filter term lvg mc 酒店 boost 1 2 DELETE my index PUT my
  • NLP——机器翻译中的Seq2Seq

    文章目录 框架 简介 Encoder Decoder CNN Seq2Seq Seq2Seq模型缺点 框架 简介 Seq2Seq 全称Sequence to Sequence 序列到序列 它是一种通用的编码器 解码器框架 这个框架最初是为了
  • Python中的MetaPathFinder

    MetaPathFinder 是 Python 导入系统中的一个关键组件 它与 sys meta path 列表紧密相关 sys meta path 是一个包含 MetaPathFinder 实例的列表 这些实例用于自定义模块的查找和加载逻
  • 外汇市场与交易系统读书笔记(1)

    本文为 外汇市场与交易系统 这本书的读书笔记 1 a 汇率报价一般有五个数字 包括小输掉 其中 精度最高到小数点后四位 一般而言 我们以五个数字中的最低位作为基本点 b 外汇即期汇率报价一般包含买入汇率和卖出汇率 例如usd jpy的汇率可
  • 【知识积累】分析并实现O(N^2)的算法(对数器验证)

    1 选择排序 package com example demo algorithm import java util Arrays Description 选择排序 数据规模 N 0 N 1 看 比 交换 1 N 1 看 比 交换 2 N
  • SoC的开发

    怎么做SoC SoC是干啥的 SoC就是将CPU GPU Uart I2C WiFi Etherne等硬件IP连起来 做到一个芯片上 主要工作有 1 用verilog将这些IP core连起来 在verilog仿真器上进行验证 也要写一些C
  • Axure Repeater系列---排序

    最新学习整理Repeater 网上也能找到一些实现排序的帖子 但是对于不熟悉中继器的同学来说 直接上手还是有点难度的 我也遇到一些坑 特整理记录下来 共同学习 学习之前最好了解下中继器的各个属性以及函数的含义 工具 Axure8 0 学习目
  • 使用人声分离在线网站获得音乐

    有时候碰到一个广告中的音乐 觉得非常悦耳 想将它占为己有 使用到自己的片子中 但奈何其中有广告人声 想获得纯的音乐 如何做到呢 本文给出了方法 希望对你有用 注 本教程使用到了几个工具 1 fdm 下载片源 2 视频编辑大师 分离视频中的音
  • MySQL必知必会——第二十章更新和删除数据

    更新和删除数据 本章介绍如何利用UPDATE和DELETE语句进一步操纵表数据 更新数据 为了更新 修改 表中的数据 可以使用UPDATE语句 UPDATE的两种用法 更新表中特定行 更新表中所有行 不要省略WHERE子句 缺少WHERE子
  • 树莓派教程 - 2.1 树莓派USB摄像头 树莓派罗技免驱摄像头 fswebcam常用参数

    树莓派外接摄像头 最常用的有两种 CSI摄像头 USB摄像头 当然网络摄像头也是可以的 一般的USB摄像头都是UVC免驱的 而且可以方便的插拔和安装 平时最为常用 一 硬件设备 usb摄像头使用的 罗技c310 只要是UVC免驱就可以 二
  • QT实现聊天室

    qt实现聊天室 项目功能简介 1 连接 客户端 需要先连接服务器 就是输入服务器端的IP和端口连接服务器 如果连接成功 连接按钮显示文字会显示已连接 颜色变浅 2 注册 接下来是注册 如果申请的用户名还有人用户注册 则可以注册成功 如果之前