android ftp客户端
1.
建立FTPClient對象,連接服務器
ftp.connect("169.254.xxx.xxx", 21);
public String[] connect(String host, int port)throws IllegalStateException, IOException,FTPIllegalReplyException, FTPException {synchronized (this.lock) {if (this.connected) {throw new IllegalStateException("Client already connected to "+ host + " on port " + port);}Socket connection = null;try {connection = this.connector.connectForCommunicationChannel(host, port);if (this.security == 1) {connection = ssl(connection, host, port);}this.communication = new FTPCommunicationChannel(connection,"UTF-8");for (Iterator i = this.communicationListeners.iterator(); i.hasNext();) {this.communication.addCommunicationListener((FTPCommunicationListener) i.next());}FTPReply wm = this.communication.readFTPReply();if (!wm.isSuccessCode()) {throw new FTPException(wm);}this.connected = true;// this.authenticated = false;// this.parser = null;this.host = host;this.port = port;this.username = null;this.password = null;this.utf8Supported = false;this.restSupported = false;this.mlsdSupported = false;this.modezSupported = false;// this.dataChannelEncrypted = false;// Returns the welcome message.return wm.getMessages();} catch (IOException e) {// D'oh!throw e;} finally {// If connection has failed...if (!connected) {if (connection != null) {// Close the connection, 'cause it should be open.try {connection.close();} catch (Throwable t) {;}}}}}}在連接服務器過程中,
connection = this.connector.connectForCommunicationChannel( host, port);
創建了控制通道的socket
this.communication = new FTPCommunicationChannel(connection, "UTF-8");
然后管理socket創建控制通道的管理類,監聽信息入口的接收與發送,建立socket完成后通過FTPReply wm = this.communication.readFTPReply(); 獲取服務器的信息,判斷是否成功。
2.
public FTPCommunicationChannel(Socket connection, String charsetName)throws IOException {this.connection = connection;this.charsetName = charsetName;InputStream inStream = connection.getInputStream();OutputStream outStream = connection.getOutputStream();// Wrap the streams into reader and writer objects.reader = new NVTASCIIReader(inStream, charsetName);writer = new NVTASCIIWriter(outStream, charsetName);} private String read() throws IOException {// Read the line from the server.String line = reader.readLine();if (line == null) {throw new IOException("FTPConnection closed");}// Call received() method on every communication listener// registered.for (Iterator iter = communicationListeners.iterator(); iter.hasNext();) {FTPCommunicationListener l = (FTPCommunicationListener) iter.next();l.received(line);}// Return the line read.return line;} public void sendFTPCommand(String command) throws IOException {writer.writeLine(command);for (Iterator iter = communicationListeners.iterator(); iter.hasNext();) {FTPCommunicationListener l = (FTPCommunicationListener) iter.next();l.sent(command);}}監聽socket信息接收與發送的管理類,該類創建了兩個繼承Reader和Writer的類來接收與發送信息——NVTASCIIReader與NVTASCIIWriter,read() 讀取方法,sendFTPCommand(String command) 發送信息,每次發送與接收都會觸發監聽事件
3.
NVTASCIIReader 類讀取信息方法:
public String readLine() throws IOException {StringBuffer buffer = new StringBuffer();int previous = -1;int current = -1;while (true) {int i = this.reader.read();if (i == -1) {if (buffer.length() == 0) {return null;}return buffer.toString();}previous = current;current = i;if (/* previous == '\r' && */current == '\n') {// End of line.return buffer.toString();} else if (previous == '\r' && current == 0) {// Literal new line.buffer.append(SYSTEM_LINE_SEPARATOR);} else if (current != 0 && current != '\r') {buffer.append((char) current);}}}該方法每次讀取一行一旦遇到\n就返回。相反NVTASCIIReader 類發送信息也是如此:
public void writeLine(String str) throws IOException {StringBuffer buffer = new StringBuffer();boolean atLeastOne = false;StringTokenizer st = new StringTokenizer(str, LINE_SEPARATOR);int count = st.countTokens();for (int i = 0; i < count; i++) {String line = st.nextToken();if (line.length() > 0) {if (atLeastOne) {buffer.append('\r');buffer.append('\000');}buffer.append(line);atLeastOne = true;}}if (buffer.length() > 0) {String statement = buffer.toString();this.writer.write(statement);this.writer.write("\r\n");this.writer.flush();}}該方法主要根據換行符分離字符串,然后加上回車符,直到要發送的文字結束才加上換行符。
4.
FTPReply wm = this.communication.readFTPReply();讀取并分析服務器返回的數據,返回一個包括服務器的返回碼和信息的FTPReply 類。代碼比較無聊,就不貼出來了,可以去查看項目源碼。
5.
接下來是登陸服務器。該方法一步一步驗證用戶名、密碼最后是
public void login(String username, String password, String account)throws IllegalStateException, IOException,FTPIllegalReplyException, FTPException {synchronized (this.lock) {this.authenticated = false;this.communication.sendFTPCommand("USER " + username);FTPReply r = this.communication.readFTPReply();boolean passwordRequired;boolean accountRequired;switch (r.getCode()) {case 230:passwordRequired = false;accountRequired = false;break;case 331:passwordRequired = true;accountRequired = false;break;case 332:passwordRequired = false;accountRequired = true;default:throw new FTPException(r);}if (passwordRequired) {if (password == null) {throw new FTPException(331);}this.communication.sendFTPCommand("PASS " + password);r = this.communication.readFTPReply();switch (r.getCode()) {case 230:accountRequired = false;break;case 332:accountRequired = true;break;default:throw new FTPException(r);}}if (accountRequired) {if (account == null) {throw new FTPException(332);}this.communication.sendFTPCommand("ACCT " + account);r = this.communication.readFTPReply();switch (r.getCode()) {case 230:break;default:throw new FTPException(r);}}this.authenticated = true;this.username = username;this.password = password;}postLoginOperations();startAutoNoopTimer();}登陸成功后,運行postLoginOperations() 和startAutoNoopTimer()方法,前一個是獲取服務器支持哪些功能,后一個是啟動循環等待計時,每段時間都去請求服務器的承認。
6.
FTPFile[] list = ftp.list();這里是重點,主要是獲取服務器當前目錄的文件。該方法去除了很多判斷的枝末^0^,如果想要完整地看它是如何處理的就要去研究查看源碼,也不難就是變量多了點。這里使用被動方式,這個方式在文章開頭理論就有說明。很簡單,先在控制通道(比如A通道)發送一個PASV這個協議(說:hey,man 我想建立socket來傳輸數據,給個端口我),服務器返回一個隨機端口告訴客戶端,客戶端分析出這個端口,然后與服務器建立一個新的socket。
private FTPDataTransferConnectionProvider openPassiveDataTransferChannel()throws IOException, FTPIllegalReplyException, FTPException {// Send the PASV command.communication.sendFTPCommand("PASV");// Read the reply.FTPReply r = communication.readFTPReply();touchAutoNoopTimer();if (!r.isSuccessCode()) {throw new FTPException(r);}// Use a regexp to extract the remote address and port.String addressAndPort = null;String[] messages = r.getMessages();for (int i = 0; i < messages.length; i++) {Matcher m = PASV_PATTERN.matcher(messages[i]);if (m.find()) {int start = m.start();int end = m.end();addressAndPort = messages[i].substring(start, end);break;}}if (addressAndPort == null) {// The remote server has not sent the coordinates for the// data transfer connection.throw new FTPIllegalReplyException();}// Parse the string extracted from the reply.StringTokenizer st = new StringTokenizer(addressAndPort, ",");int b1 = Integer.parseInt(st.nextToken());int b2 = Integer.parseInt(st.nextToken());int b3 = Integer.parseInt(st.nextToken());int b4 = Integer.parseInt(st.nextToken());int p1 = Integer.parseInt(st.nextToken());int p2 = Integer.parseInt(st.nextToken());final InetAddress remoteAddress;// Ignore address?// String useSuggestedAddress = System// .getProperty(FTPKeys.PASSIVE_DT_USE_SUGGESTED_ADDRESS);String useSuggestedAddress = "IP";if ("true".equalsIgnoreCase(useSuggestedAddress)|| "yes".equalsIgnoreCase(useSuggestedAddress)|| "1".equals(useSuggestedAddress)) {remoteAddress = InetAddress.getByAddress(new byte[] { (byte) b1,(byte) b2, (byte) b3, (byte) b4 });} else {remoteAddress = InetAddress.getByName(host);}final int remotePort = (p1 << 8) | p2;FTPDataTransferConnectionProvider provider = new FTPDataTransferConnectionProvider() {public Socket openDataTransferConnection() {// Establish the connection.Socket dtConnection = null;String remoteHost = remoteAddress.getHostAddress();try {dtConnection = connector.connectForDataTransferChannel(remoteHost, remotePort);} catch (IOException e) {}return dtConnection;}public void dispose() {// nothing to do}};return provider;}該方法返回一個新的socket。
然后就是通過新的socket來接收服務器端返回的file列表,(謹記:所有請求協議都是通過控制通道(A通道)發送的)
FTPDataTransferConnectionProvider provider = openDataTransferChannel();String command = "LIST";// Adds the file/directory selector.if (fileSpec != null && fileSpec.length() > 0) {command += " " + fileSpec;}// Sends the command.communication.sendFTPCommand(command);Socket dtConnection;try {try {dtConnection = provider.openDataTransferConnection();} finally {r = communication.readFTPReply();touchAutoNoopTimer();if (r.getCode() != 150 && r.getCode() != 125) {throw new FTPException(r);}}} finally {provider.dispose();}// Fetch the list from the data transfer connection.ArrayList lines = new ArrayList();NVTASCIIReader dataReader = null;try {// Opens the data transfer connection.dataTransferInputStream = dtConnection.getInputStream();// MODE Z enabled?if (modezEnabled) {dataTransferInputStream = new InflaterInputStream(dataTransferInputStream);}// Let's do it!dataReader = new NVTASCIIReader(dataTransferInputStream,"UTF-8");String line;while ((line = dataReader.readLine()) != null) {if (line.length() > 0) {lines.add(line);}}} catch (IOException e) {} finally {if (dataReader != null) {try {dataReader.close();} catch (Throwable t) {;}}try {dtConnection.close();} catch (Throwable t) {;}// Consume the result reply of the transfer.communication.readFTPReply();// Set to null the instance-level input stream.dataTransferInputStream = null;}這樣一個文件目錄的獲取就完成了,舉一反三,下載、上傳同樣道理。
^0^看得這么辛苦,最后給個該流程的思維導圖,導圖沒有什么規范,就是按照我覺得比較容易理解的方式畫出來。(圖片好像過大了……)
總結:
紅色線發送數據請求,藍色線獲取數據分析,主的線索就是這么簡單。剩余的就是socket類建立的設計,信息協議類的設計。
個人觀點:可能是協議的不同,我看的這個ftp源碼跟smack源碼比較發現還是smack源碼項目設計得比較好,有很多地方可以擴展自定義消息,當然整個設計也是復雜多一點。
總結
以上是生活随笔為你收集整理的android ftp客户端的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 直立两轮平衡车核心代码
- 下一篇: InDesign 教程_如何链接图形