加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

ruby – 为什么我将“200 Type设置为I.(Net :: FTPReplyError)”

发布时间:2020-12-17 03:49:45 所属栏目:百科 来源:网络整理
导读:注意:我在同一个.rb文件中有两个代码块(见下文).第一次ftp.getbinaryfile()工作,然后它抛出错误. 注意:该文件变量是文件的静态路径,仅用于调试目的. 我有这个代码在ruby 2.0.0p481(2014-05-08)[x64-mingw32] file = "/Filetrack/E-mail_Gateway/_Installer
注意:我在同一个.rb文件中有两个代码块(见下文).第一次ftp.getbinaryfile()工作,然后它抛出错误.

注意:该文件变量是文件的静态路径,仅用于调试目的.

我有这个代码在ruby 2.0.0p481(2014-05-08)[x64-mingw32]

file = "/Filetrack/E-mail_Gateway/_Installer/GA/E-mail Gateway_10.0_Changes_PUBLIC.pdf"

list = ftp.list('*')
list.each{|item| 

  counter=counter+1
  counter++
  ftp.getbinaryfile(file,where_to_save+File.basename(file)+counter.to_s,1024)
  puts "downloaded - .each used"
}

然后在同一个.rb文件中我得到了这段代码

ftp.list('*') { |item| 
  puts "downloading using .list('*') {"
  counter++
  ftp.getbinaryfile(file,1024)

  puts "downloaded #{file}"
}

那段代码抛出了这个错误

Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:974:in `parse227': 200 Type set to I. (Net::FTPReplyError)
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:394:in `makepasv'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:406:in `transfercmd'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:487:in `block (2 levels) in retrbinary'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:199:in `with_binary'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:485:in `block in retrbinary'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/monitor.rb:211:in `mon_synchronize'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:484:in `retrbinary'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:617:in `getbinaryfile'

ftp会话由.创建

ftp = Net::FTP.new('ftp.***.***.net')
ftp.passive = false
ftp.debug_mode = true
ftp.login(ftp_username,ftp_password)

有人可以解释为什么第二个版本有效吗?

UPDATE

添加了ftp调试日志:

put: USER r.***
get: 331 Password required for r.***.
put: PASS ************
get: 230-Welcome to FTP
get: 230 User r.****logged in.
put: TYPE I
get: 200 Type set to I.
put: CWD /Filetrack/E-mail_Gateway/_Installer/GA/010_000_003_000/
get: 250 CWD command successful.
put: TYPE A
get: 200 Type set to A.
put: PASV
get: 227 Entering Passive Mode (194,212,10,23,195,92).
put: LIST *
get: 125 Data connection already open; Transfer starting.
get: 226 Transfer complete.
put: TYPE I
get: 200 Type set to I.
put: PASV
get: 227 Entering Passive Mode (194,93).
put: RETR /Filetrack/E-mail_Gateway/_Installer/GA/010_000_003_000/E-mail Gateway_10.0_Changes_PUBLIC.pdf
get: 125 Data connection already open; Transfer starting.
get: 226 Transfer complete.
downloaded - .each used
put: TYPE A
get: 200 Type set to A.
put: PASV
get: 227 Entering Passive Mode (***,***,97).
put: LIST *
get: 125 Data connection already open; Transfer starting.
downloading using .list('*') {
put: TYPE I
get: 226 Transfer complete.
put: PASV
get: 200 Type set to I.
put: TYPE A
get: 227 Entering Passive Mode (***,98).
put: TYPE I
get: 200 Type set to A.
d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:974:in `parse227': 200 Type set to I. (Net::FTPReplyError)
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:394:in `makepasv'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:406:in `transfercmd'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:487:in `block (2 levels) in retrbinary'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:199:in `with_binary'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:485:in `block in retrbinary'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/monitor.rb:211:in `mon_synchronize'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:484:in `retrbinary'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:617:in `getbinaryfile'
        from download2 - debugging.rb:41:in `block in <main>'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:518:in `block (3 levels) in retrlines'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:515:in `loop'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:515:in `block (2 levels) in retrlines'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:199:in `with_binary'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:512:in `block in retrlines'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/monitor.rb:211:in `mon_synchronize'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:511:in `retrlines'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:760:in `list'
        from download2 - debugging.rb:38:in `<main>'

UPDATE2

如果使用ftp.passive = false,则记录日志

downloading using .list('*') {
put: TYPE I
get: 226 Transfer complete.
put: PORT ***,20,102,235,136
get: 200 Type set to I.
put: RETR /Filetrack/E-mail_Gateway/_Installer/GA/010_000_003_000/Email Gateway_10.0_Changes_PUBLIC.pdf
get: 200 PORT command successful.
put: TYPE A
put: TYPE I
d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/protocol.rb:211:in `write': An existing connection was forcibly closed by the remote host. (Errno::ECONNRESET)
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/protocol.rb:211:in `write0'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/protocol.rb:185:in `block in write'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/protocol.rb:202:in `writing'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/protocol.rb:184:in `write'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:283:in `putline'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:360:in `block in voidcmd'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/monitor.rb:211:in `mon_synchronize'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:359:in `voidcmd'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:183:in `send_type_command'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:172:in `binary='
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:201:in `ensure in with_binary'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:201:in `with_binary'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:512:in `block in retrlines'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/monitor.rb:211:in `mon_synchronize'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:511:in `retrlines'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:760:in `list'
        from download2 - debugging.rb:39:in `<main>'

UPDATE3

我尝试在ftp活动模式下运行相同的代码几次,实际上所有文件都已下载,但脚本完成时出错.

downloading using .list('*') {
put: TYPE I
get: 200 Type set to I.
put: PORT **,**,197,73
get: 200 PORT command successful.
put: RETR /Filetrack/E-mail_Gateway/_Installer/GA/010_000_003_000/E-mail Gateway_10.0_Changes_PUBLIC.pdf
get: 150 Opening BINARY mode data connection for /Filetrack/E-mail_Gateway/_Installer/GA/010_000_003_000/E-mail Gateway_10.0_Changes_PUBLIC.pdf(
60911 bytes).
get: 226 Transfer complete.
put: TYPE A
get: 200 Type set to A.
downloaded /Filetrack/E-mail_Gateway/_Installer/GA/010_000_003_000/E-mail Gateway_10.0_Changes_PUBLIC.pdf
put: TYPE I
get: 200 Type set to I.
d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/protocol.rb:158:in `rescue in rbuf_fill': Net::ReadTimeout (Net::ReadTimeout)
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/protocol.rb:152:in `rbuf_fill'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/protocol.rb:134:in `readuntil'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:1108:in `readline'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:289:in `getline'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:300:in `getmultiline'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:318:in `getresp'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:338:in `voidresp'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:526:in `block (2 levels) in retrlines'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:199:in `with_binary'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:512:in `block in retrlines'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/monitor.rb:211:in `mon_synchronize'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:511:in `retrlines'
        from d:/prog/Ruby200-x64/lib/ruby/2.0.0/net/ftp.rb:760:in `list'
        from download2 - debugging.rb:39:in `<main>'

解决方法

“200 Type”设置为“I.(Net :: FTPReplyError)”的原因

您的连接使用PASSIVE模式.由于您没有显示创建FTP对象的代码部分,我将假设您明确将模式设置为被动.

ftp = Net::FTP.new('example.com')
ftp.passive = true

基于异常的堆栈跟踪,可以看到当方法makepasv发出PASV命令时发生问题,而不是得到227进入被动模式(194,93)的响应,它得到一个响应200 Type设置为I.

makepasv和parse227的实现(参考后面的参考文献1和参考文献2)表明代码专门查找227的返回代码,如果不是这种情况,则会抛出FTPError.

这是给定方案中发生的情况.

为什么`parse227`收到错误的回复?

这可以归因于下面显示的语法.
这可能是不太了解的行为(正如我自己在回答这篇文章的过程中发现的那样)

ftp.list('*') { |item| 
  ftp.getbinaryfile(file,1024)
}

在上面的代码中,LIST *命令由ftp.list(‘*’)发出.此命令的典型响应如下:

put: LIST *
get: 125 Data connection already open; Transfer starting.
get: 226 Transfer complete.

可以看出,LIST *产生两行结果.这一事实对于理解这个问题至关重要.

传递给ftp.list(‘*’)的块使用getbinaryfile方法下载二进制文件.

getbinaryfile通常会发出以下命令:

> TYPE I将连接置于图像(二进制)模式
> PASV进入被动模式
> RETR / path / of / file / to / download

当块执行ftp.list(‘*’)的第一个结果,并开始发出与getbinaryfile相关的命令时,在那个时间点,只读取了LIST *的第一行响应 – 第二行尚未到被阅读.这是第二行,显示为对块中发出的下一个命令的响应.

因此,当发出第一个命令TYPE I时,代码读取LIST *的第二行作为响应(如调试日志中所示)

put: TYPE I
get: 226 Transfer complete.

发出第二个命令PASV时,代码读取TYPE I的响应(从调试日志中可以看出)

put: PASV
get: 200 Type set to I.

makepasv的实现是这样的,它希望响应的响应代码为227(参见参考文献1和参考文献2中的第394和973行).在这种情况下抛出了引发Net :: FTPReplyError的异常,因为parse227传递了TYPE I命令的响应.

总之,当使用被动模式时,似乎在给出给ftp.list(‘*’)的块中执行其他FTP操作是不可行的.

为什么`ftp.list(‘*’).each`工作?

在这种情况下,ftp.list(‘*’)在没有块的情况下被调用,因此它返回字符串数组作为输出.在该阵列上使用每个都不会产生类似的情况 – 因此,没有观察到任何问题.

似乎FTP #list的作者希望以下两个变体以相同的方式工作:

ftp.list('*') { |f|  }  # block given to list
ftp.list('*').each { |f|  } # block given to enum returned by list

根据列表API的official documentation:

list(*args) { |line| … }

Returns an array of file information in the
directory (the output is like ls -l). If a block is given,it
iterates through the listing.

如果我们查看list的实现,那么,我们看到当给出一个块时,从ftp.list(‘*’)读取的每一行都逐一产生到块.当使用被动模式时,如果块尝试执行任何其他FTP命令,则会导致上述情况.

754     def list(*args,&block) # :yield: line
755       cmd = "LIST"
756       args.each do |arg|
757         cmd = cmd + " " + arg.to_s
758       end
759       if block
760         retrlines(cmd,&block)
761       else
762         lines = []
763         retrlines(cmd) do |line|
764           lines << line
765         end
766         return lines
767       end
768     end

我们可以通过将实现改为等同于ftp.list(‘*’)来解决这个问题.每个变体首先将LIST *响应中的所有行收集到一个数组中,如果给出一个块,则将该数组传递给该块.我们仍将坚持API文档.

def list(*args,&block) # :yield: line
  cmd = "LIST"
  args.each do |arg|
    cmd = cmd + " " + arg.to_s
  end

  # First lets fetch all the lines
  lines = []
  retrlines(cmd) do |line|
    lines << line
  end

  if block
    lines.each { |l| yield l }
  else
    return lines
  end
end

我已经报告了bug in Ruby Bug Tracker建议在FTP#list方法的实现上面的变化.

参考文献1 – makepasv的实现

391     # sends the appropriate command to enable a passive connection
392     def makepasv # :nodoc:
393       if @sock.peeraddr[0] == "AF_INET"
394         host,port = parse227(sendcmd("PASV"))
395       else
396         host,port = parse229(sendcmd("EPSV"))
397         #     host,port = parse228(sendcmd("LPSV"))
398       end
399       return host,port
400     end

参考2 – parse227的实现

968     # handler for response code 227
969     # (Entering Passive Mode (h1,h2,h3,h4,p1,p2))
970     #
971     # Returns host and port.
972     def parse227(resp) # :nodoc:
973       if resp[0,3] != "227"
974         raise FTPReplyError,resp
975       end
976       if m = /((?<host>d+(,d+){3}),(?<port>d+,d+))/.match(resp)
977         return parse_pasv_ipv4_host(m["host"]),parse_pasv_port(m["port"])
978       else
979         raise FTPProtoError,resp
980       end
981     end

源代码片段取自ftp.rb.

更新:2015年9月13日
The proposed change已经由Ruby核心团队accepted针对这个问题.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读