2012-03-08 56 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