2012-03-08 54 views
4

我需要從FTP服務器獲取響應消息我正在解決連接問題,因此我使用PHP的ftp_raw函數,該函數允許我將原始ftp命令發送到遠程服務器,並獲取迴應字符串。 (內置PHP FTP命令不返回響應:(STOR命令的正確用戶

this接受的答案,我要送的命令是

PASV 
STOR /local/path/to/file.txt 

而且服務器響應

500 /local/path/to/file.txt: The system cannot find the path specified. 

我自言自語:「當然,遠程主機不知道我的本地文件系統。」我的直覺是我打開一個套接字,指定一個遠程文件名,並且我仍然需要通過管道傳輸數據但是我在搜索文檔中沒有發現任何確鑿的結論

什麼是上傳文件的原始ftp命令的完整集合?在什麼時候,以及如何實際開始將數據發送到遠程服務器?我可以使用從ftp_connect()設置的連接作爲套接字嗎?

+2

您是否花時間結帳[RFC959 - 文件傳輸協議(FTP)](http://tools.ietf.org/html/rfc959)?它寫得很好,非常值得一讀。 – 2012-03-11 19:28:15

回答

15

免責聲明:你應該知道的第一件事是,RFC959寫了一段時間後FTP走紅且仍有基礎上,一些破碎的軟件RFC959發佈之前(以及之後)缺少規範。許多較老的(並且更穩定的)FTP庫對於某些服務器有一些特殊的處理,以確保它以99.9%的時間按照你想要的方式工作。處理FTP協議的擴展尤其更常見。

我的答案的其餘部分假定服務器符合RFC959。

另外請記住,繞過你的FTP客戶端庫的更高級別的請求/響應管理意味着你需要自己重新實現這個庫的一部分。這意味着你應該對規範感到滿意,因爲你需要參考它。在可能的情況下,我會參考適當的部分,以便您可以解決。

在一個案例中,我強烈建議您跳入PHP的FTP客戶端庫來調試問題,而不是自己實現所有這些。這是可能的,你應該真的要求庫輸出它使用的所有命令。儘管如此,我仍會指導您完成幫助診斷問題的程序。


管理FTP數據連接有點痛苦。如果你想支持規範的所有可選部分,它並不像乍看起來那麼簡單。究竟如何傳輸文件主要取決於下列選項的當前狀態:

  1. 數據類型(3.1.1節):文件傳輸通常是最安全和最有效的使用圖像/二進制數據類型。這不是默認設置,某些FTP命令(如目錄列表)要求將其設置爲ASCII,所以請確保您在傳輸前將其設置爲始終爲
  2. 數據結構(第3.1.2節):文件結構通常是你想要的,但是一些較舊的計算機和大型機可能需要轉換,並從該模式。
  3. 傳輸模式(第3.4節):流模式是最常用的,但該塊模式支持恢復中斷的傳輸和壓縮模式沒有什麼意義。
  4. 連接模式(第3.2和3.3):在客戶端或服務器可以建立通過連接到它的對等體的數據連接。這必須通過以下方式進行協商:
    • 默認:客戶端在端口20上偵聽;或
    • 自定義端口:客戶端告訴它的另一個端口上列出的服務器;或
    • 被動模式:客戶端要求在哪個端口服務器監聽。

注重規範,因爲有些配置讓你保持數據連接開放,而其他可能需要將其關閉(例如流模式)。如果數據連接已經打開,那麼您不需要在每次傳輸時重新連接到服務器。


所有這些看起來很複雜,但它只是提供信息。它在調試時可能會派上用場。其實只有兩種流行的方式使用FTP傳輸文件:

  1. 服務器連接到客戶端第二個端口和文件發送使用文件數據結構的圖像(二進制)數據類型。

    1. 配置數據類型(必需):

      TYPE I

    2. 配置的數據結構(可選項,默認值):

      STRU˚F

    3. 配置傳輸模式(可選,默認):

      S模式

    4. 選擇一個可用端口(這可能是有些比你想象的更微妙的),並開始收聽。如果您選擇默認端口(20),請跳過下一步。

      選擇端口,創建套接字,聽選定的端口上。

    5. 告訴我們一個給定的端口上監聽服務器(可選,如果默認端口被選中,但它不會傷害要小心):

      PORT您的公開-IP -address,選擇端口

    6. 告訴服務器即將到來的文件傳輸:

      STOR遠程文件名

    7. 等待來自服務器的傳入連接。

    8. 發送文件內容

      打開文件,發送內容,關閉文件

    9. 關閉數據連接

      關閉套接字。

  2. 客戶端連接到所述服務器的第二端口上和所述文件發送在利用文件數據結構的圖像(二進制)的數據類型。

    1. 配置數據類型(必需):

      TYPE I

    2. 配置的數據結構(可選項,默認值):

      STRU˚F

    3. 配置傳輸模式(可選,默認):

      S模式

    4. 告訴我們一個給定的端口上偵聽的服務器(必需):

      PASV

    5. 閱讀PASV c ommand響應包含服務器正在監聽的IP地址和端口號。

    6. 告訴服務器即將到來的文件傳輸:

      STOR遠程文件名

    7. 建立連接:

      連接到服務器上的IP地址和端口號碼PASV迴應

    8. 發送文件內容

      打開文件,發送內容,關閉文件

    9. 關閉數據連接

      關閉套接字。

有一些不方便的問題與第一種方法,因爲選擇一個端口是有點棘手(使用一個端口後,您需要再次使用它之前等待一小段時間)和您防火牆或ISP可能會阻塞某些端口上的傳入連接等。第二種方法最簡單,應該是首選的,除非服務器拒絕它。

+1

這是如何回答這個問題的?海報的具體問題是關於傳遞a/local_path /文件名而不是文件名 – ChrisO 2014-10-27 19:14:38

+0

@ChrisO:實際上,如果直到最後讀到提問者的帖子,問題是「什麼是上傳文件的完整的原始ftp命令集?我真的開始向遠程服務器發送數據了嗎?我可以使用從ftp_connect()設置的連接作爲套接字嗎?「。我很明確地回答了前兩個問題,第三個答案是隱含的(不,你不能使用相同的連接)。 – 2014-10-28 17:08:27

+0

我明白了。我應該再次閱讀海報的問題。 – ChrisO 2014-10-28 22:54:08

2

工作解決方案(使用PORT完整重寫以前的解決方案)。正確的順序是

PASV 
Server responds with something like 
"227 Entering Passive Mode (127,0,0,1,30,235)" 
STOR /path/on/remote/server/foo.txt 
=> Now we have to connect to socket 30*256+235 on 127.0.0.1 and send the data. 
Done 

代碼

$fp = ftp_connect("127.0.0.1", 21, 10) or die("foo"); 
ftp_login ($fp, "anonymous", "password"); 
ftp_raw_send_file($fp, "/local/path/to/file.txt", "foo/foo.txt"); 


function ftp_raw_send_file($fp, $localfile, $remotefile) { 

    $connect = ftp_raw($fp, "PASV"); 

    // parse the response and build the IP and port from the values 
    if (count($connect) > 0 && preg_match("/.*\((\d+),(\d+),(\d+),(\d+),(\d+),(\d+)\)/", $connect[0], $m)) { 
    $address="{$m[1]}.{$m[2]}.{$m[3]}.{$m[4]}"; 
    $port=$m[5] * 256 + $m[6]; 

    print_r(ftp_raw($fp, "STOR $remotefile")); 

    $sock = socket_create(AF_INET, SOCK_STREAM, 0); 
    if ($sock) { 
     socket_connect($sock, $address, $port); 
     socket_write($sock, file_get_contents($localfile)); 
     socket_close($sock); 
    } 
    } 

} 
+0

我想你需要再次檢查[RFC959](http://tools.ietf.org/html/rfc959)。你不能在同一次傳輸中使用*'* PASV'和'PORT'(最有可能的是,以秒爲單位將覆蓋第一個)。另外,當客戶端發出'PASV'命令時,服務器監聽連接(和「充當服務器」)。 'PORT'命令做相反的事情,它告訴服務器連接到指定端口的客戶端。 – 2012-03-11 19:49:40

+0

謝謝。我已經閱讀並發現PASV是一種不需要監聽端口的方式,因此通常更容易。 – Adam 2012-03-11 20:57:56