基于Qt的UDP通信、TCP文件传输程序的设计与实现——QQ聊天群聊

🙌秋名山码民的主页
😂oi退役选手,Java、大数据、单片机、IoT均有所涉猎,热爱技术,技术无罪
🎉欢迎关注🔎点赞👍收藏⭐️留言📝
获取源码,添加WX

目录

  • 前言
  • 一、主界面和聊天窗口
  • 二、UDP聊天
  • 三、TCP文件传输
    • server类
    • Clint类
  • 最后


前言

QQ是一款优秀的聊天软件,本文将提供主要代码和思路来实现一个类似于QQ群聊的网络聊天软件,大致有以下俩个功能:

采用qt5编写,实现基于UDP的文本聊天功能,和基于TCP的文件传输功能

基本聊天会话功能

通过获取每一个用户运行该程序的时候,发送广播来实现,不仅用户登录的时候进行广播,退出、发送信息的时候都使用UDP广播来告知用户,每个用户的聊天窗口为一个端点

文件传输功能实现

文件的传输采用TCP来实现,用C/S架构

  1. 主界面选中要发送的文件,单击传输,打开发送文件对话框
  2. 当用户单击发送的时候,程序通过UDP广播给接收端,接收端在收到文件的UDP消息后,弹出提示框,是否接收
  3. 如果接收,先创建一个TCP通信客户端,双方进行TCP通信,如果拒绝,再通过UDP广播告知发送端

一、主界面和聊天窗口

在这里插入图片描述

#ifndef DRAWER_H
#define DRAWER_H#include <QToolBox>
#include <QToolButton>
#include <QWidget>
#include "myqq.h"class Drawer : public QToolBox
{
public:Drawer();
private:QToolButton *toolBtn1;//聊天对象窗口指针QWidget *chatWidget1;private slots:// 显示聊天对象窗口void showChatWidget1();MyQQ *myqq;};#endif // DRAWER_H
 	setWindowTitle(tr("My QQ v01"));setWindowIcon(QPixmap(":/images/R-C.jpg"));toolBtn1 = new QToolButton;toolBtn1->setText(tr("冰雪奇缘"));toolBtn1->setIcon(QPixmap(":/images/girl1.jpg"));toolBtn1->setAutoRaise(true); //设置toolBtn1在显示时自动提升,使得按钮外观更加立体感。toolBtn1->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); //设置toolBtn1的按钮样式为图标在文本旁边的形式。// 将显示函数与抽屉盒中相对应的用户按钮进行绑定//connect(toolBtn1,SIGNAL(clicked()),this,SLOT(showChatWidget1()));//connect(toolBtn1, &QToolButton::clicked, this, &QToolBox::showChatWidget1);connect(toolBtn1, &QToolButton::clicked, this, &Drawer::showChatWidget1);

二、UDP聊天

原理:如果要进行聊天,则首先要获取所有登录用户的信息,这个功能是通过在每一个用户运行该程序时发送广播实现的,不仅用户登录时要进行广播,而且在用户退出、发送消息时都使用UDP广播来告知所有用户。

#ifndef SERVER_H
#define SERVER_H#include <QDialog>
#include <QFile>
#include <QTcpServer>
#include <QTime>namespace Ui {
class Server;
}class Server : public QDialog
{Q_OBJECTpublic:explicit Server(QWidget *parent = nullptr);~Server();void initSrv(); // 初始化服务器void refused(); // 关闭服务器protected:void closeEvent(QCloseEvent *);void updClntProgress(qint64 numBytes);private slots:void on_Server_accepted();void sendMsg(); //发送数据void updclntProgress(qint64 numBytes); // 更新进度条void on_sOpenBtn_clicked();void on_sSendBtn_clicked();void on_sCloseBtn_clicked();private:Ui::Server *ui;qint16 tPort;QTcpServer *tSrv;QString fileName;QString theFileName;QFile *locFile; //待发送的文件qint64 totalBytes; //总共要发送的qint64 bytesWritten; //已发送的qint64 bytesTobeWrite; //待发送的qint64 payloadSize;  //被初始化为一个常量QByteArray outBlock; // 缓存一次的QTcpSocket *clntConn;QTime time;signals:void sendFileName(QString fileName);
};#endif // SERVER_H
#include "server.h"
#include "ui_server.h"#include <QFile>
#include<QTcpServer>
#include<QTcpSocket>
#include<QMessageBox>
#include <QFileDialog>
#include<QDebug>Server::Server(QWidget *parent) :QDialog(parent),ui(new Ui::Server)
{ui->setupUi(this);setFixedSize(400,207);tPort = 5555;tSrv = new QTcpServer(this);connect(tSrv,&QTcpServer::newConnection,this,&Server::sendMsg);initSrv();
}void Server::initSrv()
{payloadSize = 64*1024;totalBytes = 0;bytesWritten = 0;ui->sOpenBtn->setEnabled(true);ui->sSendBtn->setEnabled(false);tSrv->close();
}// 发送数据
void Server::sendMsg()
{ui->sSendBtn->setEnabled(false);clntConn = tSrv->nextPendingConnection();connect(clntConn,SIGNAL(bytesWritten(gint64)),this,SLOT(updCIntProgress(qint64)));ui->sStatusLabel->setText(tr("开始传送文件 号1 !").arg(theFileName));locFile = new QFile(fileName);if(!locFile->open((QFile::ReadOnly))){QMessageBox::warning(this,tr("应用程序"), tr("无法读取文件号1: n各2").arg(fileName).arg(locFile->errorString()));return;}totalBytes = locFile->size();QDataStream sendOut(&outBlock, QIODevice::WriteOnly);sendOut.setVersion(QDataStream::Qt_4_7);time.start();QString curFile = fileName.right(fileName.size() - fileName.lastIndexOf('/') - 1);sendOut << qint64(0) << qint64((outBlock.size() - sizeof(qint64)*2));bytesTobeWrite = totalBytes - clntConn->write(outBlock);outBlock.reserve(0);
}// 更新进度条
void Server::updClntProgress(qint64 numBytes)
{// 防止传输大文件产生冻结qApp->processEvents();bytesWritten += (int)numBytes;if(bytesTobeWrite > 0){outBlock = locFile->read(qMin(bytesTobeWrite,payloadSize));bytesTobeWrite -= (int)clntConn->write(outBlock);outBlock.resize(0);} else{locFile->close();}ui->progressBar->setMaximum(totalBytes);ui->progressBar->setValue(bytesWritten);float useTime = time.elapsed();double speed = bytesWritten / useTime;ui->sStatusLabel->setText(tr("已发送 %1MB (%2MB/s)\n 共%3MB 已用时:%4s\n 估计剩余时间:%5秒").arg(bytesWritten/(1024*1024)).arg(bytesWritten / (1024*1024)).arg(speed*1000 / (1024*1024),0,'f',0).arg(totalBytes / (1024 * 1024)).arg(useTime/1000,0,'f',0).arg(totalBytes/speed/1000 - useTime/1000,0,'f',0));if(bytesWritten == totalBytes){locFile->close();tSrv->close();ui->sStatusLabel->setText(tr("传送文件 %1 成功").arg(theFileName));}}
Server::~Server()
{delete ui;
}void Server::on_sOpenBtn_clicked()
{fileName = QFileDialog::getOpenFileName(this);if(!fileName.isEmpty()){theFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/')-1);ui->sStatusLabel->setText(tr("要发送的文件为:%1").arg(theFileName));ui->sOpenBtn->setEnabled(false);ui->sSendBtn->setEnabled(true);}
}void Server::on_sSendBtn_clicked()
{if(!tSrv->listen(QHostAddress::Any,tPort)){qDebug() << tSrv ->errorString();close();return;}ui->sStatusLabel->setText("等待……");emit sendFileName(theFileName);
}void Server::on_sCloseBtn_clicked()
{if(tSrv->isListening()){tSrv->close();if(locFile->isOpen())locFile->close();clntConn->abort();}close();
}void Server::closeEvent(QCloseEvent *)
{on_sCloseBtn_clicked();
}void Server::refused()
{tSrv->close();ui->sStatusLabel->setText(tr("对方拒绝!"));
}

三、TCP文件传输

文件的传输采用TCP来实现,用C/S(客户端/服务器)方式,创建俩个新类,client和server类

server类

在这里插入图片描述

#ifndef SERVER_H
#define SERVER_H#include <QDialog>
#include <QFile>
#include <QTcpServer>
#include <QTime>namespace Ui {
class Server;
}class Server : public QDialog
{Q_OBJECTpublic:explicit Server(QWidget *parent = nullptr);~Server();void initSrv(); // 初始化服务器void refused(); // 关闭服务器protected:void closeEvent(QCloseEvent *);void updClntProgress(qint64 numBytes);private slots:void on_Server_accepted();void sendMsg(); //发送数据void updclntProgress(qint64 numBytes); // 更新进度条void on_sOpenBtn_clicked();void on_sSendBtn_clicked();void on_sCloseBtn_clicked();private:Ui::Server *ui;qint16 tPort;QTcpServer *tSrv;QString fileName;QString theFileName;QFile *locFile; //待发送的文件qint64 totalBytes; //总共要发送的qint64 bytesWritten; //已发送的qint64 bytesTobeWrite; //待发送的qint64 payloadSize;  //被初始化为一个常量QByteArray outBlock; // 缓存一次的QTcpSocket *clntConn;QTime time;signals:void sendFileName(QString fileName);
};#endif // SERVER_H
#include "server.h"
#include "ui_server.h"#include <QFile>
#include<QTcpServer>
#include<QTcpSocket>
#include<QMessageBox>
#include <QFileDialog>
#include<QDebug>Server::Server(QWidget *parent) :QDialog(parent),ui(new Ui::Server)
{ui->setupUi(this);setFixedSize(400,207);tPort = 5555;tSrv = new QTcpServer(this);connect(tSrv,&QTcpServer::newConnection,this,&Server::sendMsg);initSrv();
}void Server::initSrv()
{payloadSize = 64*1024;totalBytes = 0;bytesWritten = 0;ui->sOpenBtn->setEnabled(true);ui->sSendBtn->setEnabled(false);tSrv->close();
}// 发送数据
void Server::sendMsg()
{ui->sSendBtn->setEnabled(false);clntConn = tSrv->nextPendingConnection();connect(clntConn,SIGNAL(bytesWritten(gint64)),this,SLOT(updCIntProgress(qint64)));ui->sStatusLabel->setText(tr("开始传送文件 号1 !").arg(theFileName));locFile = new QFile(fileName);if(!locFile->open((QFile::ReadOnly))){QMessageBox::warning(this,tr("应用程序"), tr("无法读取文件号1: n各2").arg(fileName).arg(locFile->errorString()));return;}totalBytes = locFile->size();QDataStream sendOut(&outBlock, QIODevice::WriteOnly);sendOut.setVersion(QDataStream::Qt_4_7);time.start();QString curFile = fileName.right(fileName.size() - fileName.lastIndexOf('/') - 1);sendOut << qint64(0) << qint64((outBlock.size() - sizeof(qint64)*2));bytesTobeWrite = totalBytes - clntConn->write(outBlock);outBlock.reserve(0);
}// 更新进度条
void Server::updClntProgress(qint64 numBytes)
{// 防止传输大文件产生冻结qApp->processEvents();bytesWritten += (int)numBytes;if(bytesTobeWrite > 0){outBlock = locFile->read(qMin(bytesTobeWrite,payloadSize));bytesTobeWrite -= (int)clntConn->write(outBlock);outBlock.resize(0);} else{locFile->close();}ui->progressBar->setMaximum(totalBytes);ui->progressBar->setValue(bytesWritten);float useTime = time.elapsed();double speed = bytesWritten / useTime;ui->sStatusLabel->setText(tr("已发送 %1MB (%2MB/s)\n 共%3MB 已用时:%4s\n 估计剩余时间:%5秒").arg(bytesWritten/(1024*1024)).arg(bytesWritten / (1024*1024)).arg(speed*1000 / (1024*1024),0,'f',0).arg(totalBytes / (1024 * 1024)).arg(useTime/1000,0,'f',0).arg(totalBytes/speed/1000 - useTime/1000,0,'f',0));if(bytesWritten == totalBytes){locFile->close();tSrv->close();ui->sStatusLabel->setText(tr("传送文件 %1 成功").arg(theFileName));}}
Server::~Server()
{delete ui;
}void Server::on_sOpenBtn_clicked()
{fileName = QFileDialog::getOpenFileName(this);if(!fileName.isEmpty()){theFileName = fileName.right(fileName.size() - fileName.lastIndexOf('/')-1);ui->sStatusLabel->setText(tr("要发送的文件为:%1").arg(theFileName));ui->sOpenBtn->setEnabled(false);ui->sSendBtn->setEnabled(true);}
}void Server::on_sSendBtn_clicked()
{if(!tSrv->listen(QHostAddress::Any,tPort)){qDebug() << tSrv ->errorString();close();return;}ui->sStatusLabel->setText("等待……");emit sendFileName(theFileName);
}void Server::on_sCloseBtn_clicked()
{if(tSrv->isListening()){tSrv->close();if(locFile->isOpen())locFile->close();clntConn->abort();}close();
}void Server::closeEvent(QCloseEvent *)
{on_sCloseBtn_clicked();
}void Server::refused()
{tSrv->close();ui->sStatusLabel->setText(tr("对方拒绝!"));
}

Clint类

TCP客户端类,用于接收文件。
在这里插入图片描述

#ifndef CLIENT_H
#define CLIENT_H#include <QDialog>
#include <QHostAddress>
#include <QFile>
#include <QTime>
#include <QTcpSocket>namespace Ui {
class client;
}class client : public QDialog
{Q_OBJECTpublic:explicit client(QWidget *parent = nullptr);~client();void setHostAddr(QHostAddress addr);void setFileName(QString name);protected:void closeEvent(QCloseEvent *);private:Ui::client *ui;QTcpSocket *tClnt;quint16 blockSize;QHostAddress hostAddr;qint16 tPort;qint64 totalBytes;qint64 bytesReceived;qint64 fileNameSize;QString fileName;QFile *locFile;QByteArray inBlock;QTime time;private slots:void newConn(); // 连接到服务器void readMsg(); // 读取文件数据
//    void displayErr(QAbstractSocket::SocketError); // 显示错误信息void on_cCancleBtn_clicked();void on_cCloseBtn_clicked();
};#endif // CLIENT_H
#include "client.h"
#include "ui_client.h"
#include <QDebug>
#include <QMessageBox>
#include <QTime>client::client(QWidget *parent) :QDialog(parent),ui(new Ui::client)
{ui->setupUi(this);setFixedSize(400,190);totalBytes = 0;bytesReceived = 0;fileNameSize = 0;tClnt = new QTcpSocket(this);tPort = 5555;connect(tClnt, &QTcpSocket::readyRead, this, &client::readMsg);}// 连接服务器
void client::newConn()
{blockSize = 0;tClnt->abort();tClnt->connectToHost(hostAddr,tPort);time.start();
}// 发送文件
void client::readMsg()
{QDataStream in(tClnt);in.setVersion(QDataStream::Qt_4_7);float useTime = time.elapsed();if (bytesReceived <= sizeof(qint64)*2){if ((tClnt->bytesAvailable() >= sizeof(qint64)*2) && (fileNameSize == 0)){in>>totalBytes>>fileNameSize;bytesReceived += sizeof(qint64)*2;}if((tClnt->bytesAvailable() >= fileNameSize) && (fileNameSize != 0)){in>>fileName;bytesReceived +=fileNameSize;if(!locFile->open(QFile::WriteOnly)){QMessageBox::warning(this,tr("应用程序"),tr("无法读取文件%1:\n%2.").arg(fileName).arg(locFile->errorString()));}return;}else{return;}}if (bytesReceived < totalBytes){bytesReceived += tClnt->bytesAvailable();inBlock = tClnt->readAll();locFile->write(inBlock);inBlock.resize(0);}ui->progressBar->setMaximum(totalBytes);ui->progressBar->setValue(bytesReceived);double speed = bytesReceived / useTime;ui->label_2->setText(tr("已接收 %1MB (%2MB/s)\n 共%3MB 已用时:%4s\n 估计剩余时间:%5秒").arg(bytesReceived/(1024*1024)).arg(speed*1000 / (1024*1024),0,'f',0).arg(totalBytes / (1024 * 1024)).arg(useTime/1000,0,'f',0).arg(totalBytes/speed/1000 - useTime/1000,0,'f',0));if(bytesReceived == totalBytes){locFile->close();tClnt->close();ui->label->setText(tr("接收文件 %1 成功").arg(fileName));}
}
client::~client()
{delete ui;
}void client::on_cCancleBtn_clicked()
{tClnt->abort();if(locFile->isOpen())locFile->close();}void client::on_cCloseBtn_clicked()
{tClnt->abort();if(locFile->isOpen())locFile->close();close();
}void client::closeEvent(QCloseEvent *)
{on_cCloseBtn_clicked();
}

最后

至此已完成,读者还可根据自己所需来添加一些拓展功能,更改字体、字号和颜色等等……如果本文对你有所帮助,还请三连支持一下博主!
请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/160940.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PostgreSQL序列,怎么才能第二天重新从1开始计数

--确定日期最大值和每天序列号最大值 with cte as(select (((1::bigint)<<32)-1) as max_date_second,(((1::bigint)<<31)-1) as max_sn )select max_date_second,to_timestamp(max_date_second),max_sn,((max_date_second<<31)|max_sn) as max_val,(((max_d…

Selenium 元素不能定位总结

目录 元素不能定位总结: 1、定位语法错误&#xff1a; 定位语法错误&#xff0c;如无效的xpath&#xff0c;css selector,dom路径错误&#xff0c;动态dom 定位语法错误&#xff0c;动态路径&#xff08;动态变化&#xff09; 定位策略错误&#xff0c;如dom没有id用id定位…

研发探索:导购APP、查券返利机器人与淘客系统,全面对比与选择

研发探究&#xff1a;导购APP、查券返利机器人与淘客系统&#xff0c;全面对比与选择 在互联网购物的时代&#xff0c;导购APP、淘客机器人和微赚淘客系统成为了消费者们的三大好帮手。它们各具优势&#xff0c;但也存在一些不足。本文将为您详细对比这三种工具&#xff0c;帮…

vue history路径编码

记录今天遇到的一个问题&#xff1a; 问题现状 有一个需要前端伪造302进行重定向的需求&#xff0c;我们需要将这样的一个路径&#xff1a;http://xxx.com/system-name/#/index&#xff0c;拼接在跳转地址的后面&#xff0c;进行重定向。拼接的方式是这样的&#xff1a; htt…

攻防世界-web-Confusion1

1. 题目描述 打开链接&#xff0c;如图 点击Login和Rigister&#xff0c;都报错 但是有提示 指出了flag所在的位置&#xff0c;题目中直接能获取到的信息暂时就这么些了 2. 思路分析 既然告诉了我们flag文件的位置&#xff0c;那么要读取到这个文件&#xff0c;要么是任意文…

AI辅助带货直播场景源码系统 附带网站的搭建教程

互联网技术的发展和普及&#xff0c;直播带货行业迅速崛起。然而&#xff0c;直播带货在带来商机的同时&#xff0c;也面临着诸多挑战。如直播内容缺乏新意、转化率低等问题。针对这些问题&#xff0c;AI辅助带货直播场景源码系统应运而生&#xff0c;旨在利用人工智能技术&…

【高级渗透篇】网络安全面试

【高级渗透篇】网络安全面试 1.权限维持2.代码安全Python语法相关 1.权限维持 Linux权限维持方法论 Windows权限维持方法论 2.代码安全 Python 语法相关 1、Python的值类型和引用类型是哪些 Python 中的值类型包括&#xff1a; 数字类型&#xff08;如整数、浮点数、复数…

对接苹果支付退款退单接口

前言 一般而言&#xff0c;我们其实很少对接退款接口&#xff0c;因为退款基本都是商家自己决定后进行操作的&#xff0c;但是苹果比较特殊&#xff0c;用户可以直接向苹果发起退款请求&#xff0c;苹果觉得合理会退给用户&#xff0c;但是目前公司业务还是需要对接这个接口&am…

试试MyBatis-Plus可视化代码生成器,太香了,你一定会感谢我

前言 在基于Mybatis的开发模式中&#xff0c;很多开发者还会选择Mybatis-Plus来辅助功能开发&#xff0c;以此提高开发的效率。虽然Mybatis也有代码生成的工具&#xff0c;但Mybatis-Plus由于在Mybatis基础上做了一些调整&#xff0c;因此&#xff0c;常规的生成工具生成的代码…

PC端使子组件的弹框关闭

子组件 <template><el-dialog title"新增部门" :visible"showDialog" close"close"> </el-dialog> </template> <script> export default {props: {showDialog: {type: Boolean,default: false,},},data() {retu…

【JavaSE】-5-嵌套循环

回顾 一、java语言特点 二、配置java环境 path 三、记事本 javac -d . java 包名.类名 四、eclipse 五、变量 定义变量 数据类型 变量名值; 六、相关的数据类型 ​ 基本&#xff08;四类 、8种&#xff09;、引用 ​ 类型转换&#xff08;自动、强制&#xff09; ​ 运…

Java面向对象(高级)-- 类中属性赋值的位置及过程

文章目录 一、赋值顺序&#xff08;1&#xff09;赋值的位置及顺序&#xff08;2&#xff09;举例&#xff08;3&#xff09;字节码文件&#xff08;4&#xff09;进一步探索&#xff08;5&#xff09;最终赋值顺序&#xff08;6&#xff09;实际开发如何选 二、(超纲)关于字节…

1992-2021年省市县经过矫正的夜间灯光数据(GNLD、VIIRS)

1992-2021年省市县经过矫正的夜间灯光数据&#xff08;GNLD、VIIRS&#xff09; 1、时间&#xff1a;1992-2021年3月&#xff0c;其中1992-2013年为年度数据&#xff0c;2013-2021年3月为月度数据 2、来源&#xff1a;DMSP、VIIRS 3、范围&#xff1a;分区域汇总&#xff1a…

SpringBoot : ch05 整合Mybatis

前言 随着Java Web应用程序的快速发展&#xff0c;开发人员需要越来越多地关注如何高效地构建可靠的应用程序。Spring Boot作为一种快速开发框架&#xff0c;旨在简化基于Spring的应用程序的初始搭建和开发过程。而MyBatis作为一种优秀的持久层框架&#xff0c;提供了对数据库…

【Linux】-进程间通信-共享内存(SystemV),详解接口函数以及原理(使用管道处理同步互斥机制)

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

中低压MOSFET 2N7002T 60V 300mA 双N通道 采用SOT-523封装形式

2N7002KW小电流双N通道MOSFET&#xff0c;电压60V电流300mA&#xff0c;采用SOT-523封装形式。低Ros (on)的高密度单元设计&#xff0c;坚固可靠&#xff0c;具有高饱和电流能力&#xff0c;ESD防护门HBM2KV。可应用于直流/直流转换器&#xff0c;电池开关等产品应用上。

Redis JDBC

1、导入依赖&#xff1a; <dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.4.3</version></dependency> 2、连接JDBC public class JedisDemo05 {public static void main(String[]…

成为AI产品经理——AI产品经理工作全流程

一、业务背景 背景&#xff1a;日常排球训练&#xff0c;中考排球项目和排球体测项目耗费大量人力成本和时间成本。 目标&#xff1a;开发一套用于实时检测排球运动并进行排球垫球计数和姿势分析的软件。 二、产品工作流程 我们这里对于产品工作流程的关键部分进行讲解&…

「Docker」如何在苹果电脑上构建简单的Go云原生程序「MacOS」

介绍 使用Docker开发Golang云原生应用程序&#xff0c;使用Golang服务和Redis服务 注&#xff1a;写得很详细 为方便我的朋友可以看懂 环境部署 确保已经安装Go、docker等基础配置 官网下载链接直达&#xff1a;Docker官网下载 Go官网下载 操作步骤 第一步 创建一个…