被动模式下FTP的详细工作过程

Posted on

被动模式下FTP的详细工作过程 - 大浪淘沙 - 51CTO技术博客

分享到

百度分享

51CTO首页51CTO博客我的博客 搜索 每日博报

社区:学院论坛博客下载更多

登录注册

[

大浪淘沙

](http://yuanbin.blog.51cto.com)

http://yuanbin.blog.51cto.com 【复制】 【订阅】

原创:29翻译:0转载:8

博 客|图库|写博文|帮 助

yuanbin0710 的BLOG

写留言邀请进圈子发消息 加友情链接进家园 加好友 2012年度IT博客大赛 十大杰出IT博客诞生

博客统计信息

51CTO推荐博客 用户名:yuanbin0710 文章数:37 评论数:166 访问量:279930 无忧币:20 博客积分:1338 博客等级:4 注册日期:2008-04-17

热门专题

更多>>

RHEL6从零基础到熟练使用 阅读量:8542

VMware5.5从零开始学起 阅读量:3326 Hyper-V 3.0功能部署 阅读量:2014

Windows Server入门教程 阅读量:4726

热门文章

我的技术圈(2)

更多>>

nj_ka..

huan108

doget

gaoti..

lonrn

wei13..

deeps..

champ..

flyer..

sht19..

浩临天下

登徒浪子

最新评论

更多>>

友情链接

·【征文】聊IT运维标准化与安全,奖50元京东购物卡·iOS企业级开发初级课程-UIView与控件(共20集 )

博主的更多文章>>

被动模式下FTP的详细工作过程 2008-10-25 20:03:53

标签:FTP 休闲 被动模式 职场 工作过程 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://yuanbin.blog.51cto.com/363003/108009

文中有不对或者有不清楚的地方,请大家告诉我,谢谢!


被动模式下FTP的详细工作过程

PASV FTP在工作的第一步,与PORT FTP一样,会首先使用21端口建立控制连接;但在第二步,由FTP客户端主动发起建立数据传输连接请求,服务器的数据传输端口为随机端口,因此,每次建立的数据传输连接通道,服务器所使用的端口都不相同。

本示例客户端IP是192.168.120.168,服务器IP是192.168.120.240,因为客户端的MS-DOS不支持PASV模式,所以使用FlashFXP软件来连接服务器。通过抓包工具来查看用户从连接FTP服务器到退出FTP服务器经过的所有过程。 使用FlashFXP所做的操作:1.输入FTP服务器的IP,要登入的用户名和密码。2.进入用户家目录,查看.bashrc和.bash_profile两个文件。3.退出FTP。

PASV FTP**的详细工作过程:**

1. 因为FTP使用的是TCP协议,所以客户端FlashFXP在连接服务器192.168.120.240时,首先会经历TCP的三次握手来建立控制通道。客户端使用任意的端口N(>1024)来连接FTP服务器默认的21端口。这和PORT模式一样。 2. 在TCP三次握手结束后,服务器端正式响应客户端的控制连接请求,控制通道建立。

3. 客户端向服务器发送含有ACK的数据段来确认控制连接建立并发送用户名。 4. 服务器向客户端发送含有ACK的数据段来确认用户名。

5. 服务器向客户端询问密码。 6. 客户端向服务器发送含有ACK的数据段来确认并发送密码,密码为明文。

7. 服务器向客户端发送含有ACK的数据段来确认密码收到。 8. 服务器向客户端发送登陆成功的信息。

9. 客户端向服务器发送含有ACK的数据段来确认并发送查询系统类型的指令(见图一)。 10. 服务器向客户端发送含有ACK的数据段来确认收到指令。

11. 服务器向客户端回应系统的类型为UNIX(见图二)。 12. 客户端向服务器发送列出服务器的所有扩展命令和扩展功能的指令(见图三)。

13. 服务器响应客户端的FEAT请求(见图四)。 14. 服务器响应客户端有EPSV(扩展PASV,支持非IPV4)的扩展功能(见图五)。

15. 客户端向服务器发送含有ACK的数据段来确认收到信息。 16. 服务器响应客户端自己的其他特性(见图六)。MDTM:保留下载文件的日期/时间;REST STREAM:重设文件传输方式为stream形式。

17. 客户端确认收到服务器的信息。 18. 服务器响应客户端进入到用户的家目录(见图七)。

19. 客户端确认收到服务器的信息。 20. 客户端向服务器发送文件传输使用何种模式(Binary、ASCII)的指令(见图八)。

21. 服务器回应客户端使用Binary模式(见图九)。 22. 客户端向服务器询问.bashrc文件的大小(见图十)。

23. 服务器回应客户端.bashrc文件的大小为124字节(见图十一)。 24. 客户端向服务器发出PASV的指令(用来进行数据传输)(见图十二)。

25. 服务器回应客户端使用PASV模式,并且商量数据传输端口用232x256+151=59543(见图十三)。 26. 客户端主动使用N+1端口来连接服务器的59543端口,并且向服务器发送含有SYN的数据段来开始进行数据传输连接的第1次握手(见图十四)。

27. 服务器向客户端发送含有SYN和ACK的数据段来进行第2次握手(见图十五)。 28. 客户端向服务器发送含有ACK的数据段来进行第3次握手(见图十六)。

29. 3次握手过程完成,客户端向服务器发送查看.bashrc文件的指令(见图十七)。 30. 服务器回应客户端使用Binary数据传输模式连接.bashrc文件(见图十八)。

31. 服务器开始进行数据传输。 32. 服务器确定数据传输完毕,然后向客户端发送含有FIN和ACK的数据段来请求断开本次数据连接,第1次断开(见图十九)。

33. 客户端向服务器发送含有ACK的数据段答应服务器的断接请求,第2次断开(见图二十)。 34. 服务器响应客户端文件发送完成的信息(见图二十一)。

35. 客户端向服务器发送含有ACK的数据段来确认收到信息。 36. 客户端向服务器发送含有FIN和ACK的数据段来请求断开客户端到服务器之间的数据连接,第3次断开(见图二十二)。

37. 服务器向客户端发送含有ACK的数据段确认断开连接,第4次断开(见图二十三) 38. 接下来是查看.bash_profile文件的过程。其过程和查看.bashrc文件的过程一样(在此不详述了),只是服务器和客户端的数据传输端口变了。服务器的数据传输端口是随机的;客户端的数据传输端口是N+2。

39. 客户端在查看完.bash_profile文件并彻底完成该次数据连接的4次断开后,向服务器发送退出的指令(见图二十四)。 40. 服务器响应客户端的退出请求(见图二十五)。

41. 服务器向客户端发送含有FIN和ACK的数据段,请求断开控制连接(见图二十六)。这是第一次断开控制连接。 42. 客户端向服务器发送含有ACK的数据段来应答服务器的断接请求(见图二十七)。这是第二次断开控制连接。

43. 客户端向服务器发送含有FIN和ACK的数据段,请求断开到服务器之间的控制连接(见图二十八)。这是第三次断开控制连接。 44. 服务器向客户端发送含有ACK的数据段,来确认客户端的断接请求(见图二十九)。这样本次FTP控制连接完全断开。

通过查看本次示例(被动FTP)的详细过程,我们可以得到以下几点结果:

1. 服务器的控制连接端口为21,数据传输端口随机(>1024)并且每次的数据传输端口都不一样。 2. 客户端的控制连接端口为N(>1024),数据传输端口在第一次进行数据传输时为N+1,在第二次数据传输时为N+2,依次类推。

3. 服务器主动告诉客户端自己的数据传输端口。 4. 客户端主动用数据传输端口N+x去连接服务器的随机数据传输端口。

5. 在一次FTP连接的过程中,只有一次控制连接,却可以有多次数据传输连接。 6. 不管是控制连接还是数据传输连接,在每次从建立到完成都需要经过3次连接和4次断开的过程。(这符合TCP协议的要求)

本文档使用的图例:

11 图一:客户端发送查询系统类型的指令

13

图二:服务器响应客户端系统类型为UNIX

14 图三:客户端请求服务器列出它的所有扩展命令和扩展功能

15

图四:服务器响应客户端的FEAT请求

16 图五:服务器响应客户端有EPSV(扩展PASV,支持非IPV4)的扩展功能

18

图六:服务器响应客户端自己支持的其他特性

20 图七:服务器响应客户端进入到用户家目录

22

图八:客户端向服务器询问文件传输的模式

23 图九:服务器响应客户端使用Binary模式

24

图十:客户端向服务器询问.bashrc文件的大小

25 图十一:服务器回应客户端.bashrc文件的大小为124字节

26

图十二:客户端向服务器发送PASV指令

27 图十三:服务器响应客户端使用PASV模式,并且商量数据传输端口用59543

28

图十四:第一次数据传输连接之第一次握手

29 图十五:第一次数据传输连接之第二次握手

30

图十六:第一次数据传输连接之第三次握手

31 图十七:客户端向服务器发送查看.bashrc文件的指令

32

图十八:服务器响应客户端使用Binary数据传输模式来连接.bashrc文件

34 图十九:服务器发送FIN和ACK请求断开本次数据连接(第1次断开)

35

图二十:客户端发送ACK答应服务器的请求(第2次断开)

36 图二十一:服务器响应客户端文件发送完毕的信息

38

图二十二:客户端向服务器发送含有FIN和ACK的数据段请求断开数据连接(第3次断开)

39 图二十三:服务器向客户端发送含有ACK的数据段确认断开连接(第4次断开)

56

图二十四:客户端向服务器发出退出的指令

57 图二十五:服务器响应客户端的退出请求

58

图二十六:服务器向客户端发送FIN和ACK请求断开控制连接(第1次断开)

59 图二十七:客户端向服务器发送ACK答应服务器请求(第2次断开)

60

图二十八:客户端向服务器发送FIN和ACK请求断开控制连接(第3次断开)

61 图二十九:服务器向客户端发送ACK答应断开控制连接(第4次断开)

62

图三十:本次示例全部过程

本文档完整的图例,请见附件。

本文出自 “大浪淘沙” 博客,请务必保留此出处http://yuanbin.blog.51cto.com/363003/108009 分享至 ** 更多 0 一键收藏,随时查看,分享好友!

cccc0912 1人 了这篇文章 附件下载: FTP被动模式详细工作过程(图例)

类别:Linux Server技术圈(0)┆阅读(2046)┆评论(2) ┆ 推送到技术圈返回首页 上一篇 主动模式下FTP的详细工作过程 下一篇 FTP总结

相关文章

文章评论

[1楼] [匿名]yangming1052 回复

2008-10-27 11:17:13 不错 学习了。。

[2楼] 小芳1213 回复

2010-09-21 08:25:03 很不错噢。

发表评论

周刊:IT行业盲目涨工资比房产泡沫更危险? 昵 称: 登录 快速注册 验证码:

点击图片可刷新验证码请点击后输入验证码博客过2级,无需填写验证码 内 容:

同时赞一个 返回顶部

Copyright By 51CTO.COM 版权所有

附件下载close

请输入验证码: 确认

Validate XML using a XSD

Posted on

Validate XML using a XSD

1、xsd文件源;

SchemaFactory: newSchema(Source schema)

// Parser object is: com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl@c9ba38 Start document: Start element: local name: PHONEBOOK qname: PHONEBOOK uri: Characters: Start element: local name: PERSON qname: PERSON uri: Characters: Start element: local name: NAME qname: NAME uri: Attributes: Name : firstName Type : CDATA Value: Joe Name : lastName Type : CDATA Value: Yin Characters: Joe Characters: Yin End element: local name: NAME qname: NAME uri: Characters: Start element: local name: EMAIL qname: EMAIL uri: Characters: joe@yourserver.com End element: local name: EMAIL qname: EMAIL uri: Characters: Start element: local name: TELEPHONE qname: TELEPHONE uri: Characters: 202-999-9999 End element: local name: TELEPHONE qname: TELEPHONE uri: Characters: Start element: local name: WEB qname: WEB uri: Characters: www.java2s.com End element: local name: WEB qname: WEB uri: Characters: End element: local name: PERSON qname: PERSON uri: Characters: End element: local name: PHONEBOOK qname: PHONEBOOK uri: End document: // import java.io.StringReader; import javax.xml.XMLConstants; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.sax.SAXSource; import javax.xml.validation.SchemaFactory; import org.xml.sax.Attributes; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; public class MainClass { public static **void main(String args[])throws Exception { SAXParserFactory spf = SAXParserFactory.newInstance(); SAXParser parser = null; spf.setNamespaceAware(true); try { SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); spf.setSchema(sf.newSchema(new SAXSource(new InputSource(new StringReader(schemaString))))); parser = spf.newSAXParser(); } catch(SAXException e) { e.printStackTrace(System.err); System.exit(1); } catch(ParserConfigurationException e) { e.printStackTrace(System.err); System.exit(1); } MySAXHandler handler = new MySAXHandler(); System.out.println(schemaString); parser.parse(new InputSource(new StringReader(xmlString)), handler); } static String xmlString = "<?xml version=\"1.0\"?>" + "" + "rtoName" + "FromName" + "Info" + "Message Body" + ""; static String schemaString ="<?xml version=\"1.0\"?>" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + ""; } class MySAXHandler extends DefaultHandler { public **void startDocument() { System.out.println("Start document: "); } public **void endDocument() { System.out.println("End document: "); } public **void startElement(String uri, String localName, String qname, Attributes attr) { System.out.println("Start element: local name: " + localName + " qname: " + qname + " uri: "+uri); int attrCount = attr.getLength(); if(attrCount>0) { System.out.println("Attributes:"); for(int i = 0 ; i<attrCount ; i++) { System.out.println(" Name : " + attr.getQName(i)); System.out.println(" Type : " + attr.getType(i)); System.out.println(" Value: " + attr.getValue(i)); } } } public **void endElement(String uri, String localName, String qname) { System.out.println("End element: local name: " + localName + " qname: " + qname + " uri: "+uri); } public **void characters(char[] ch, int start, int length) { System.out.println("Characters: " + new String(ch, start, length)); } public **void ignorableWhitespace(char[] ch, int start, int length) { System.out.println("Ignorable whitespace: " + new String(ch, start, length)); } public **void startPrefixMapping(String prefix, String uri) { System.out.println("Start \"" + prefix + "\" namespace scope. URI: " + uri); } public **void endPrefixMapping(String prefix) { System.out.println("End \"" + prefix + "\" namespace scope."); } public **void warning(SAXParseException spe) { System.out.println("Warning at line "+spe.getLineNumber()); System.out.println(spe.getMessage()); } public **void fatalError(SAXParseException spe) throws SAXException { System.out.println("Fatal error at line "+spe.getLineNumber()); System.out.println(spe.getMessage()); throw **spe; } }

http://www.java2s.com/Code/JavaAPI/javax.xml.validation/SchemaFactorynewSchemaSourceschema.htm

另外,可参考:

import java.io.IOException; // SAX import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.XMLReader; //SAX and external XSD import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.SchemaFactory; import javax.xml.parsers.ParserConfigurationException; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.InputSource; public class XMLUtils { private XMLUtils() {} // validate SAX and external XSD public static boolean validateWithExtXSDUsingSAX(String xml, String xsd) throws ParserConfigurationException, IOException { try { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setValidating(false); factory.setNamespaceAware(true); SchemaFactory schemaFactory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema"); SAXParser parser = null; try { factory.setSchema(schemaFactory.newSchema(new Source[] {new StreamSource( xsd )})); parser = factory.newSAXParser(); } catch (SAXException se) { System.out.println("SCHEMA : " + se.getMessage()); // problem in the XSD itself return false; } XMLReader reader = parser.getXMLReader(); reader.setErrorHandler( new ErrorHandler() { public void warning(SAXParseException e) throws SAXException { System.out.println("WARNING: " + e.getMessage()); // do nothing } public void error(SAXParseException e) throws SAXException { System.out.println("ERROR : " + e.getMessage()); throw e; } public void fatalError(SAXParseException e) throws SAXException { System.out.println("FATAL : " + e.getMessage()); throw e; } } ); reader.parse(new InputSource(xml)); return true; } catch (ParserConfigurationException pce) { throw pce; } catch (IOException io) { throw io; } catch (SAXException se){ return false; } } public static void main (String args[]) throws Exception{ System.out.println (XMLUtils.validateWithExtXSDUsingSAX ("c:/temp/howto.xml", "c:/temp/howto.xsd")); // output : true // } }

http://www.rgagnon.com/javadetails/java-0669.html

IBM教程上有这个,但是我测试失败:

SAX 示例

清单 2演示了如何使用 JAXP 1.2 中的新特性,通过 SAX 解析器来验证文档。要使用 SAX 解析器来验证文档,请执行以下操作:

  1. 创建一个

SAXParserFactory 对象。

  1. 将 namespace-aware 和 validating 特性设置为 true。
  2. 获取

SAXParser 对象。

  1. 设置模式语言和模式源的特性(这是 JAXP 1.2 和模式中新出现的)。
  2. 解析文档。解析器必须有权访问

ErrorHandler 对象。

清单 2. ValidateSAX.java 演示了 JAXP 1.2

package org.ananas.tips; import java.io./; import org.xml.sax./; import javax.xml.parsers./*; public class ValidateSAX { public static String SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage", XML_SCHEMA = "http://www.w3.org/2001/XMLSchema", SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource"; public final static void main(String[] args) throws IOException, SAXException, ParserConfigurationException { if(args.length < 2) { System.err.println("usage is:"); System.err.println(" java -jar tips.jar -validatesax " + "input.xml schema.xsd"); return; } File input = new File(args[0]), schema = new File(args[1]); SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(true); factory.setValidating(true); SAXParser parser = factory.newSAXParser(); try { parser.setProperty(SCHEMA_LANGUAGE,XML_SCHEMA); parser.setProperty(SCHEMA_SOURCE,schema); } catch(SAXNotRecognizedException x) { System.err.println("Your SAX parser is not JAXP 1.2 compliant."); } parser.parse(input,new ErrorPrinter()); } }

http://www.ibm.com/developerworks/cn/xml/tips/x-tipvalschm/

有时间到话,可以在测试。

列下jaxp 1.2教程:

This release includes XML data and example programs showing how to use JAXP to process XML. Additional examples can be found on the http://xml.apache.org site.

The example programs include a cross-platform ant build file that can be used to build and run the example. Ant is a build tool similar to

make on Unix and

nmake on WindowsNT that is also an XML application. To use ant, download it from thewebsite and read the install docs. Alternatively, you can also view the ant

build.xml file to see what needs to be done to manually compile and run an example program on your platform. Note: The ant utility uses the value of the

JAVA_HOME environment variable to determine which Java platform it uses to compile and run the sample scripts. Make sure that variable points to the version you intend to use. If using version 1.4, make sure that the JAXP jar files are installed, as described in the Release Notes.

Sample XML Files

A handful of sample XML files have been provided in the "samples" subdirectory. Note that the links may not work depending on your browser environment. Please look in ../samples/data if the links do not display in your browser.

book-order.xml ) suggesting how an on-line business might send and receive data.

REC-xml-19980210.xml ) was written in XML. With a prepublication version of its Document Type Definition (DTD) file (

spec.dtd ), it's included here as a rather sophisticated example of how DTDs are used.

  • Courtesy of Fuji Xerox, a short XML file in Japanese, using Japanese tags. This is a weekly report (

weekly-euc-jp.xml ) with its DTD (

weekly-euc-jp.dtd ), which can be validated.

two_gent.xml ) and The Tragedy of Richard the Third (

rich_iii.xml ). These include their DTD (

play.dtd ).

namespace.xml ).

Printing a DOM Tree

One of the first things many programmers want to know is how to read an XML file and generate a DOM Document object from it. Use the DOMEcho example to learn how to do this in three steps. The important lines are: // Step 1: create a DocumentBuilderFactory and setNamespaceAware DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); // Step 2: create a DocumentBuilder DocumentBuilder db = dbf.newDocumentBuilder(); // Step 3: parse the input file to get a Document object Document doc = db.parse(new File(filename));

The program also gives an example of using an error handler and of setting optional configuration options, such as validation. Finally, this program helps you understand how DOM works by showing you the structure and contents of a DOM tree.

SAX Program to Count Tags

The SAXLocalNameCountprogram counts the number of unique element local names in an XML document, ignoring the namespace name for simplicity. This example also shows one way to turn on DTD or XSD validation and how to use a SAX ErrorHandler.

There are several ways to parse a document using SAX and JAXP. We show one approach here. The first step is to bootstrap a parser. There are two ways: one is to use only the SAX API, the other is to use the JAXP utility classes in the javax.xml.parsers package. We use the second approach here because at the time of this writing it probably is the most portable solution for a JAXP compatible parser. After bootstrapping a parser/XMLReader, there are several ways to begin a parse. In this example, we use the SAX API.

Schema Examples

Both of the sample programs include an option (-xsd) that lets you validate the incoming document using XML Schema, instead of the document's DTD. In addition, they include an -xsdss option that lets you specify the "schema source" (the file that defines the schema for the document).

Both programs define the following constants: static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema"; static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";

The schema language property defines the language the schema is written in. The W3C XML Schema language is specified in these examples. The schema source property directs the parser to a schema to use, regardless of any schema pointer that the XML instance document may contain.

This code is abstracted from the SAX example: SAXParserFactory spf = SAXParserFactory.newInstance(); // Set namespaceAware to true to get a parser that corresponds to // the default SAX2 namespace feature setting. This is necessary // because the default value from JAXP 1.0 was defined to be false. spf.setNamespaceAware(true); spf.setValidating(true); SAXParser saxParser = spf.newSAXParser(); // Set the schema language if necessary try { saxParser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA); } catch (SAXNotRecognizedException x) { // This can happen if the parser does not support JAXP 1.2 ... } ... saxParser.setProperty(JAXP_SCHEMA_SOURCE, new File(schemaSource));

And here is the code abstracted from the DOM example:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();     // Set namespaceAware to true to get a DOM Level 2 tree with nodes     // containing namesapce information.  This is necessary because the     // default value from JAXP 1.0 was defined to be false.     dbf.setNamespaceAware(true);     dbf.setValidating(true);     try {        dbf.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);     } catch (IllegalArgumentException x) {         // This can happen if the parser does not support JAXP 1.2         ...     }     // Specify other factory configuration settings    dbf.setAttribute(JAXP_SCHEMA_SOURCE, new File(schemaSource));     ...     DocumentBuilder db = dbf.newDocumentBuilder();

Note that the values are used to modify the SAX parser, using

setProperty(), but they are used to modify the DOM parserfactory, using

setAttribute() .

http://people.apache.org/~edwingo/jaxp-ri-1.2.0-fcs/docs/samples.html

另外一种,用 Validating Parser

Using the Validating Parser

By now, you have done a lot of experimenting with the nonvalidating parser. It's time to have a look at the validating parser to find out what happens when you use it to parse the sample presentation.

You need to understand about two things about the validating parser at the outset:

*

  • A schema or document type definition (DTD) is required.

  • Because the schema or DTD is present, the

ignorableWhitespace method is invoked whenever possible.

Configuring the Factory

The first step is to modify the Echo program so that it uses the validating parser instead of the nonvalidating parser.

Note: The code in this section is contained in

Echo10.java .

To use the validating parser, make the following highlighted changes: public static void main(String argv[]) { if (argv.length != 1) { ... } // Use the default (non-validating) parser // Use the validating parser SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setValidating(true); try { ...

Here, you configure the factory so that it will produce a validating parser when

newSAXParser is invoked. To configure it to return a namespace-aware parser, you can also use

setNamespaceAware(true) . Sun's implementation supports any combination of configuration options. (If a combination is not supported by a particular implementation, it is required to generate a factory configuration error.)

Validating with XML Schema

Although a full treatment of XML Schema is beyond the scope of this tutorial, this section shows you the steps you take to validate an XML document using an existing schema written in the XML Schema language. (To learn more about XML Schema, you can review the online tutorial, XML Schema Part 0: Primer, at

http://www.w3.org/TR/xmlschema-0/ . You can also examine the sample programs that are part of the JAXP download. They use a simple XML Schema definition to validate personnel data stored in an XML file.)

Note: There are multiple schema-definition languages, including RELAX NG, Schematron, and the W3C "XML Schema" standard. (Even a DTD qualifies as a "schema," although it is the only one that does not use XML syntax to describe schema constraints.) However, "XML Schema" presents us with a terminology challenge. Although the phrase "XML Schema schema" would be precise, we'll use the phrase "XML Schema definition" to avoid the appearance of redundancy.

To be notified of validation errors in an XML document, the parser factory must be configured to create a validating parser, as shown in the preceding section. In addition, the following must be true:

*

  • The appropriate properties must be set on the SAX parser.
  • The appropriate error handler must be set.
  • The document must be associated with a schema.

Setting the SAX Parser Properties

It's helpful to start by defining the constants you'll use when setting the properties: static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage"; static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";

Next, you configure the parser factory to generate a parser that is namespace-aware as well as validating: ... SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setNamespaceAware(true); factory.setValidating(true);

You'll learn more about namespaces in Validating with XML Schema. For now, understand that schema validation is a namespace-oriented process. Because JAXP-compliant parsers are not namespace-aware by default, it is necessary to set the property for schema validation to work.

The last step is to configure the parser to tell it which schema language to use. Here, you use the constants you defined earlier to specify the W3C's XML Schema language: saxParser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);

In the process, however, there is an extra error to handle. You'll take a look at that error next.

Setting Up the Appropriate Error Handling

In addition to the error handling you've already learned about, there is one error that can occur when you are configuring the parser for schema-based validation. If the parser is not 1.2-compliant and therefore does not support XML Schema, it can throw a

SAXNotRecognizedException .

To handle that case, you wrap the

setProperty() statement in a

try /

catch block, as shown in the code highlighted here: ... SAXParser saxParser = factory.newSAXParser();try { saxParser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA); } catch (SAXNotRecognizedException x) { // Happens if the parser does not support JAXP 1.2 ...}...

Associating a Document with a Schema

Now that the program is ready to validate the data using an XML Schema definition, it is only necessary to ensure that the XML document is associated with one. There are two ways to do that:

*

  • By including a schema declaration in the XML document

  • By specifying the schema to use in the application

Note: When the application specifies the schema to use, it overrides any schema declaration in the document.

To specify the schema definition in the document, you create XML such as this:

...

The first attribute defines the XML namespace (

xmlns ) prefix,

xsi , which stands for XML Schema instance. The second line specifies the schema to use for elements in the document that do not have a namespace prefix--that is, for the elements you typically define in any simple, uncomplicated XML document.

Note: You'll learn about namespaces in Validating with XML Schema. For now, think of these attributes as the "magic incantation" you use to validate a simple XML file that doesn't use them. After you've learned more about namespaces, you'll see how to use XML Schema to validate complex documents that use them. Those ideas are discussed in Validating with Multiple Namespaces.

You can also specify the schema file in the application: static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource"; ... SAXParser saxParser = spf.newSAXParser(); ...saxParser.setProperty(JAXP_SCHEMA_SOURCE, new File(schemaSource));

Now that you know how to use an XML Schema definition, we'll turn to the kinds of errors you can see when the application is validating its incoming data. To do that, you'll use a document type definition (DTD) as you experiment with validation.

Experimenting with Validation Errors

To see what happens when the XML document does not specify a DTD, remove the

DOCTYPE statement from the XML file and run the Echo program on it.

Note: The output shown here is contained in

Echo10-01.txt . (The browsable version is

Echo10-01.html .)

The result you see looks like this: <?xml version='1.0' encoding='UTF-8'?> // Parsing error, line 9, uri .../slideSample01.xml Document root element "slideshow", must match DOCTYPE root "null"

Note: This message was generated by the JAXP 1.2 libraries. If you are using a different parser, the error message is likely to be somewhat different.

This message says that the root element of the document must match the element specified in the

DOCTYPE declaration. That declaration specifies the document's DTD. Because you don't yet have one, it's value is null. In other words, the message is saying that you are trying to validate the document, but no DTD has been declared, because no

DOCTYPE declaration is present.

So now you know that a DTD is a requirement for a valid document. That makes sense. What happens when you run the parser on your current version of the slide presentation, with the DTD specified?

Note: The output shown here is produced using

slideSample07.xml , as described in Referencing Binary Entities. The output is contained in

Echo10-07.txt . (The browsable version is

Echo10-07.html .)

This time, the parser gives a different error message: // Parsing error, line 29, uri file:... The content of element type "slide" must match "(image?,title,item/*)

This message says that the element found at line 29 (

) does not match the definition of the

element in the DTD. The error occurs because the definition says that the

slide element requires a

title . That element is not optional, and the copyright slide does not have one. To fix the problem, add a question mark to make

title an optional element: <!ELEMENT slide (image?, title?, item/*)>

Now what happens when you run the program?

Note: You could also remove the copyright slide, producing the same result shown next, as reflected in

Echo10-06.txt . (The browsable version is

Echo10-06.html .)

The answer is that everything runs fine until the parser runs into the

tag contained in the overview slide. Because that tag is not defined in the DTD, the attempt to validate the document fails. The output looks like this: ... ELEMENT: CHARS: Overview END_ELM: ELEMENT: CHARS: Why // Parsing error, line 28, uri: ...Element "em" must be declared.org.xml.sax.SAXParseException: ... ...

The error message identifies the part of the DTD that caused validation to fail. In this case it is the line that defines an

item element as

(/#PCDATA | item) .

As an exercise, make a copy of the file and remove all occurrences of

from it. Can the file be validated now? (In the next section, you'll learn how to define parameter entries so that we can use XHTML in the elements we are defining as part of the slide presentation.)

Error Handling in the Validating Parser

It is important to recognize that the only reason an exception is thrown when the file fails validation is as a result of the error-handling code you entered in the early stages of this tutorial. That code is reproduced here: public void error(SAXParseException e) throws SAXParseException { throw e;}

If that exception is not thrown, the validation errors are simply ignored. Try commenting out the line that throws the exception. What happens when you run the parser now?

In general, a SAX parsing error is a validation error, although you have seen that it can also be generated if the file specifies a version of XML that the parser is not prepared to handle. Remember that your application will not generate a validation exception unless you supply an error handler such as the one here.

http://docs.oracle.com/javaee/1.4/tutorial/doc/JAXPSAX9.html

JSch

Posted on

JSch - Java实现的SFTP(文件上传详解篇)

JSch是Java Secure Channel的缩写。JSch是一个SSH2的纯Java实现。它允许你连接到一个SSH服务器,并且可以使用端口转发,X11转发,文件传输等,当然你也可以集成它的功能到你自己的应用程序。

本文只介绍如何使用JSch实现的SFTP功能。 SFTP 是Secure File Transfer Protocol的缩写,安全文件传送协议。可以为传输文件提供一种安全的加密方法。SFTP 为 SSH的一部份,是一种传输文件到服务器的安全方式。SFTP是使用加密传输认证信息和传输的数据,所以,使用SFTP是非常安全的。但是,由于这种传输 方式使用了加密/解密技术,所以传输效率比普通的FTP要低得多,如果您对网络安全性要求更高时,可以使用SFTP代替FTP。(来自百度的解释)

要使用JSch,需要下载它的jar包,请从官网下载它:http://www.jcraft.com/jsch/

ChannelSftp类是JSch实现SFTP核心类,它包含了所有SFTP的方法,如: put(): 文件上传

get(): 文件下载 cd(): 进入指定目录

ls(): 得到指定目录下的文件列表 rename(): 重命名指定文件或目录

rm(): 删除指定文件 mkdir(): 创建目录

rmdir(): 删除目录 等等(这里省略了方法的参数,put和get都有多个重载方法,具体请看源代码,这里不一一列出。)

JSch支持三种文件传输模式:

OVERWRITE 完全覆盖模式,这是JSch的默认文件传输模式,即如果目标文件已经存在,传输的文件将完全覆盖目标文件,产生新的文件。 RESUME 恢复模式,如果文件已经传输一部分,这时由于网络或其他任何原因导致文件传输中断,如果下一次传输相同的文件,

则会从上一次中断的地方续传。 APPEND 追加模式,如果目标文件已存在,传输的文件将在目标文件后追加。

创建ChannelSftp对象

编写一个工具类,根据ip,用户名及密码得到一个SFTP channel对象,即ChannelSftp的实例对象,在应用程序中就可以使用该对象来调用SFTP的各种操作方法。

SFTPChannel.java SFTPChannel.java

SFTPConstants是一个静态成员变量类:

SFTPConstans.java SFTPConstants.java

文件上传

实现文件上传可以调用ChannelSftp对象的put方法。ChannelSftp中有12个put方法的重载方法:

public void put(String src, String dst) 将本地文件名为src的文件上传到目标服务器,目标文件名为dst,若dst为目录,则目标文件名将与src文件名相同。

采用默认的传输模式:OVERWRITE public void put(String src, String dst, int mode) 将本地文件名为src的文件上传到目标服务器,目标文件名为dst,若dst为目录,则目标文件名将与src文件名相同。

指定文件传输模式为mode(mode可选值为:ChannelSftp.OVERWRITE,ChannelSftp.RESUME,

ChannelSftp.APPEND)

public void put(String src, String dst, SftpProgressMonitor monitor)

将本地文件名为src的文件上传到目标服务器,目标文件名为dst,若dst为目录,则目标文件名将与src文件名相同。

采用默认的传输模式:OVERWRITE

并使用实现了SftpProgressMonitor接口的monitor对象来监控文件传输的进度。 public void put(String src, String dst, SftpProgressMonitor monitor, int mode)

将本地文件名为src的文件上传到目标服务器,目标文件名为dst,若dst为目录,则目标文件名将与src文件名相同。

指定传输模式为mode

并使用实现了SftpProgressMonitor接口的monitor对象来监控文件传输的进度。 public void put(InputStream src, String dst) 将本地的input stream对象src上传到目标服务器,目标文件名为dst,dst不能为目录。

采用默认的传输模式:OVERWRITE public void put(InputStream src, String dst, int mode) 将本地的input stream对象src上传到目标服务器,目标文件名为dst,dst不能为目录。

指定文件传输模式为mode public void put(InputStream src, String dst, SftpProgressMonitor monitor)

将本地的input stream对象src上传到目标服务器,目标文件名为dst,dst不能为目录。

采用默认的传输模式:OVERWRITE

并使用实现了SftpProgressMonitor接口的monitor对象来监控传输的进度。 public void put(InputStream src, String dst, SftpProgressMonitor monitor, int mode)

将本地的input stream对象src上传到目标服务器,目标文件名为dst,dst不能为目录。

指定文件传输模式为mode

并使用实现了SftpProgressMonitor接口的monitor对象来监控传输的进度。 public OutputStream put(String dst) 该方法返回一个输出流,可以向该输出流中写入数据,最终将数据传输到目标服务器,目标文件名为dst,dst不能为目录。

采用默认的传输模式:OVERWRITE public OutputStream put(String dst, final int mode) 该方法返回一个输出流,可以向该输出流中写入数据,最终将数据传输到目标服务器,目标文件名为dst,dst不能为目录。

指定文件传输模式为mode public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode) 该方法返回一个输出流,可以向该输出流中写入数据,最终将数据传输到目标服务器,目标文件名为dst,dst不能为目录。

指定文件传输模式为mode

并使用实现了SftpProgressMonitor接口的monitor对象来监控传输的进度。 public OutputStream put(String dst, final SftpProgressMonitor monitor, final int mode, long offset) 该方法返回一个输出流,可以向该输出流中写入数据,最终将数据传输到目标服务器,目标文件名为dst,dst不能为目录。

指定文件传输模式为mode

并使用实现了SftpProgressMonitor接口的monitor对象来监控传输的进度。

offset指定了一个偏移量,从输出流偏移offset开始写入数据。

应用实例:

SFTPTest.java SFTPTest.java

:请分别将代码段1,代码段2,代码段3取消注释,运行程序来进行测试。这三段代码分别演示了如何使用JSch的不同的put方法来进行文件上传。

代码段1:采用向put方法返回的输出流中写入数据的方式来传输文件。 需要由程序来决定写入什么样的数据,这里是将本地文件的输入流写入输出流。采用这种方式的好处是,可以自行设定每次写入输出流的数据块大小,如本示例中的语句: byte[] buff = new byte[1024 /* 256]; // 设定每次传输的数据块大小为256KB

代码段2:直接将本地文件名为src的文件上传到目标服务器,目标文件名为dst。(注:使用这个方法时,dst可以是目录,当dst是目录时,上传后的目标文件名将与src文件名相同)

代码段3:将本地文件名为src的文件输入流上传到目标服务器,目标文件名为dst。

这三段代码实现的功能是一样的,都是将本地的文件src上传到了服务器的dst文件。使用时可根据具体情况选择使用哪种实现方式。

监控传输进度

从前面的介绍中知道,JSch支持在文件传输时对传输进度的监控。可以实现JSch提供的SftpProgressMonitor接口来完成这个功能。

SftpProgressMonitor接口类的定义为: SftpProgressMonitor.java

init(): 当文件开始传输时,调用init方法。

count(): 当每次传输了一个数据块后,调用count方法,count方法的参数为这一次传输的数据块大小。

end(): 当传输结束时,调用end方法。

下面是一个简单的实现:

MyProgressMonitor.java MyProgressMonitor.java

此时如果改变SFTPTest main方法里调用的put方法,即可实现监控传输进度:

SFTPTest.java SFTPTest.java

注意修改的内容仅仅是put方法,在put方法中增加了SftpProgressMonitor的实现类对象monitor作为参数,即添加了对进度监控的支持。

运行,输出结果如下: logs

当然这个SftpProgressMonitor的实现实在太简单。JSch每次传输一个数据块,就会调用count方法来实现主动进度通知。

现在我们希望每间隔一定的时间才获取一下文件传输的进度。。。看看下面的SftpProgressMonitor实现:

复制代码

package

com.longyg.sftp; import java.text.DecimalFormat;import java.util.Timer;import

java.util.TimerTask; import

com.jcraft.jsch.SftpProgressMonitor; public class FileProgressMonitor extends TimerTask implements

SftpProgressMonitor {

private long progressInterval = 5 /* 1000; // 默认间隔时间为5秒

private boolean isEnd = false; // 记录传输是否结束

private long transfered; // 记录已传输的数据总大小

private long fileSize; // 记录文件总大小

private Timer timer; // 定时器对象

private boolean isScheduled = false; // 记录是否已启动timer记时器

public FileProgressMonitor(long

fileSize) { this.fileSize =

fileSize; }

@Override

public void

run() { if (!isEnd()) { // 判断传输是否已结束

        System.out.println("Transfering is in progress."

); long transfered =

getTransfered(); if (transfered != fileSize) { // 判断当前已传输数据大小是否等于文件总大小

            System.out.println("Current transfered: " + transfered + " bytes"

); sendProgressMessage(transfered);

        }

else

{ System.out.println("File transfering is done."

); setEnd(true); // 如果当前已传输数据大小等于文件总大小,说明已完成,设置end

        }
    }else

{ System.out.println("Transfering done. Cancel timer."

); stop();// 如果传输结束,停止timer记时器

return

; }

}

public void

stop() { System.out.println("Try to stop progress monitor."

); if (timer != null

) { timer.cancel();

        timer.purge();
        timer= null

; isScheduled= false

; }

    System.out.println(

"Progress monitor stoped."

); }

public void

start() { System.out.println("Try to start progress monitor."

); if (timer == null

) { timer= new

Timer(); }

    timer.schedule(

this, 1000

, progressInterval); isScheduled= true

; System.out.println("Progress monitor started."

); }

///

 /* 打印progress信息

 /*

@param

transfered /*/

private void sendProgressMessage(long

transfered) { if (fileSize != 0

) { double d = ((double)transfered /* 100)/(double

)fileSize; DecimalFormat df= new DecimalFormat( "/#./#/#"

); System.out.println("Sending progress message: " + df.format(d) + "%"

); }else

{ System.out.println("Sending progress message: " +

transfered); }

}

///

 /* 实现了SftpProgressMonitor接口的count方法

/*/

public boolean count(long

count) { if (isEnd()) return false

; if (!

isScheduled) { start();

    }
    add(count);

return true

; }

///

 /* 实现了SftpProgressMonitor接口的end方法

/*/

public void

end() { setEnd(true

); System.out.println("transfering end."

); }

private synchronized void add(long

count) { transfered= transfered +

count; }

private synchronized long

getTransfered() { return

transfered; }

public synchronized void setTransfered(long

transfered) { this.transfered =

transfered; }

private synchronized void setEnd(boolean

isEnd) { this.isEnd =

isEnd; }

private synchronized boolean

isEnd() { return

isEnd; }

public void init(int op, String src, String dest, long

max) { // Not used for putting InputStream

}

} 复制代码

再次修改SFTPTest main方法里的put方法,改为使用新的SftpProgressMonitor的实现类对象monitor作为参数,注意新的monitor对象的构造函数需要传入文件大小作为参数:

复制代码

package

com.longyg.sftp; import java.io.File;import java.util.HashMap;import

java.util.Map; import

com.jcraft.jsch.ChannelSftp; public class

SFTPTest {

public

SFTPChannel getSFTPChannel() { return new

SFTPChannel(); }

///

 /*@param

args /*@throws

Exception /*/

public static void main(String[] args) throws

Exception { SFTPTest test= new

SFTPTest();

    Map

sftpDetails = new HashMap

(); // 设置主机ip,端口,用户名,密码

    sftpDetails.put(SFTPConstants.SFTP_REQ_HOST, "10.9.167.55"

); sftpDetails.put(SFTPConstants.SFTP_REQ_USERNAME,"root"

); sftpDetails.put(SFTPConstants.SFTP_REQ_PASSWORD,"arthur"

); sftpDetails.put(SFTPConstants.SFTP_REQ_PORT,"22"

);

    String src

= "D:\DevSoft\HB-SnagIt1001.rar"; // 本地文件名

    String dst = "/home/omc/ylong/sftp/HB-SnagIt1001.rar";// 目标文件名


    SFTPChannel channel=

test.getSFTPChannel(); ChannelSftp chSftp= channel.getChannel(sftpDetails, 60000

);

    File file

= new

File(src); long fileSize =

file.length();

///

     /* 代码段1

    OutputStream out = chSftp.put(dst, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); // 使用OVERWRITE模式
    byte[] buff = new byte[1024 /* 256]; // 设定每次传输的数据块大小为256KB

    int read;
    if (out != null) {

        System.out.println("Start to read input stream");
        InputStream is = new FileInputStream(src);

        do {
            read = is.read(buff, 0, buff.length);

            if (read > 0) {
                out.write(buff, 0, read);

            }
            out.flush();

        } while (read >= 0);
        System.out.println("input stream read done.");

    }
    /*/*/




    chSftp.put(src, dst,

new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); //

代码段2

// chSftp.put(new FileInputStream(src), dst, new FileProgressMonitor(fileSize), ChannelSftp.OVERWRITE); // 代码段3

    chSftp.quit();

    channel.closeChannel();
}

} 复制代码

再次运行,结果输出为:

复制代码

Try to start progress monitor.

Progress monitor started. Transfering is in progress.

Current transfered: 98019 bytes Sending progress message: 2.55%

Transfering is in progress. Current transfered: 751479 bytes

Sending progress message: 19.53% Transfering is in progress.

Current transfered: 1078209 bytes Sending progress message: 28.02%

...... Transfering is in progress.

Current transfered: 3430665 bytes Sending progress message: 89.15%

transfering end. Transfering done. Cancel timer.

Try to stop progress monitor. Progress monitor stoped. 复制代码

现在,程序每隔5秒钟才会打印一下进度信息。可以修改FileProgressMonitor类里的progressInterval变量的值,来修改默认的间隔时间。

来源: [http://www.cnblogs.com/longyg/archive/2012/06/25/2556576.html](http://www.cnblogs.com/longyg/archive/2012/06/25/2556576.html)

FTP的主动模式

Posted on

FTP的主动模式

FTP的主动模式(active mode)和被动模式(passive mode):(转)

FTP的主动模式(active mode)和被动模式(passive mode):(转)

2010-03-01 23:57 大多数的TCP服务是使用单个的连接,一般是客户向服务器的一个周知端口发起连接,然后使用这个连接进行通讯。但是,FTP协议却有所不同,它使用双向的多个连接 ,而且使用的端口很难预计。一般,FTP连接包括: 一个控制连接 (control connection) 这个连接用于传递客户端的命令和服务器端对命令的响应。它使用服务器的21端口,生存期是整个FTP会话时间。 几个数据连接 (data connection) 这些连接用于传输文件和其它数据,例如:目录列表等。这种连接在需要数据传输时建立,而一旦数据传输完毕就关闭,每次使用的端口也不一定相同。而且,数据 连接既可能是客户端发起的,也可能是服务器端发起的。 在FTP协议中,控制连接使用周知端口21 ,因此使用ISA的IP PACKET FILTER就可以这种连接进行很好的安全保护。相反,数据传输连接的目的端口通常实现无法知道,因此处理这样的端口转发非常困难。F

TP协议使用一个标准的端口21作为ftp-data端口,但是这个端口只用于连接的源地址是服务器端的情况,在这个端口上根本就没有监听进程。

FTP的数据连接和控制连接的方向一般是相反的,也就是说,是服务器向客户端发起一个用于数据传输的连接。连接的端口是由服务器端和客户端协商确定的。FTP协议 的这个特征对ISA 转发以及防火墙和NAT的配置增加了很多困难。 除此之外,还有另外一种FTP模式,叫做被动模式 (passive mod)。在这种模式下,数据连接是由客户程序发起的,和刚才讨论过的模式(我们可以叫做主动模式 )相反。

是否采取被动模式取决于客户程序,在ftp命令行中使用passive命令就可以关闭/打开被动模式。

iptables**中配置vsftp**

问题:配置iptables后, vsftpd不能用。常见现象是能够登录到服务器,LIST列目录失败(超时)。

分析:

主动模式 下,客户连接 TCP/21,服务器通过 TCP/20 连接客户的随机端口

————这种情况下,通过状态防火墙可以解决 iptables -A INPUT -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT 被动模式 下,客户连接 TCP/21,客户再通过其他端口连接服务器的随机端口

卡住的原因,是因为服务器在被动模式下没有打开临时端口让 client 连过来,因此需要几个条件

1、client 没有防火墙时,用主动模式连接即可 2、server 没有防火墙时,用被动模式即可 3、双方都有防火墙时,vsftpd 设置被动模式高端口范围,server 打开那段范围,client 用被动模式连接即可 4、加载 ip_conntrack_ftp 模块,使 server 支持 connection tracking,支持临时打洞,client 用被动模式即可 5、server 使用 ip_conntrack_ftp、client 使用 ip_conntrack_ftp 和 ip_nat_ftp,支持临时打洞和临时 NAT 穿越打洞,双方使用主动或被动模式均可

  • “打洞”的配置说明:
  • 优点:不影响ftp配置;缺点: 客户会感觉到连接有些延迟。原因参见ip_conntract的实现原理

在/etc/modprobe.conf中添加 alias ip_conntrack ip_conntract_ftp ip_nat_ftp

在/etc/rc.local中添加

/sbin/modprobe ip_conntract /sbin/modprobe ip_conntrack_ftp /sbin/modprobe ip_nat_ftp

这个作用就是完成上面所说的“临时打洞”的方法。

  • 限制被动模式连接端口的配置说明
  • 优点:对连接速度没有影响。缺点: 限制了被动模式下,客户端并发连接的数量。

在/etc/vsftpd/vsftpd.conf中添加 pasv_min_port=2222 pasv_max_port=2225

iptables中开放这段端口

-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -p tcp -m state --state NEW -m tcp --dport 21 -j ACCEPT -A INPUT -p tcp --dport 2222:2225 -j ACCEPT

Apache FTP 实用代码

Posted on

Apache FTP 实用代码

结合网上两个使用apache commons-net包的两个优秀的代码,结合实际,写出了如下的FtpUtil,供参考!

参考网址:http://www.iteye.com/topic/173786http://hi.baidu.com/victorlin23/blog/item/edc62a35dbae8a1a91ef3947.html

package com.pccw.portlet.publictools;

import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.io.OutputStream;

import java.io.RandomAccessFile;

import java.net.SocketException;

import java.util.ArrayList;

import java.util.List;

import org.apache.commons.logging.Log;

import org.apache.commons.logging.LogFactory;

import org.apache.commons.net.ftp.FTP;

import org.apache.commons.net.ftp.FTPClient;

import org.apache.commons.net.ftp.FTPFile;

import org.apache.commons.net.ftp.FTPReply;

///

/* FTP工具类

/*

/* @author WangXianfeng 1:16:50 PM Jul 16, 2011

/*/

public class FtpUtil {

   private FTPClient ftpClient;

   private static Log log = LogFactory.getLog(FtpUtil.class);



   //*/*

    /* 根路径为"/",如果需要链接服务器之后跳转到路径,则在path中定义

    /* @param ftpConfig

    /* @throws SocketException

    /* @throws IOException

    /*/

   public boolean connectServer(FtpConfig ftpConfig) throws SocketException,

                 IOException {

          String server = ftpConfig.getServer();

          int port = ftpConfig.getPort();

          String user = ftpConfig.getUsername();

          String password = ftpConfig.getPassword();

          String path = ftpConfig.getPath();

          return connectServer(server, port, user, password, path);

   }

   //*/*

    /* 连接ftp服务器

    /* @param server 服务器ip

    /* @param port 端口,通常为21

    /* @param user 用户名

    /* @param password 密码

    /* @param path 进入服务器之后的默认路径

    /* @return 连接成功返回true,否则返回false

    /* @throws SocketException

    /* @throws IOException

    /*/

   public boolean connectServer(String server, int port, String user,

                 String password, String path) throws SocketException, IOException {

          ftpClient = new FTPClient();

          ftpClient.connect(server, port);

          ftpClient.setControlEncoding("GBK");

          log.info("Connected to " + server + ".");

          log.info("FTP server reply code:" + ftpClient.getReplyCode());

          if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {

                 if (ftpClient.login(user, password)) {

                        // Path is the sub-path of the FTP path

                        if (path.length() != 0) {

                               ftpClient.changeWorkingDirectory(path);

                        }

                        return true;

                 }

          }

          disconnect();

          return false;

   }

   //*/*

    /* 断开与远程服务器的连接

    /* @throws IOException

    /*/

   public void disconnect() throws IOException {

          if (ftpClient.isConnected()) {

                 ftpClient.disconnect();

          }

   }



   //*/*

    /* 从FTP服务器上下载文件,支持断点续传,下载百分比汇报

    /* @param remote 远程文件路径及名称

    /* @param local 本地文件完整绝对路径

    /* @return 下载的状态

    /* @throws IOException

    /*/

   public DownloadStatus download(String remote, String local)

                 throws IOException {

          // 设置被动模式

          ftpClient.enterLocalPassiveMode();

          // 设置以二进制方式传输

          ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

          DownloadStatus result;



          // 检查远程文件是否存在

          FTPFile[] files = ftpClient.listFiles(new String(

                        remote.getBytes("GBK"), "iso-8859-1"));

          if (files.length != 1) {

                 log.info("远程文件不存在");

                 return DownloadStatus.RemoteFileNotExist;

          }



          long lRemoteSize = files[0].getSize();

          File f = new File(local);

          // 本地存在文件,进行断点下载

          if (f.exists()) {

                 long localSize = f.length();

                 // 判断本地文件大小是否大于远程文件大小

                 if (localSize >= lRemoteSize) {

                        log.info("本地文件大于远程文件,下载中止");

                        return DownloadStatus.LocalFileBiggerThanRemoteFile;

                 }



                 // 进行断点续传,并记录状态

                 FileOutputStream out = new FileOutputStream(f, true);

                 ftpClient.setRestartOffset(localSize);

                 InputStream in = ftpClient.retrieveFileStream(new String(remote

                               .getBytes("GBK"), "iso-8859-1"));

                 byte[] bytes = new byte[1024];

                 long step = lRemoteSize / 100;

                 step = step==0?1:step;//文件过小,step可能为0

                 long process = localSize / step;

                 int c;

                 while ((c = in.read(bytes)) != -1) {

                        out.write(bytes, 0, c);

                        localSize += c;

                        long nowProcess = localSize / step;

                        if (nowProcess > process) {

                               process = nowProcess;

                               if (process % 10 == 0){

                                      log.info("下载进度:" + process);

                               }

                        }

                 }

                 in.close();

                 out.close();

                 boolean isDo = ftpClient.completePendingCommand();

                 if (isDo) {

                        result = DownloadStatus.DownloadFromBreakSuccess;

                 } else {

                        result = DownloadStatus.DownloadFromBreakFailed;

                 }

          } else {

                 OutputStream out = new FileOutputStream(f);

                 InputStream in = ftpClient.retrieveFileStream(new String(remote

                               .getBytes("GBK"), "iso-8859-1"));

                 byte[] bytes = new byte[1024];

                 long step = lRemoteSize / 100;

                 step = step==0?1:step;//文件过小,step可能为0

                 long process = 0;

                 long localSize = 0L;

                 int c;

                 while ((c = in.read(bytes)) != -1) {

                        out.write(bytes, 0, c);

                        localSize += c;

                        long nowProcess = localSize / step;

                        if (nowProcess > process) {

                               process = nowProcess;

                               if (process % 10 == 0){

                                      log.info("下载进度:" + process);

                               }

                        }

                 }

                 in.close();

                 out.close();

                 boolean upNewStatus = ftpClient.completePendingCommand();

                 if (upNewStatus) {

                        result = DownloadStatus.DownloadNewSuccess;

                 } else {

                        result = DownloadStatus.DownloadNewFailed;

                 }

          }

          return result;

   }



   public boolean changeDirectory(String path) throws IOException {

          return ftpClient.changeWorkingDirectory(path);

   }



   public boolean createDirectory(String pathName) throws IOException {

          return ftpClient.makeDirectory(pathName);

   }



   public boolean removeDirectory(String path) throws IOException {

          return ftpClient.removeDirectory(path);

   }



   public boolean removeDirectory(String path, boolean isAll)

                 throws IOException {



          if (!isAll) {

                 return removeDirectory(path);

          }



          FTPFile[] ftpFileArr = ftpClient.listFiles(path);

          if (ftpFileArr == null || ftpFileArr.length == 0) {

                 return removeDirectory(path);

          }

          //  

          for (FTPFile ftpFile : ftpFileArr) {

                 String name = ftpFile.getName();

                 if (ftpFile.isDirectory()) {

                        log.info("/* [sD]Delete subPath [" + path + "/" + name + "]");

                        if (!ftpFile.getName().equals(".")

                                      && (!ftpFile.getName().equals(".."))) {

                               removeDirectory(path + "/" + name, true);

                        }

                 } else if (ftpFile.isFile()) {

                        log.info("/* [sF]Delete file [" + path + "/" + name + "]");

                        deleteFile(path + "/" + name);

                 } else if (ftpFile.isSymbolicLink()) {



                 } else if (ftpFile.isUnknown()) {



                 }

          }

          return ftpClient.removeDirectory(path);

   }



   //*/*

    /* 查看目录是否存在

    /* @param path

    /* @return

    /* @throws IOException

    /*/

   public boolean isDirectoryExists(String path) throws IOException {

          boolean flag = false;

          FTPFile[] ftpFileArr = ftpClient.listFiles(path);

          for (FTPFile ftpFile : ftpFileArr) {

                 if (ftpFile.isDirectory()

                               && ftpFile.getName().equalsIgnoreCase(path)) {

                        flag = true;

                        break;

                 }

          }

          return flag;

   }



   //*/*

    /* 得到某个目录下的文件名列表

    /* @param path

    /* @return

    /* @throws IOException

    /*/

   public List<String> getFileList(String path) throws IOException {

          // listFiles return contains directory and file, it's FTPFile instance

          // listNames() contains directory, so using following to filer

          // directory.

          // String[] fileNameArr = ftpClient.listNames(path);

          FTPFile[] ftpFiles = ftpClient.listFiles(path);



          List<String> retList = new ArrayList<String>();

          if (ftpFiles == null || ftpFiles.length == 0) {

                 return retList;

          }

          for (FTPFile ftpFile : ftpFiles) {

                 if (ftpFile.isFile()) {

                        retList.add(ftpFile.getName());

                 }

          }

          return retList;

   }



   public boolean deleteFile(String pathName) throws IOException {

          return ftpClient.deleteFile(pathName);

   }



   //*/*

    /* 上传文件到FTP服务器,支持断点续传

    /* @param local 本地文件名称,绝对路径

    /* @param remote 远程文件路径,按照Linux上的路径指定方式,支持多级目录嵌套,支持递归创建不存在的目录结构

    /* @return 上传结果

    /* @throws IOException

    /*/

   public UploadStatus upload(String local, String remote) throws IOException {

          // 设置PassiveMode传输

          ftpClient.enterLocalPassiveMode();

          // 设置以二进制流的方式传输

          ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

          ftpClient.setControlEncoding("GBK");

          UploadStatus result;

          // 对远程目录的处理

          String remoteFileName = remote;

          if (remote.contains("/")) {

                 remoteFileName = remote.substring(remote.lastIndexOf("/") + 1);

                 // 创建服务器远程目录结构,创建失败直接返回

                 if (createDirecroty(remote, ftpClient) == UploadStatus.CreateDirectoryFail) {

                        return UploadStatus.CreateDirectoryFail;

                 }

          }



          // 检查远程是否存在文件

          FTPFile[] files = ftpClient.listFiles(new String(remoteFileName

                        .getBytes("GBK"), "iso-8859-1"));

          if (files.length == 1) {

                 long remoteSize = files[0].getSize();

                 File f = new File(local);

                 long localSize = f.length();

                 if (remoteSize == localSize) { // 文件存在

                        return UploadStatus.FileExits;

                 } else if (remoteSize > localSize) {

                        return UploadStatus.RemoteFileBiggerThanLocalFile;

                 }



                 // 尝试移动文件内读取指针,实现断点续传

                 result = uploadFile(remoteFileName, f, ftpClient, remoteSize);



                 // 如果断点续传没有成功,则删除服务器上文件,重新上传

                 if (result == UploadStatus.UploadFromBreakFailed) {

                        if (!ftpClient.deleteFile(remoteFileName)) {

                               return UploadStatus.DeleteRemoteFaild;

                        }

                        result = uploadFile(remoteFileName, f, ftpClient, 0);

                 }

          } else {

                 result = uploadFile(remoteFileName, new File(local), ftpClient, 0);

          }

          return result;

   }



   //*/*

    /* 递归创建远程服务器目录

    /* @param remote 远程服务器文件绝对路径

    /* @param ftpClient FTPClient对象

    /* @return 目录创建是否成功

    /* @throws IOException

    /*/

   public UploadStatus createDirecroty(String remote, FTPClient ftpClient)

                 throws IOException {

          UploadStatus status = UploadStatus.CreateDirectorySuccess;

          String directory = remote.substring(0, remote.lastIndexOf("/") + 1);

          if (!directory.equalsIgnoreCase("/")

                        && !ftpClient.changeWorkingDirectory(new String(directory

                                      .getBytes("GBK"), "iso-8859-1"))) {

                 // 如果远程目录不存在,则递归创建远程服务器目录

                 int start = 0;

                 int end = 0;

                 if (directory.startsWith("/")) {

                        start = 1;

                 } else {

                        start = 0;

                 }

                 end = directory.indexOf("/", start);

                 while (true) {

                        String subDirectory = new String(remote.substring(start, end)

                                      .getBytes("GBK"), "iso-8859-1");

                        if (!ftpClient.changeWorkingDirectory(subDirectory)) {

                               if (ftpClient.makeDirectory(subDirectory)) {

                                      ftpClient.changeWorkingDirectory(subDirectory);

                               } else {

                                      log.info("创建目录失败");

                                      return UploadStatus.CreateDirectoryFail;

                               }

                        }

                        start = end + 1;

                        end = directory.indexOf("/", start);

                        // 检查所有目录是否创建完毕

                        if (end <= start) {

                               break;

                        }

                 }

          }

          return status;

   }



   //*/*

    /* 上传文件到服务器,新上传和断点续传

    /* @param remoteFile 远程文件名,在上传之前已经将服务器工作目录做了改变

    /* @param localFile 本地文件File句柄,绝对路径

    /* @param processStep 需要显示的处理进度步进值

    /* @param ftpClient FTPClient引用

    /* @return

    /* @throws IOException

    /*/

   public UploadStatus uploadFile(String remoteFile, File localFile,

                 FTPClient ftpClient, long remoteSize) throws IOException {

          UploadStatus status;

          // 显示进度的上传

          System.out.println("localFile.length():"+localFile.length());

          long step = localFile.length() / 100;

          step = step==0?1:step;//文件过小,step可能为0

          long process = 0;

          long localreadbytes = 0L;

          RandomAccessFile raf = new RandomAccessFile(localFile, "r");

          OutputStream out = ftpClient.appendFileStream(new String(remoteFile

                        .getBytes("GBK"), "iso-8859-1"));

          // 断点续传

          if (remoteSize > 0) {

                 ftpClient.setRestartOffset(remoteSize);

                 process = remoteSize / step;

                 raf.seek(remoteSize);

                 localreadbytes = remoteSize;

          }

          byte[] bytes = new byte[1024];

          int c;

          while ((c = raf.read(bytes)) != -1) {

                 out.write(bytes, 0, c);

                 localreadbytes += c;

                 if (localreadbytes / step != process) {

                        process = localreadbytes / step;

                        if (process % 10 == 0){

                               log.info("上传进度:" + process);

                        }

                 }

          }

          out.flush();

          raf.close();

          out.close();

          boolean result = ftpClient.completePendingCommand();

          if (remoteSize > 0) {

                 status = result ? UploadStatus.UploadFromBreakSuccess

                               : UploadStatus.UploadFromBreakFailed;

          } else {

                 status = result ? UploadStatus.UploadNewFileSuccess

                               : UploadStatus.UploadNewFileFailed;

          }

          return status;

   }



   public InputStream downFile(String sourceFileName) throws IOException {

          return ftpClient.retrieveFileStream(sourceFileName);

   }



   public enum UploadStatus {

          CreateDirectoryFail, // 远程服务器相应目录创建失败

          CreateDirectorySuccess, // 远程服务器闯将目录成功

          UploadNewFileSuccess, // 上传新文件成功

          UploadNewFileFailed, // 上传新文件失败

          FileExits, // 文件已经存在

          RemoteFileBiggerThanLocalFile, // 远程文件大于本地文件

          UploadFromBreakSuccess, // 断点续传成功

          UploadFromBreakFailed, // 断点续传失败

          DeleteRemoteFaild; // 删除远程文件失败

   }



   public enum DownloadStatus {

          RemoteFileNotExist, // 远程文件不存在

          DownloadNewSuccess, // 下载文件成功

          DownloadNewFailed, // 下载文件失败

          LocalFileBiggerThanRemoteFile, // 本地文件大于远程文件

          DownloadFromBreakSuccess, // 断点续传成功

          DownloadFromBreakFailed; // 断点续传失败

   }

} FtpConfig类: /// / // package com.pccw.portlet.publictools; /// / FTP配置类 / @author WangXianfeng 1:18:45 PM Jul 16, 2011 // public class FtpConfig { private String server; private int port; private String username; private String password; private String path; gets 。。sets。。 } 测试类: import java.util.List; import com.pccw.portlet.publictools.FtpConfig; import com.pccw.portlet.publictools.FtpUtil; /// / / @author WangXianfeng 1:20:45 PM Jul 16, 2011 // public class HelloWorld { /// / @param args // public static void main(String[] args) throws Exception { FtpConfig config = new FtpConfig(); config.setServer("192.168.0.199"); config.setPort(21); config.setUsername("wxf"); config.setPassword("wxf"); config.setPath("bankafile"); FtpUtil ftp = new FtpUtil(); ftp.connectServer(config); String localFile = "c:\Seagull.rar"; ftp.upload(localFile, "sea.rar"); List files = ftp.getFileList(""); //ftp.changeDirectory("bankafile");

    for(String file:files){
        System.out.println(file);
        String local = "D:\\ct\\199\\temp\\" + file;
        ftp.download(file, local);
    }
}

}

来源: [http://xianfengmc.blog.163.com/blog/static/82690025201161731615607/](http://xianfengmc.blog.163.com/blog/static/82690025201161731615607/)