Vampor 2020-04-22
来源 https://zhuanlan.zhihu.com/p/35314228
自从Qt5删除了QFtp模块后,就没有了可方便使用的FTP类。根据官方的说法,是因为该模块实现质量不好被删除,而用Qt5的网络模块就可以轻松实现。对于初学者没了现成的工具就不知道该咋办了。本文从FTP协议开始讲起,先明白FTP协议是一个怎样的结构。然后讲解如何用代码去实现。
全文分两大部分,第一部分是FTP协议的讲解,第二部分是Qt5的代码实现。笔者会尽量用简洁明了的语句给列为讲清楚的,让我们开始吧~~
什么是FTP协议?
就是传文件用的协议。整个过程就是先用TCP与服务器建立连接,然后发送命令,服务器执行并进行反馈。
FTP的两个端口
一般的客户端/服务器之间是开一个Socket端口,既传输命令又传输数据。而FTP的协议中说:我要开两个端口,一个传输命令,一个传输数据。
所以在编程的时候要创建两个QTcpSocket哦~
FTP的两种过程
分为被动模式和主动模式。这是从客户端的角度来说的。比如常见的被动模式意思就是服务器告诉你我们用哪个端口进行数据传输,我们(客户端)没有指定端口的权力,它(服务器)说用啥端口我们(客户端)就用啥端口。
而主动模式就是我们(客户端)自己定好用哪个端口,告诉了服务器,然后双方在这个端口下进行通信。
根据上文所述,既然都“被动”了,那我们客户端就等着服务器给我们分配端口号吧。
(1)首先我们任意打开一个端口N连接FTP服务器,并在Socket中写“PASV”命令。这里的N必须大于1024;
(2)然后服务器会返回个信息,类似“227 Entering Passive Mode(47,94,99,120,39,18)”这样。前四个是服务器IP地址,后两个是和分配的端口号相关的数。从这条信息我们就知道了服务器给我们分配的用于数据传输的端口号是:39*256+18;
(3)然后我们就打开第二个端口N+1和服务器的这个39*256+18端口进行连接,然后数据传输。
根据上文所述,既然都“主动”了,那我们客户端就自由分配进行数据传输的端口号吧。
(1)第一步还是任意打开两个端口N和N+1,先用N端口连接FTP服务器的21端口,同时第二个端口N+1进行监听。在第一个端口发送“port N+1”命令;
(2)这时服务器就会主动连接到我们的N+1端口上了。
FTP命令及响应码
FTP命令
命令字符串结尾要加‘\n‘
FTP响应码
网络编程三步骤:Socket连接、Write/Read读写、Close关闭
一般的服务器都是采用被动模式,所以先讲解这部分。
1/3 Socket连接
/* 构造Socket,并连接 */ socket = new QTcpSocket(this); connect(socket,&QTcpSocket::connected,this,&MainWindow::slotConnected); connect(socket,&QTcpSocket::readyRead,this,&MainWindow::dataReceived); socket->connectToHost("47.94.99.120",21);
有两个槽,slotConnected()槽的作用是如果Socket和服务器建立连接,就在textEdit上显示“已建立Socket连接”的字样;dataeceived()槽的作用是显示服务器反馈回来的信息。
void MainWindow::slotConnected() { ui->textEdit->append("已建立Socket连接"); }
void MainWindow::dataReceived() { while (socket->bytesAvailable()) { /* 读取Socket并存入datagram */ QByteArray datagram = socket->readAll(); /* 展示在textEdit */ QString str = QString::fromLocal8Bit(datagram); ui->textEdit->append(str); } }
看下效果:
通过连接,服务器反馈回“220 (vsFTPd 3.0.2)”的信息。由上文所述,220响应码的含义是“服务就绪”。那么接下来就是write/read过程了。
2/3 Write/Read读写
“发送命令”按钮的代码:
void MainWindow::on_pbSend_clicked() { QByteArray command = ui->lineEdit->text().toLatin1(); command += ‘\n‘; socket->write(command); }
注意:命令结尾不要忘了加个‘\n‘回车符号。
发送 USEuser_name 用户名
发送 PASS xxxxxxx 密码
发送 PASV 命令
到此为止,服务器给我们回复“227 Entering Passive Mode(47,94,99,120,39,16)”的响应码,这是要告诉我们“应该采用被动模式”,后面还附带了服务器IP地址,以及两个变量。通过计算得知,服务器给我们分配的数据传输端口号是:39*256+16=10000。
接下来就是再构造一个Socket,端口号为10000,去连接服务器的20端口号(传输数据用)。然后各种命令,接受的文件也用Socket的read()等函数去接受,然后QFile各种存文件操作等等等等。
接下来的步骤就不写了,FTP协议属于应用层的部分,具体如何按照这个标准去组织语言就是列位发挥的部分了。
刚开始我写FTP协议的时候摸不着头脑,也不懂HTTP协议、FTP协议等内容,后来经过摸索才知道:哦,这就是应用层的东西。人家都已经告诉你如何去实现FTP的过程了,我们就照着这个过程写程序就是咯。
所以整个过程还是很简单的,就是用Socket去你来我往的交流而已,没有涉及多么高大上的东西。FTP协议很简单吧~
总共分两步骤:编写下载脚本、添加定时任务。
编写下载脚本
#!/bin/bash ftp -n << EOF open xxx.xxx.xxx.xxx #FTP服务器地址 user username password #用户名、密码 binary lcd /home/psx/data #设置文件保存位置 prompt mget * close bye EOF
其中,prompt意思是关闭交互。
添加定时任务(每分钟执行一次)
打开终端输入:
crontab -e
编写内容:
*/1 * * * * /bin/bash /home/psx/download_ftp
download_ftp是我自己写的脚本,你改成自己的脚本路径。*号和空格不能少。然后按ESC,按shift+:,输入wq保存退出即可立即生效。
关于crontab参数含义可以网上搜索下看看,非常简单。
=========== End