功能简介:
该系统通过在网络环境下,客户端在网络模块下连接到服务器,使用文件打开的方式上传文件至服务器。服务器端通过网络模块监听客户端相同端口传输过来的文件,并以文件的形式写入到服务器端。
客户端实现
- 首先,该系统是在网络模块下运行的,因此需要注意在工程(*.pro)文件下添加
QT += core gui network
- 其次,客户端使用的是
QTcpSocket *myClient
,通过文件对话框的方式QFileDialog
选取文件,使用QFile *myFile
进行读取文件信息并发送至服务器 - 使用进度条的方式显示当前传送的进度
totalSize=0;//文件总的大小在初始化的时候为0
sendSize=0;//文件当前已经发送的大小在初始化的时候为0
- 绑定连接按钮
使用myClient->connectToHost()
连接服务器地址和服务器端口
使用手动绑定的方式,绑定客户端myClient对象的连接信号,使用自定义槽处理 - 绑定槽
实现槽doProcessConnected()
,当客户端连接到服务器时候,对应的槽处理显示连接成功的自定义标志信息并展示在客户端的信息展示栏
【注意】连接成功之后将连接按钮关闭,否则在监听的同时再进行监听当前地址会发生冲突 - 绑定文件上传选择按钮
- 获取文件
使用文件对话框QFileDialog::getOpenFileName()
打开文件,返回当前文件的路径字符串,如果用户取消选择会返回空字符串。 - 打开文件
为QFile对象设置获取的文件名,使用文件对象以只读的方式打开创建 - 进度条显示设置
清空进度条(设置进度条的值为0,文件总的大小以及发送的大小都为0)
获取文件大小(设置进度条的滚动)通过读取文件的大小size设置进度条的范围,并将文件大小的信息展示在客户端的信息展示栏 - 发送文件头给服务器
使用QFileInfo info(filename);
获取文件名,文件大小,并将信息写入至服务器
myClient->waitForBytesWritten();
//等待对方收完当前发送的数据再进行下一次的写,确保每一次都能够完全写成功(服务器完全收到了) - 边读文件边发数据
当打开的文件没有读到文件尾,就一直以每次2k的大小进行读文件
将读取的文件塞给服务器,并获取发送给服务器的文件的大小,以此来设置进度条的值同步
myClient->waitForBytesWritten();
//等待对方收完当前发送的数据再进行下一次的写,确保每一次都能够完全写成功(服务器完全收到了)
文件读到末尾(发送完毕),关闭打开的文件 - 使用文件总的大小与已经发送的大小进行比对,检查文件上传是否成功
//widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QTcpSocket>
#include<QFileDialog>
#include<QFile>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_btn_connect_clicked();
void doProcessConnected();
void on_btn_fileUpload_clicked();
private:
Ui::Widget *ui;
QTcpSocket *myClient;
QFile *myFile;
quint64 totalSize;
quint64 sendSize;
void Init();
};
#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"
#define MSG_LEN 1024*2
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
Init();
}
Widget::~Widget()
{
delete ui;
}
void Widget::Init()
{
totalSize=0;
sendSize=0;
myClient=new QTcpSocket(this);
myFile=new QFile(this);
}
//slots
void Widget::on_btn_connect_clicked()
{
QString servAddres=ui->lineEdit_Server->text();
QString servPort = ui->lineEdit_Server_Port->text();
//连接服务器
myClient->connectToHost(servAddres,servPort.toUInt());
connect(myClient,SIGNAL(connected()),
this,SLOT(doProcessConnected()));
}
void Widget::doProcessConnected()
{
QString msg = "服务器连接成功!";
ui->textEdit_Server->append(msg);
ui->btn_connect->setEnabled(false);
}
void Widget::on_btn_fileUpload_clicked()
{
//打开文件
QString filename = QFileDialog::getOpenFileName(this,"上传文件:",
"C:/Users/gys/Pictures","video(*.mp4)");
// qDebug()<<filename;
if(filename.isEmpty())
{
return;
}
QString msg = QString("准备发送[%1]文件").arg(filename);
ui->textEdit_Server->append(msg);
myFile->setFileName(filename);
bool ret = myFile->open(QIODevice::ReadOnly|QIODevice::Unbuffered);
if(!ret)
{
return;
}
//设置进度条清空全局值
ui->progressBar->setValue(0);
this->totalSize=0;
this->sendSize=0;
//获取文件大小(设置进度条的滚动)
this->totalSize = myFile->size();
ui->progressBar->setRange(0,totalSize);
msg=QString("文件总大小为:%1").arg(totalSize);
ui->textEdit_Server->append(msg);
//发送头给服务器 filename#totalsize
QFileInfo info(filename);
msg = QString("%1#%2#").arg(info.fileName()).arg(this->totalSize);
myClient->write(msg.toUtf8());
myClient->waitForBytesWritten();//等待对方收完当前发送的数据再进行下一次的写,确保每一次都能够完全写成功(服务器完全收到了)
//准备边读文件边发数据
quint64 len;
while(!myFile->atEnd())
{
QByteArray ba = myFile->read(MSG_LEN);//读取文件2K
len = myClient->write(ba);//将读取的文件塞给服务器
this->sendSize += len;//设置进度条的同步
ui->progressBar->setValue(this->sendSize);
myClient->waitForBytesWritten();//等待对方收完当前发送的数据再进行下一次的写,确保每一次都能够完全写成功(服务器完全收到了)
}
myFile->close();
if(this->totalSize==this->sendSize)
{
msg=QString("文件上传成功!");
}
else
{
msg=QString("文件上传失败[totalSize:%1 sendSize:%2]")
.arg(this->totalSize)
.arg(this->sendSize);
}
ui->textEdit_Server->append(msg);
}
//end slots
服务器实现
- 首先,服务器端也是在网络模块下运行的,因此需要注意在工程(*.pro)文件下添加
QT += core gui network
- 其次,初始化“接收到的文件数据大小”与”文件总的大小“。
- 服务器连接按钮事件
- 服务器对象监听输入框中对应的IP和Port,通过监听返回的标志判断是否监听成功,并展示对应的信息于状态信息展示栏。
- 设置服务器的最大连接数
myServer->setMaxPendingConnections(MAXNUM);
- 通过绑定槽、信号方式检测服务器新的信号接入
- 处理槽
doProcessNewConnection()
,
- 服务器使用的是
QTcpServer *myServer;
,通过绑定信号槽的方式检测具有相同(IP,Port)的连接接入信号; - 当检测到新的接入信号时,通过
QTcpSocket *client;
套接字的方式使服务器获取到客户端的描述符。client = myServer->nextPendingConnection();//获取到客户端的描述符
,并将连入客户端的IP地址和端口号展示在服务器的信息展示栏,通过槽的方式准备读取客户端传送过来的信号connect(client,SIGNAL(readyRead()),this, SLOT(doProcessReadyRead()));
- 处理槽
doProcessReadyRead()
,
- 服务器通过
QTcpSocket
套接字对象进行读取每次传送过来的数据 - 通过当前传送的文件数据大小判断,当第一次接收到数据的时候,获取客户端发送过来的文件名及需要上传文件的大小,动态增加接收到的文件大小
- 打开文件
为QFile对象设置获取的文件名,使用文件对象以只写的方式打开创建,通过返回值检测文件是否创建并打开成功,当文件创建成功,展示当前正在接收的文件并设置进度条的最大/小值。 - 之后每一次接收到的文件,都将其写入打开的文件中,并更新接收到的文件大小
- 每次通过网络套接字接收到数据之后,都更新一下进度条,通过判断文件总大小与当前传送的大小是否相等,若相等,即文件接收成功,将打开的文件关闭,更新进度条的值为0
//widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include<QTcpServer>
#include<QTcpSocket>
#include<QFile>
#include<QDebug>
#include<QMessageBox>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_btn_bind_clicked();
void doProcessNewConnection();
void doProcessReadyRead();
private:
Ui::Widget *ui;
QTcpServer *myServer;
QTcpSocket *client;
qint64 recvTotal;
qint64 allTotal;
QFile *myFile;
void Init();
};
#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"
#define MAXNUM 100
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
Init();
}
Widget::~Widget()
{
delete ui;
}
void Widget::Init()
{
recvTotal=0;//当前传输的文件数据
allTotal=0;//文件总的大小
myServer = new QTcpServer(this);
myFile = new QFile(this);
}
//slots
void Widget::on_btn_bind_clicked()
{
QString myAddr;
//手动设置IP
myAddr=ui->lineEdit_Server->text();
QString myPort=ui->lineEdit_Server_Port->text();
bool ret = myServer->listen(QHostAddress(myAddr),myPort.toUInt());
QString msg;
if(!ret)
{
msg="绑定失败!";
}
else
{
msg="绑定成功!";
ui->btn_bind->setEnabled(false);
}
ui->textEdit_Server->append(msg);
myServer->setMaxPendingConnections(MAXNUM);
connect(myServer,SIGNAL(newConnection()),
this,SLOT(doProcessNewConnection()));
}
void Widget::doProcessNewConnection()
{
client = myServer->nextPendingConnection();//获取到客户端的描述符
// arryClients.append(client);
QString msg=QString("客户端[%1:%2]连入!")
.arg(client->peerAddress().toString())
.arg(client->peerPort());//对方的IP地址和d端口号
ui->textEdit_Server->append(msg);
//使用槽读取内容
connect(client,SIGNAL(readyRead()),this,
SLOT(doProcessReadyRead()));
}
void Widget::doProcessReadyRead()
{
QByteArray ba = client->readAll();
//第一次接收数据
if(this->recvTotal==0)
{
this->allTotal =0;
//获取客户端发送过来的文件名及需要上传文件的大小
//filename#totalsize
QStringList list = QString(ba).split("#");
QString filename = list.at(0);
this->allTotal = QString(list.at(1)).toLongLong();
this->allTotal += ba.length();
this->recvTotal += ba.length();
//打开文件
myFile->setFileName(filename);
int ret = myFile->open(QIODevice::WriteOnly|QIODevice::Truncate);
if(!ret)
{
this->recvTotal = 0;
QMessageBox::warning(this,"warning","file can't open!");
return;
}
QString msg=QString("正在接收文件%1:").arg(filename);
ui->textEdit_Server->append(msg);
ui->progressBar->setRange(0,allTotal);
}
else
{
qint64 len = myFile->write(ba);
this->recvTotal+=len;
}
//更新进度条
ui->progressBar->setValue(this->recvTotal);
//判断是否接收完成
if(this->recvTotal == this->allTotal)
{
myFile->close();
this->recvTotal=0;
this->allTotal=0;
QString msg="接收文件成功!";
ui->textEdit_Server->append(msg);
}
}
// end slots