赞
踩
文件传输协议是包含于应用层中,在服务器端与客户端通信过程中需要两条链路的支持:一个是能够在服务器端和客户端之间传递数据文件的命令请求的链路,另一个是能够实现服务器端和客户端之间数据文件的处理的链路。 FTP使用TCP进行高速运输服务,具有可靠性,同时能够减小或消除在不同系统环境下传输文件的不确定性和不兼容性,从而体现它的时效性。文件传输协议依赖的是服务器进程和客户端进程,其中服务器进程能够实现为两个或两个以上的客户端进程提供数据传输请求服务。在文件传输过程中,服务器端的组成分为两类:一个是主要的进程,功能是接收来自客户端新的请求并对该请求进行处理和响应;另一个是次要的进程,主要负责处理客户端靠后的文件数据请求。客户端的组成也分为两部分:一个是主要的进程,负责发送和服务器连接的数据请求;另一个是次要的进程,负责等待与服务器连接之后的单个靠后的文件数据请求。
文件传输需要服务器端和客户端之间的相互配合才能实现发送。在服务器端定义一个定时器实现延时发送,客户端发送文件数据传输的请求,服务器端处理并响应文件数据请求,接着回馈给客户端,接收确认信息,实现与FTP服务器程序相连,并将请求结果发送到客户端。例如,开发用户者利用客户端发出请求命令,要求服务器向开发用户传输一个视频文件,服务器会接受请求并响应这条请求命令,读取视频文件中的内容信息,然后将该文视频件发送到客户端上并设置进度条观察文件传输的进度,最后将传输的文件存放在开发用户者创建的目录中。
(1)先在serverfile.h里面定义监听套接字和通信套接字以及文件对象、大小、名字,最重要的就是定义一个定时器;
QTcpServer *tcpServer; //监听套接字
QTcpSocket *tcpSocket; //通信套接字
QFile file; //文件对象
QString fileName; //文件名字
qint64 fileSize; //文件大小
qint64 sendSize; //已经发送文件的大小
QTimer timer; //定时器
(2)在serverfile.cpp中利用监听套接字进行监听并分配对象,利用listen函数识别端口号8888;通过通信套接字获取对方IP地址和端口号;
tcpServer = new QTcpServer(this);//监听套接字
tcpServer->listen(QHostAddress::Any, 8888);
//获取对方的ip和端口
QString ip = tcpSocket->peerAddress().toString();
quint16 port = tcpSocket->peerPort();
(3)在UI界面选择按钮,在选择的按钮中建立槽函数。通过“选择文本”的按钮建立的槽函数选择对应路径下的视频文件,如果选择的文件路径有效则进行获取视频文件的名字和大小;通过“发送文件”的按钮建立的槽函数将选择的视频文件发送并发送文件的大小;
QString filePath = QFileDialog::getOpenFileName(this, "open", "../");
if(false == filePath.isEmpty()) //如果选择文件路径有效
{
fileName.clear();
fileSize = 0;
//获取文件信息
QFileInfo info(filePath);
fileName = info.fileName(); //获取文件名字
fileSize = info.size(); //获取文件大小
sendSize = 0; //发送文件的大小
}
(4)在通信套接字中进行write写入时需要将其编码为Utf-8,否则中文会乱码,再通过调用定时器延时20ms后启动定时器开始发送;
qint64 len = tcpSocket->write( head.toUtf8() );
if(len > 0)
{
//定时器延时 20 ms
timer.start(20);
}
(5)定义一个buf数组设置每次发送数据的的大小,调用read函数实现往文件中读数据,在利用write函数将读取的数据进行发送,读多少,发多少;
//每次发送数据的大小
char buf[4*1024] = {0};
len = 0;
//往文件中读数据
len = file.read(buf, sizeof(buf));
//发送数据,读多少,发多少
len = tcpSocket->write(buf, len);
(6)通过if循环语句判断文件是否发送完毕,如果发送完毕后就打印“文件发送完毕”的信息,并利用disconnectFromHost函数和close函数主动断开与客户端之间的连接。
if(QString(buf) == "file done")
{//文件接收完毕
ui->textEdit->append("文件发送完毕");
file.close();
//断开客户端端口
tcpSocket->disconnectFromHost();
tcpSocket->close();
}
服务器在传输文件的过程中需要注意的首先在槽函数中要定义文件头,防止TCP黏包,同时起到加密作用,还有就是必须使用定时器,不然文件传输不能成功。
(1)和服务器端一样首先在clientfile.h里面定义通信套接字和文件对象、大小、名字。定义方法和服务器中定义的一样;
(2)同样需要获取IP和端口号,在lineEdit文本框内输入对应网络下的IP和定义的端口号,利用connectToHost函数主动和服务器连接;
(3)使用connect函数进行连接,并提示是否连接成功,在textEdit部件中打印“准备就绪,等待服务器传送文件……”的信息;
connect(tcpSocket, &QTcpSocket::connected,
[=]()
{
//提示连接成功
ui->textEdit->clear();
ui->textEdit->append("准备就绪,等待服务器传送文件……");
});
(4)在客户端进行判断是否接收来自服务器发送的文件,如果是的话就将视频文件的文件名和内存大小显示出来并把接收的信息返回给服务器;
(5)设置进度条,观察文件传输情况,如果传输完成,进度条会加载到100%;
isStart = true;
//设置进度条
ui->progressBar->setValue(0);
(6)通过循环判断文件是否传输完成,如果传输完成首先在Qt运行结果上打印“file done”,同时给服务器端发送接收完成的信息并弹出一个可视化小窗口提示“文件接收完成”的信息,最后关闭文件并利用disconnectFromHost函数和close函数主动断开连接。
if(recvSize == fileSize)
{
tcpSocket->write("file done");
ui->progressBar->setValue(fileSize/1024); //最大值
QMessageBox::information(this, "完成", "文件接收完成");
file.close(); //关闭文件
ui->progressBar->setValue(0); //当前值
//断开连接
tcpSocket->disconnectFromHost();
tcpSocket->close();
}
在客户端接收文件过程中需要注意的是必须通过使用标志位isStart进行循环判断是否能够接收,如果可以能够进行接收,那么标志位isStart就设置为false,防止第二次重复的接收,否则就通过使用标志位isOK进行循环判断并调用disconnectFromHost函数和close函数进行连接的断开和客户端的关闭。
界面上包含五部分,两个文本框,三个按钮。Read文本框是将客户端中写入的信息读取并显示在文本框中,Write文本框是在服务器端进行数据写入,方便客户端进行数据读取。三个按钮为发送、传输文件和断开连接。发送按钮实现的是将写入的信息发送至客户端;传输文件的按钮实现的是进入传送文件的界面,在这个界面中实现的是视频文件的传输;断开连接的按钮实现的是与客户端连接的断开。
服务器端的可视化窗口
客户端的可视化窗口
输入的IP号为“192.168.1.108”,为了区别于视频文件数据的传输这里的端口号设置为“9999”,点击“start connect”按钮,在服务器端和客户端的Read文本框内就会显示“Succefful Connection with Server”的信息。
在服务器端文件传输窗口上只有一个文本框用来显示视频文件路径和传输结果,在客户端文件传输窗口上有输入IP和端口号的文本框,一个“Ready to receive”的按钮,一个进行显示接收的文件名字和大小的文本框,还有一个进度条进行文件传输进度的显示。
clientfile.cpp
#include "clientfile.h" #include "ui_clientfile.h" #include <QDebug> #include <QMessageBox> #include <QHostAddress> ClientFile::ClientFile(QWidget *parent) : QWidget(parent), ui(new Ui::ClientFile) { ui->setupUi(this); tcpSocket = new QTcpSocket(this); isStart = true; ui->progressBar->setValue(0); //当前值 setWindowTitle("客户端"); connect(tcpSocket, &QTcpSocket::connected, [=]() { //提示连接成功 ui->textEdit->clear(); ui->textEdit->append("准备就绪,等待服务器传送文件……"); } ); connect(tcpSocket, &QTcpSocket::readyRead, [=]() { //取出接收的内容 QByteArray buf = tcpSocket->readAll(); if(true == isStart) { isStart = false; fileName = QString(buf).section("##",0,0); fileSize = QString(buf).section("##",1,1).toInt(); recvSize = 0; file.setFileName(fileName); bool isOk = file.open(QIODevice::WriteOnly); if(false == isOk) { qDebug()<<"WriteOnly error 49"; tcpSocket->disconnectFromHost(); tcpSocket->close(); return; } QString str = QString("接收的文件: [%1: %2kb]").arg(fileName).arg(fileSize/1024); //QMessageBox::information(this, "文件信息", str); ui->textEdit->setText(str); ui->progressBar->setMinimum(0); //最小值 ui->progressBar->setMaximum(fileSize/1024); //最大值 ui->progressBar->setValue(0); //当前值 } else { qDebug()<<"123456"; qint64 len = file.write(buf); if(len > 0) { recvSize += len; //累计接收大小 qDebug() << len; } ui->progressBar->setValue(recvSize/1024); if(recvSize == fileSize) { //先给服务发送(接收文件完成的信息) tcpSocket->write("file done"); ui->progressBar->setValue(fileSize/1024); //最大值 QMessageBox::information(this, "完成", "文件接收完成"); file.close(); //关闭文件 ui->progressBar->setValue(0); //当前值 //断开连接 tcpSocket->disconnectFromHost(); tcpSocket->close(); } } } ); } ClientFile::~ClientFile() { delete ui; } void ClientFile::on_ButtonConnect_clicked() { //获取服务器的ip和端口 QString ip = ui->lineEditIP->text(); quint16 port = ui->lineEditPort->text().toInt(); //主动和服务器连接 tcpSocket->connectToHost(QHostAddress(ip), port); isStart = true; //设置进度条 ui->progressBar->setValue(0); } void ClientFile::on_pushButton_2_clicked() { emit mySignal(); }
severfile.cpp
#ifndef CLIENTFILE_H #define CLIENTFILE_H #include <QWidget> #include <QTcpSocket> #include <QFile> namespace Ui { class ClientFile; } class ClientFile : public QWidget { Q_OBJECT public: explicit ClientFile(QWidget *parent = 0); ~ClientFile(); private slots: void on_ButtonConnect_clicked(); void on_pushButton_2_clicked(); signals: void mySignal(); private: Ui::ClientFile *ui; QTcpSocket *tcpSocket; QFile file; //文件对象 QString fileName; //文件名字 qint64 fileSize; //文件大小 qint64 recvSize; //已经接收文件的大小 bool isStart; //标志位,是否为头部信息 }; #endif // CLIENTFILE_H
感谢大家关注点赞!!!!!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。