Qt5.10編寫FTP客戶端
來源 https://zhuanlan.zhihu.com/p/35314228
自從Qt5刪除了QFtp模塊后,就沒有了可方便使用的FTP類。根據官方的說法,是因為該模塊實現質量不好被刪除,而用Qt5的網絡模塊就可以輕松實現。對於初學者沒了現成的工具就不知道該咋辦了。本文從FTP協議開始講起,先明白FTP協議是一個怎樣的結構。然后講解如何用代碼去實現。
全文分兩大部分,第一部分是FTP協議的講解,第二部分是Qt5的代碼實現。筆者會盡量用簡潔明了的語句給列為講清楚的,讓我們開始吧~~
- FTP協議
- Qt5的代碼實現
第一部分 FTP協議
什么是FTP協議?
就是傳文件用的協議。整個過程就是先用TCP與服務器建立連接,然后發送命令,服務器執行並進行反饋。
FTP的兩個端口
一般的客戶端/服務器之間是開一個Socket端口,既傳輸命令又傳輸數據。而FTP的協議中說:我要開兩個端口,一個傳輸命令,一個傳輸數據。
- 傳輸命令端口(21)
- 傳輸數據端口(20)
所以在編程的時候要創建兩個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'
- ABOR:中斷數據連接程序
- ACCT <account>:系統特權帳號
- ALLO <bytes>:為服務器上的文件存儲器分配字節
- APPE <filename>:添加文件到服務器同名文件
- CDUP <dir path>:改變服務器上的父目錄
- CWD <dir path>:改變服務器上的工作目錄
- DELE <filename>:刪除服務器上的指定文件
- HELP <command>:返回指定命令信息
- LIST <name>:如果是文件名列出文件信息,如果是目錄則列出文件列表
- MODE <mode>:傳輸模式(S=流模式,B=塊模式,C=壓縮模式)
- MKD <directory>:在服務器上建立指定目錄
- NLST <directory>:列出指定目錄內容
- NOOP:無動作,除了來自服務器上的承認
- PASS <password>:系統登錄密碼
- PASV:請求服務器等待數據連接
- PORT <address>:IP 地址和兩字節的端口 ID
- PWD:顯示當前工作目錄
- QUIT:從 FTP 服務器上退出登錄
- REIN:重新初始化登錄狀態連接
- REST <offset>:由特定偏移量重啟文件傳遞
- RETR <filename>:從服務器上找回(復制)文件
- RMD <directory>:在服務器上刪除指定目錄
- RNFR <old path>:對舊路徑重命名
- RNTO <new path>:對新路徑重命名
- SITE <params>:由服務器提供的站點特殊參數
- SMNT <pathname>:掛載指定文件結構
- STAT <directory>:在當前程序或目錄上返回信息
- STOR <filename>:儲存(復制)文件到服務器上
- STOU <filename>:儲存文件到服務器名稱上
- STRU <type>:數據結構(F=文件,R=記錄,P=頁面)
- SYST:返回服務器使用的操作系統
- TYPE <data type>:數據類型(A=ASCII,E=EBCDIC,I=binary)
- USER <username>:系統登錄的用戶名
FTP響應碼
- 110:新文件指示器上的重啟標記
- 120:服務器准備就緒的時間(分鍾數)
- 125:打開數據連接,開始傳輸
- 150:打開連接
- 200:成功
- 202:命令沒有執行
- 211:系統狀態回復
- 212:目錄狀態回復
- 213:文件狀態回復
- 214:幫助信息回復
- 215:系統類型回復
- 220:服務就緒
- 221:退出網絡
- 225:打開數據連接
- 226:結束數據連接
- 227:進入被動模式(IP 地址、ID 端口)
- 230:登錄因特網
- 250:文件行為完成
- 257:路徑名建立
- 331:要求密碼
- 332:要求帳號
- 350:文件行為暫停
- 421:服務關閉
- 425:無法打開數據連接
- 426:結束連接
- 450:文件不可用
- 451:遇到本地錯誤
- 452:磁盤空間不足
- 500:無效命令
- 501:錯誤參數
- 502:命令沒有執行
- 503:錯誤指令序列
- 504:無效命令參數
- 530:未登錄網絡
- 532:存儲文件需要帳號
- 550:文件不可用
- 551:不知道的頁類型
- 552:超過存儲分配
- 553:文件名不允許
第二部分 FTP的Qt5的代碼實現
網絡編程三步驟: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 slotConnected()槽
void MainWindow::slotConnected()
{
ui->textEdit->append("已建立Socket連接");
}
- void dataReceived()槽
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協議很簡單吧~
使用Linux自帶工具實現定時下載FTP文件(4月8日新增)
總共分兩步驟:編寫下載腳本、添加定時任務。
編寫下載腳本
#!/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