建立工程

在学习了霍亚飞的《Qt Creator快速入门(第3版)》后,参考“18.4 TCP”中的示例程序,编写了一个在同一个工程中实现服务器与客户端的习作程序,变量名命名方式和示例程序大体一致,程序实现了TCP客户端发送文件,服务器接收文件的功能,用到了QFile,QDataStream,QTcpServer,QTcpSocket等QT类。本文记录了编写过程,主要目的就是为了熟悉QT下TCP编程。
首先新建工程,选择“Qt Widget Application”,工程名称是“TcpFileServerClient”,下一步到“Class Information”的时候基类选择QWidget,Class Name改成Client,如下图所示:

点击下一步直到完成。
在项目栏中双击工程文件名:

在QT += core gui后面添加network,变成这样:
QT += core gui network
这个操作将会把QT网络编程需要的库文件添加进来。

设计客户端界面

双击client.ui文件,进入设计师界面,在界面上先放一个窗体布局控件(Form Layout),然后在里面右键添加“窗体布局行”,标签文字“主机:”,字段名称改成hostEdit,如下图所示:

然后用相同方法添加“端口:”,字段名称改成portEdit
然后添加进度条(Progress Bar),使用默认名称progressBar即可
添加一个标签控件(label),名称改成labelStatus,文本内容改成“请先打开文件”
添加两个按钮(Push Button),一个按钮文本改成“打开文件”,名称改成openButton;一个按钮文本改成“发送文件”,名称改成sendButton
最终界面如下图所示:

编写客户端代码

修改client.h文件,在里面添加如下内容:

//这个头文件中定义了网络错误处理槽函数测参数类型。
#include <QAbstractSocket>

client.h里面只是使用到了类指针,可以前置声明这个类来减少编译时间。

//前置声明类
class QTcpSocket;
class QFile;

……

//定义私有变量
private:QTcpSocket* tcpClient;//客户端连接QFile* localFile;//文件操作qint64 totalBytes;//总传输字节数qint64 bytesToWrite;//还剩下要写的字节数qint64 bytesWritten;//已经写的字节数qint64 payloadSize;//每次传输字节数QString fileName;//包含了全路径的文件名QByteArray outBlock;//输出缓冲区
//定义私有槽函数,全都是和网络相关的
private slots://建立连接成功后槽函数,开始传输void startTransfer();//发送完成槽函数,更新进度条,开始下一批传输void sendFile(qint64 numSend);//网络错误处理槽函数void displayError(QAbstractSocket::SocketError);

修改client.c文件
包含必要头文件:

#include <QtNetwork>
#include <QFileDialog>
#include <QDebug>

此时可以先编译一下,不用考虑是否会成功,主要目的是为了生成ui对象中包含的窗体对象变量,这样后续编程会比较方便。
在构造函数中实例化客户端对象,并且关联相关槽函数。

//实例化客户端对象tcpClient = new QTcpSocket(this);//关联建立连接成功槽函数connect(tcpClient, &QTcpSocket::connected, this, &Client::startTransfer);
//建立发送完成槽函数connect(tcpClient, &QTcpSocket::bytesWritten, this, &Client::sendFile);//关联错误处理槽函数,因为error是一个多态函数,所以只能使用SIGNAL关键字来关联connect(tcpClient, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(displayError(QAbstractSocket::SocketError)));ui->sendButton->setEnabled(false);ui->hostEdit->setText(tr("localhost"));ui->portEdit->setText(tr("6666"));

双击client.ui进入设计师界面,然后在打开文件按钮上右键选择转到槽,选择默认的clicked(),点击OK按钮后会自动生成
void Client::on_openButton_clicked()
这个函数将在单击按钮时执行。此函数代码如下:

void Client::on_openButton_clicked()
{//通过对话框得到含有全路径的文件名fileName = QFileDialog::getOpenFileName(this);if (!fileName.isEmpty()){ui->sendButton->setEnabled(true);ui->labelStatus->setText(tr("等待传输文件%1").arg(fileName));}
}

然后使用设计师界面自动生成发送文件按钮的槽函数,具体如下:

void Client::on_sendButton_clicked()
{//打开文件localFile = new QFile(fileName);if (!localFile->open(QFile::ReadOnly)){qDebug() << localFile->errorString();
ui->labelStatus->setText(tr("打开文件失败!"));return;}
ui->labelStatus->setText(tr("连接中……"));tcpClient->connectToHost(ui->hostEdit->text(),ui->portEdit->text().toInt());
}

接下来写建立连接成功槽函数,在函数中发送文件头,包含了总传输数据量和文件名称等信息:

void Client::startTransfer()
{//注意QDataStream是写操作的时候,需要传递缓冲区指针QDataStream out(&outBlock, QIODevice::WriteOnly);//发送和接收版本号要一致out.setVersion(QDataStream::Qt_5_9);//得到去掉路径的文件名QString currentFileName =fileName.right(fileName.size() -fileName.lastIndexOf('/') - 1);//前面保留出总字节数和文件名字节数。out << qint64(0) << qint64(0) << currentFileName;totalBytes = localFile->size() + outBlock.size();//得到文件名占用字节数,注意QDataStream有其单独的数据结构//所以文件名占用的字节数不能使用currentFileName.size()获得。qint64 fileNameSize = outBlock.size() - sizeof(qint64) * 2;out.device()->seek(0);out << totalBytes << fileNameSize;//先将文件头写出去bytesToWrite = totalBytes - tcpClient->write(outBlock);
}

接下来在发送完成槽函数中将文件全部传输完毕,并且更新进度条:

void Client::sendFile(qint64 numSend)
{bytesWritten += numSend;if (bytesToWrite > 0){outBlock = localFile->read(qMin(bytesToWrite, payloadSize));bytesToWrite -= tcpClient->write(outBlock);}//更新进度条ui->progressBar->setMaximum(totalBytes);ui->progressBar->setValue(bytesWritten);if (bytesWritten == totalBytes){localFile->close();tcpClient->close();ui->labelStatus->setText("发送文件成功!");}
}

最后在网络错误槽函数中处理错误信息

void Client::displayError(QAbstractSocket::SocketError)
{qDebug() << tcpClient->errorString();tcpClient->close();localFile->close();ui->sendButton->setEnabled(true);
}

设计服务器端界面

在工程名上点击右键选择Add New,如下图所示:

选择Qt,Qt界面设计师类,如下图所示:

然后选择Widget,下一步,选择类名的时候改成Server,如下图所示:

下一步,完成。然后在窗体设计界面中添加一个进度条,一个标签,一个按钮,将标签文本修改成“正在监听localhost的6666端口”,将按钮文本修改成“开始监听”。如下图所示:

编写服务器端代码

首先在头文件server.h中添加包含的头文件和类声明:

#include <QAbstractSocket>
#include <QTcpServer>class QTcpSocket;
class QFile;

然后在Server类中声明必要的成员函数与槽函数:

//进行网络通讯和传输文件所需变量
private:QTcpServer tcpServer;QTcpSocket* tcpConnection;//保存客户端连接QFile* localFile;qint64 fileNameSize;qint64 bytesReceived;qint64 totalBytes;QString fileName;private slots:void newConnection();//客户端建立连接槽函数void readyRead();//端口数据可读槽函数void displayError(QAbstractSocket::SocketError);//网络错误处理函数void on_pushButton_clicked();

修改Server.c文件
首先包含必要头文件:

#include <QtNetwork>
#include <QFile>
#include <QDebug>

在构造函数中关联新建连接槽函数:

connect(&tcpServer, &QTcpServer::newConnection, this, &Server::newConnection);

接下来实现新建连接槽函数,实现读数据槽函数,错误处理函数,以及单击按钮槽函数:

void Server::newConnection()
{//保存连接实例tcpConnection = tcpServer.nextPendingConnection();//关联读数据槽函数connect(tcpConnection, &QTcpSocket::readyRead, this, &Server::readyRead);//关联网络错误槽函数connect(tcpConnection, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(displayError(QAbstractSocket::SocketError)));fileNameSize = 0;totalBytes = 0;bytesReceived = 0;//已经成功和客户端建立连接,可以关闭端口tcpServer.close();
}void Server::readyRead()
{if (bytesReceived <= sizeof(qint64) * 2){//先接收头部信息,获取总数居字节数和文件名QDataStream in(tcpConnection);in.setVersion(QDataStream::Qt_5_9);//先获取总数居个数和文件名大小if (!fileNameSize &&tcpConnection->bytesAvailable() >= sizeof(qint64)*2){in >> totalBytes >> fileNameSize;bytesReceived += sizeof(qint64) * 2;}else{return;}//获取文件名if (fileNameSize &&tcpConnection->bytesAvailable() >= fileNameSize){in >> fileName;localFile = new QFile(fileName);if (!localFile->open(QFile::WriteOnly)){qDebug() << localFile->errorString();tcpConnection->close();ui->label->setText(tr("写文件错误"));return;}bytesReceived += fileNameSize;}elsereturn;}if (bytesReceived < totalBytes){//从网络缓冲区读取数据bytesReceived += tcpConnection->bytesAvailable();QByteArray inBlock = tcpConnection->readAll();//写入文件localFile->write(inBlock);}//更新进度条ui->progressBar->setMaximum(totalBytes);ui->progressBar->setValue(bytesReceived);if (bytesReceived == totalBytes){//传输完成localFile->close();tcpConnection->close();ui->label->setText(tr("接收文件%1成功!").arg(fileName));ui->pushButton->setEnabled(true);}
}void Server::displayError(QAbstractSocket::SocketError)
{qDebug() << tcpConnection->errorString();ui->label->setText(tr("网络错误:%1").arg(tcpConnection->errorString()));tcpConnection->close();
}void Server::on_pushButton_clicked()
{if (!tcpServer.listen(QHostAddress::LocalHost, 6666)){qDebug() << tcpServer.errorString();ui->label->setText(tr("监听失败!"));}ui->label->setText(tr("正在监听localhost的6666端口"));ui->progressBar->setValue(0);ui->pushButton->setEnabled(false);
}

将服务器和客户端对话框同时显示

修改“main.cpp”文件如下所示:

#include "client.h"
#include "server.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);Client w;w.show();Server s;s.show();return a.exec();
}

至此,大功告成,可以到我的资源里面下载源程序。
QT编写的TCP服务端和客户端传输文件的源程序

使用QT编写TCP服务器与客户端程序相关推荐

  1. Qt 零基础设计实现TCP服务器和客户端上位机(零基础实战详解,附源码文件)

    文章目录 TCP和UDP TCP的三次握手和四次分手 TCP和UDP的区别 关于Socket(套接字) Qt TCP服务器的设计与实现 使用Qt的 帮助 TCP服务器和客户端 区别 UI界面设计和原则 ...

  2. QT网络编程——TCP服务器和客户端通信

    目录 一.服务器端 1.QT中TCP服务器的开发思路 2.QT服务器界面设计 3.QT服务器代码实现 二.客户端 1.QT中TCP客户端的开发思路 2.QT客户端界面设计 3.QT客户端代码实现 网络 ...

  3. 【TCP服务器和客户端的简单编写】

    TCP服务器和客户端的简单编写 TCP简介 TCP是什么 传输控制协议(TCP,Transmission Control Protocol)是一种面向连接的.可靠的.基于字节流的传输层通信协议,由IE ...

  4. 【lwip】第二篇:使用lwip的netconn接口编写TCP服务器

    一.netconn介绍   参考链接:https://lwip.fandom.com/wiki/Netconn_API   netconn API 是一个顺序API,旨在使lwip协议栈更易于使用(与 ...

  5. Qt的Tcp服务器多线程编程-附带代码展示

    Qt的Tcp服务器多线程编程-附带代码展示 该程序主要实现tcp服务器如何使用多线程的方式来连接多个客户端,此文章没有实现客户端的多线程编程. 创建子线程时需要注意的点: 1.子线程与主线程之间交互数 ...

  6. daytime协议的服务器和客户端程序,用Socket套接字实现DAYTIME协议的服务器和客户端程序-20210726002244.doc-原创力文档...

    一.设计目的 为了提高同学的自主动手能力,把理论知识运用于实 践中,从实践中更好的领悟所学的知识. 二.题目要求及需求分析 1.网络I/O程序设计:用Socket套接字实现DAYTIME 协 议的服务 ...

  7. daytime协议的服务器和客户端程序,用Socket套接字实现DAYTIME协议的服务器和客户端程序-20210414073352.docx-原创力文档...

    用Socket套接字实现 DAY TIME 协议的服务器和客户端程序 一.设计目的 为了提高同学的自主动手能力,把理论知识运用于实 践中,从实践中更好的领悟所学的知识. 二.题目要求及需求分析 网络I ...

  8. daytime协议的服务器和客户端程序,用Socket套接字实现DAYTIME协议的服务器和客户端程序.doc...

    用Socket套接字实现DAYTIME协议的服务器和客户端程序.doc 一. 设计目的 为了提高同学的自主动手能力,把理论知识运用于实践中,从实践中更好的领悟所学的知识 . 二. 题目要求及需求分析 ...

  9. daytime协议的服务器和客户端程序,用socket套接字实现daytime协议服务器和客户端程序.doc...

    文档介绍: 用socket套接字实现daytime协议服务器和客户端程序.doc一.设计目的为了提高同学的自主动手能力,把理论知识运用于实践中,从实践中更好的领悟所学的知识.二.题目要求及需求分析1. ...

最新文章

  1. 【Qt】Qt动态库和静态库的创建和使用
  2. 01_字符串处理-----04_在文本中应用ZIpf定律
  3. C# MD5加密工具方法
  4. 设置Golang的GOPATH
  5. 计算机二本生不学编程还能干什么6,不建议二本考生报考的6个专业,除非真的感兴趣!...
  6. VTK:可视化之VisualizeVTP
  7. java正则表达式性能_译:Java 中的正则表达式性能概述
  8. C语言——小型图书管理系统(课程设计)
  9. c#连接kafka_c#操作kafka(上)搭建kafka环境
  10. java day02 【数据类型转换、运算符、方法入门】
  11. Origin软件使用TIPS
  12. ubuntu20.04 桌面文件和文件夹图标 丢失解决
  13. Win10+Android+夜神安卓模拟器 搭建ReactNative开发环境
  14. 本周内外盘行情回顾2022.2.27
  15. 送书 | 《数据产品经理:实践进阶》
  16. 高仿人人网客户端安卓源码
  17. 调研人工智能技术在无人驾驶汽车中的应用
  18. C#关联自定义文件类型到应用程序并实现自动导入
  19. 用java编写博弈树_并行博弈树搜索算法-第7篇 另辟蹊径:其他的博弈树并行搜索算法...
  20. LDA-模型的实现-----吉布斯采样

热门文章

  1. Python中def函数
  2. 【论文阅读】SIGMOD‘19 FITing-Tree: A Data-aware Index Structure
  3. Android——“i 分享”APP开发Day06
  4. Win10无法拖动文件怎么办?Win10系统无法拖动文件的解决方法
  5. Excel制作两级分类级联的一种方法
  6. jieba分词原理 ‖ 词性标注
  7. linux 剪切命令 mv
  8. 中国再生水行业运行格局及投资战略咨询报告2022-2027年新版
  9. Thoughts | Something
  10. const mutable