ruby – 为什么我将“200 Type设置为I.(Net :: FTPReplyError)”
注意:我在同一个.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将连接置于图像(二进制)模式 当块执行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的实现,那么,我们看到当给出一个块时,从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 源代码片段取自 更新:2015年9月13日 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |