| |||||||||
日 | 一 | 二 | 三 | 四 | 五 | 六 | |||
---|---|---|---|---|---|---|---|---|---|
27 | 28 | 29 | 30 | 1 | 2 | 3 | |||
4 | 5 | 6 | 7 | 8 | 9 | 10 | |||
11 | 12 | 13 | 14 | 15 | 16 | 17 | |||
18 | 19 | 20 | 21 | 22 | 23 | 24 | |||
25 | 26 | 27 | 28 | 29 | 30 | 31 | |||
1 | 2 | 3 | 4 | 5 | 6 | 7 |
Published April 2013
對于許多基于客戶端-服務器程序來說,老的HTTP 請求-響應模型已經有它的局限性. 信息必須通過多次請求才能將其從服務端傳送到客戶端.
過去許多的黑客使用某些技術來繞過這個問題,例如:長輪詢(long polling)、基于 HTTP 長連接的服務器推技術(Comet).
在2011年, IETF發布了標準WebSocket協議-RFC 6455. 從那時起,大多數Web瀏覽器都實現了支持WebSocket協議的客戶端APIs.同時,許多Java 包也開始實現了WebSocket協議.
WebSocket協議利用HTTP升級技術來將HTTP連接升級到WebSocket. 一旦升級后,連接就有了在兩個方向上相互獨立(全雙式)發送消息(數據楨)的能力.
不需要headers 或cookies,這大大降低了所需的帶寬. 通常,WebSockets來周期性地發送小消息 (例如,幾個字節).
JSR 356, WebSocket的Java API, 明確規定了API,當Java開發者需要在應用程序中集成WebSocket時,就可以使用此API—服務端和客戶端均可. 每個聲明兼容JSR 356的WebSocket協議,都必須實現這個API.
因此,開發人員可以自己編寫獨立于底層WebSocket實現的WebSocket應用。這是一個巨大的好處,因為它可以防止供應商鎖定,并允許更多的選擇、自由的庫、應用程序服務器。
JSR 356是即將到來的java EE 7標準的一部分,因此,所有與Java EE 7兼容的應用服務器都有JSR 365標準WebSocket的實現.一旦建立,WebSocket客戶端和服務器節點已經是對稱的了。客戶端API與服務器端API的區別是很小的,JSR 356定義的Java client API只是Java EE7完整API的子集.
客戶段-服務器端程序使用WebSockets,通常會包含一個服務器組件和多個客戶端組件, 如圖1所示:
圖1
在這個例子中,server application 是通過Java編寫的,WebSocket 協議細節是由包含在Java EE 7容器中JSR 356 實現來處理的.
JavaFX 客戶端可依賴任何與JSR 356兼容的客戶端實現來處理WebSocket協議問題.
其它客戶端(如,iOS 客戶端和HTML5客戶端)可使用其它 (非Java)與RFC6455兼容的實現來與server application通信.
JSR 356定義的專家小組,希望支持Java EE開發人員常用的模式和技術。因此,JSR 356使用了注釋和注入。
一般來說,支持兩種編程模型:
Endpoint接口和與生命周期交互的方法.
典型的WebSocket 交互生命周期如下:
大部分WebSocket生命周期事件都與Java方法對應,不管是 annotation-driven 還是interface-driven.
接受WebSocket請求的端點可以是以 @ServerEndpoint
注解的POJO.
此注解告知容器,此類應該被認為是WebSocket端點.
必須的value
元素指定了WebSocket端點的路徑.
考慮下面的代碼片斷:
@ServerEndpoint("/hello") public class MyEndpoint { }
此代碼將會以相對路徑hello來發布一個端點.在后續方法調用中,此路徑可攜帶路徑參數,如: /hello/{userid}是一個有效路徑,在這里
{userid}
的值,可在生命周期方法使用@PathParam
注解獲取.
在GlassFish中,如果你的應用程序是用上下文mycontextroot
部署的,且在localhost的8080端口上監聽
, WebSocket可通過使用ws://localhost:8080/mycontextroot/hello來訪問
.
初始化WebSocket連接的端點可以是以 @ClientEndpoint
注解的POJO.@ClientEndpoint
和 @ServerEndpoint的主要區別是
ClientEndpoint
不接受路徑路值元素,因為它監聽進來的請求。
@ClientEndpoint public class MyClientEndpoint {}
Java中使用注解驅動POJO方式來初始化WebSocket連接,可通過如下代碼來完成:
javax.websocket.WebSocketContainer container = javax.websocket.ContainerProvider.getWebSocketContainer(); container.conntectToServer(MyClientEndpoint.class, new URI("ws://localhost:8080/tictactoeserver/endpoint"));
此后,以 @ServerEndpoint
或@ClientEndpoint
注解的類都稱為注解端點.
一旦建立了WebSocket連接 ,就會創建 Session,并且會調用注解端點中以
@OnOpen注解的方法.
此方法包含了幾個參數:
javax.websocket.Session
參數, 代表創建的Session
EndpointConfig
實例包含了關于端點配置的信息@PathParam注解的
字符串參數,指的是端點路徑的path參數下面的方法實現了當打開WebSocket時,將會打印session的標識符:
@OnOpen public void myOnOpen (Session session) { System.out.println ("WebSocket opened: "+session.getId()); }
Session實例只要WebSocket未關閉就會一直有效
. Session類中包含了許多有意思的方法,以允許開發者獲取更多關于的信息
。
同時,Session
也包含了應用程序特有的數據鉤子,即通過getUserProperties()
方法來返回 Map<String, Object>
.
這允許開發者可以使用session-和需要在多個方法調用間共享的應用程序特定信息來填充Session
實例.
i當WebSocket端收到消息時,將會調用以@OnMessage
注解的方法.以@OnMessage
注解的方法可包含下面的參數:
javax.websocket.Session
參數.@PathParam注解的
字符串參數,指的是端點路徑的path參數當其它端發送了文本消息時,下面的代碼片斷會打印消息內容:
@OnMessage public void myOnMessage (String txt) { System.out.println ("WebSocket received message: "+txt); }
如果以@OnMessage
i注解的方法返回值不是void
, WebSocket實現會將返回值發送給其它端點.下面的代碼片斷會將收到的文本消息以首字母大寫的形式發回給發送者:
@OnMessage public String myOnMessage (String txt) { return txt.toUpperCase(); }
另一種通過WebSocket連接來發送消息的代碼如下:
RemoteEndpoint.Basic other = session.getBasicRemote(); other.sendText ("Hello, world");
在這種方式中,我們從Session
對象開始,它可以從生命周期回調方法中獲取(例如,以 @OnOpen注解的方法
).session實例上getBasicRemote()
方法返回的是WebSocket其它部分的代表RemoteEndpoint
. RemoteEndpoint
實例可用于發送文本或其它類型的消息,后面有描述.
當關閉WebSocket連接時,將會調用@OnClose
注解的方法。此方法接受下面的參數:
javax.websocket.Session
參數. 注意,一旦WebSocket真正關閉了,此參數就不能被使用了,這通常發生在@OnClose
注解方法返回之后.javax.websocket.CloseReason
參數,用于描述關閉WebSocket的原因,如:正常關閉,協議錯誤,服務過載等等.@PathParam注解的
字符串參數,指的是端點路徑的path參數下面的代碼片段打印了WebSocket關閉的原因:
@OnClose public void myOnClose (CloseReason reason) { System.out.prinlnt ("Closing a WebSocket due to "+reason.getReasonPhrase()); }
完整情況下,這里還有一個生命周期注解:如果收到了錯誤,將會調用 @OnError
注解的方法。
annotation-driven 方式允許我們注解一個Java類,以及使用生命周期注解來注解方法.
使用interface-driven方式,開發者可繼承javax.websocket.Endpoint
并覆蓋其中的onOpen
, onClose
, 以及onError
方法:
public class myOwnEndpoint extends javax.websocket.Endpoint { public void onOpen(Session session, EndpointConfig config) {...} public void onClose(Session session, CloseReason closeReason) {...} public void onError (Session session, Throwable throwable) {...} }
為了攔截消息,需要在onOpen實現中注冊一個javax.websocket.MessageHandler
:
public void onOpen (Session session, EndpointConfig config) { session.addMessageHandler (new MessageHandler() {...}); }
MessageHandler
接口有兩個子接口: MessageHandler.Partial和
MessageHandler.Whole
.
MessageHandler.Partial
接口應該用于當開發者想要收到部分消息通知的時候,MessageHandler.Whole的實現應該用于整個消息到達通知
。
下面的代碼片斷會監聽進來的文件消息,并將文本信息轉換為大小版本后發回給其它端點:
public void onOpen (Session session, EndpointConfig config) { final RemoteEndpoint.Basic remote = session.getBasicRemote(); session.addMessageHandler (new MessageHandler.Whole<String>() { public void onMessage(String text) { try { remote.sendString(text.toUpperCase()); } catch (IOException ioe) { // handle send failure here } } }); }
WebSocket的JavaAPI非常強大,因為它允許發送任或接收任何對象作為WebSocket消息.
基本上,有三種不同類型的消息:
當使用interface-driven模式,每個session最多只能為這三個不同類型的消息注冊一個MessageHandler
.
當使用annotation-driven模式,針對不同類型的消息,只允許出現一個@onMessage
注解方法. 在注解方法中,消息內容中允許的參數依賴于消息類型。
Javadoc for the @OnMessage
annotation 明確指定了消息類型上允許出現的消息參數:
String
用于接收整個消息String
和 boolean 對用于部分接收消息Reader
用于以阻塞流的方式接收整個消息Decoder.Text
或 Decoder.TextStream
).byte[]
或 ByteBuffer
用于接收整個消息byte[]
和 boolean 對, 或者 ByteBuffer
和boolean對用于部分接收消息InputStream
用于按阻塞流的方式接收整個消息Decoder.Binary
or Decoder.BinaryStream
).PongMessage
用于處理pong消息"任何Java對象使用編碼器都可以編碼為基于文本或二進制的消息.這種基于文本或二進制的消息將轉輸到其它端點,在其它端點,它可以解碼成Java對象-或者被另外的WebSocket 包解釋.
通常情況下,XML或JSON用于來傳送WebSocket消息, 編碼/解碼然后會將Java對象編組成XML或JSON并在另一端解碼為Java對象.
encoder是以javax.websocket.Encoder
接口的實現來定義,decoder是以javax.websocket.Decoder
接口的實現來定義的.
有時,端點實例必須知道encoders和decoders是什么.使用annotation-driven方式, 可向@ClientEndpoint
和 @ServerEndpoint
l注解中的encode和decoder元素傳遞 encoders和decoders的列表。
Listing 1 中的代碼展示了如何注冊一個 MessageEncoder
類(它定義了MyJavaObject實例到文本消息的轉換). MessageDecoder
是以相反的轉換來注冊的.
@ServerEndpoint(value="/endpoint", encoders = MessageEncoder.class, decoders= MessageDecoder.class) public class MyEndpoint { ... } class MessageEncoder implements Encoder.Text<MyJavaObject> { @override public String encode(MyJavaObject obj) throws EncodingException { ... } } class MessageDecoder implements Decoder.Text<MyJavaObject> { @override public MyJavaObject decode (String src) throws DecodeException { ... } @override public boolean willDecode (String src) { // return true if we want to decode this String into a MyJavaObject instance } }
Listing 1
Encoder
接口有多個子接口:
Encoder.Text
用于將Java對象轉成文本消息Encoder.TextStream
用于將Java對象添加到字符流中Encoder.Binary
用于將Java對象轉換成二進制消息Encoder.BinaryStream
用于將Java對象添加到二進制流中類似地,Decoder
接口有四個子接口:
Decoder.Text
用于將文本消息轉換成Java對象Decoder.TextStream
用于從字符流中讀取Java對象Decoder.Binary
用于將二進制消息轉換成Java對象Decoder.BinaryStream
用于從二進制流中讀取Java對象WebSocket Java API為Java開發者提供了標準API來集成IETF WebSocket標準.通過這樣做,Web 客戶端或本地客戶端可使用任何WebSocket實現來輕易地與Java后端通信。
rabbitmq-service.bat — 管理RabbitMQ AMQP service
rabbitmq-service.bat [command]
RabbitMQ是AMQP的實現, 后者是高性能企業消息通信的新興標準. RabbitMQ server是AMQP 中間件的健壯,可擴展實現.
運行rabbitmq-service,可允許RabbitMQ broker在NT/2000/2003/XP/Vista®環境上以服務來運行,這樣就可以通過Windows® services applet來啟動和停止服務.
默認情況下,服務會以本地系統帳戶中認證上下文來運行。因此,有必要將Erlang cookies 和本地系統帳戶進行同步(典型地,C:\WINDOWS\.erlang.cookie和帳戶將用來運行 rabbitmqctl).
顯示使用信息.
安裝service,安裝后,它不會啟動。如果環境變量修改了的話,隨后的調用將更新服務參數.
刪除service.如果刪除時,service正在運行,則將會自動停止。 它不會刪除任何文件,后續可通過rabbitmq-server 繼續操作。
啟動service. 在此之前,service必須被正確安裝
停止service.
禁用service. 這等價于在服務控制面板中,將啟動類型設置為禁用.
啟用service. 這等價于在服務控制面板中,將啟動類型設置為自動.
默認為RabbitMQ.
默認是當前用戶的應用程序數據目錄. 這是日志和數據目錄的位置(C:\Users\Administrator\AppData\Roaming\RabbitMQ).
默認是rabbit. 當你想在一臺機器上運行多個節點時,此配置是相當有用的, RABBITMQ_NODENAME在每個erlang-node和機器的組合中應該唯一。
參考clustering on a single machine guide 來更多細節.
默認情況下,RabbitMQ會綁定到所有網絡接口上,如果只想綁定某個網絡接口,可修改此設置。
默認為5672.
默認為C:\Program Files\erl5.5.5\erts-5.5.5\bin (或64位環境 中為C:\Program Files (x86)\erl5.5.5\erts-5.5.5\bin). 這是Erlang service manager的安裝位置.
將此變量設置為new或reuse,以將服務器控制臺的輸出重定向到名為SERVICENAME.debug文件中(位于安裝服務的用戶應用程序數據目錄).在Vista下,其位置在C:\Users\AppData\username\SERVICENAME. 在Windows的前期版本中,位置在C:\Documents and Settings\username\Application Data\SERVICENAME.
如果RABBITMQ_CONSOLE_LOG設置為new,那么每次服務啟動時都會創建一個新文件。
如果RABBITMQ_CONSOLE_LOG設置為reuse,那么每次服務啟動時,文件都會被覆蓋.
當RABBITMQ_CONSOLE_LOG 沒有設置或設置的值不是new或reuse時,默認的行為是丟棄服務器輸出。
要運行ftp4j library,你需要Java 運行時環境v. 1.4+.
將ftp4j JAR文件添加到你應用程序的classpath中, 然后你就可以自動啟用ftp4j類的使用了.
可參考ftp4j javadocs.
包中的主類是FTPClient (it.sauronsoftware.ftp4j.FTPClient).
創建一個FTPClient 實例:
FTPClient client = new FTPClient();
連接遠程FTP服務:
client.connect("ftp.host.com");
如果服務端口不是標準的21端口 (或 FTPS的990端口),需要使用port參數進行指定:
client.connect("ftp.host.com", port);
如:
client.connect("ftp.host.com", 8021);
然后進行登錄流程:
client.login("carlo", "mypassword");
如果沒有拋出任何異常的話,那么你就通過遠程服務器的認證了.否則,如果驗證失敗,你將會收到it.sauronsoftware.ftp4j.FTPException異常.
匿名認證,如果被連接服務認可的話, 可通過發送用戶名"anonymous" 和任意密碼來完成(注意,有些服務器需要e-mail地址來代替密碼):
client.login("anonymous", "ftp4j");
使用遠程FTP服務來做任何事情,然后再斷開連接:
client.disconnect(true);
這會向遠程器發送FTP QUIT命令, 以進行一個合法斷開流程.如果你只是想中斷連接而不想向服務器發送任何通知,那么可以使用:
client.disconnect(false);
客戶端通過連接器(一個繼承自it.sauronsoftware.ftp4j.FTPConnector的對象)來連接服務器, 它將返回一個已經打開的連接(一個實現了it.sauronsoftware.ftp4j.FTPConnection 接口的對象).這也是為什么ftp4j 可以支持大量代理的原因.
在連接遠程服務器前,客戶端實例可以使用setConnector() 方法來設置連接器:
client.setConnector(anyConnectorYouWant);
如果沒有設置連接器的話,會使用默認的連接器DirectConnector (it.sauronsoftware.ftp4j.connectors.DirectConnector), 它實現了對遠程服務器的直接連接,且不會使用代理。
如果你只能通過代理來連接遠程服務器, ftp4j包可以讓你在下面的連接器中進行選擇:
因為ftp4j的連接器架構設計為可插拔的,因此你可以繼承FTPConnector 抽象類來構建自己的連接器.
ftp4j包支持FTPS (隱式基于 TLS/SSL的FTP) 和FTPES (顯示基于TLS/SSL的FTP).
setSecurity() 方法可用來打開這種特性:
client.setSecurity(FTPClient.SECURITY_FTPS); // 啟用 FTPS
client.setSecurity(FTPClient.SECURITY_FTPES); // 啟用 FTPES
兩個方法都需要在連接遠程服務器前調用.
如果安全協議設置成了SECURITY_FTPS, 則connect() 方法默認使用的端口為990.
默認情況下,客戶端對象商討SSL連接會使用javax.net.ssl.SSLSocketFactory.getDefault()作為其套接字工廠.可通過調用client.setSSLSocketFactory()方法來改變默認套接字工廠. 另外一種SSLSocketFactory, 可用來信任遠程服務器頒發的證書(謹慎使用):
import it.sauronsoftware.ftp4j.FTPClient;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
// ... TrustManager[] trustManager = new TrustManager[] { new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() { return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) { }
public void checkServerTrusted(X509Certificate[] certs, String authType) { } } };
SSLContext sslContext = null;
try { sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustManager, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
FTPClient client = new FTPClient();
client.setSSLSocketFactory(sslSocketFactory);
client.setSecurity(FTPClient.SECURITY_FTPS);
// or client.setSecurity(FTPClient.SECURITY_FTPES); // ...
獲取當前目錄的的絕對路徑(此目錄是FTP服務器的home目錄):
String dir = client.currentDirectory();
改變目錄:
client.changeDirectory(newPath);
你可以使用絕對路徑和相對路徑:
client.changeDirectory("/an/absolute/one");
client.changeDirectory("relative");
回到父目錄:
client.changeDirectoryUp();
要重命名遠程文件或目錄:
client.rename("oldname", "newname");
rename() 方法也可以用來從當前位置移動文件或目錄到其它位置.
在這個例子子,假設在當前工作目錄中,你有一個名為"myfile.txt"的文件,然后你想將其移動到子目錄"myfolder"中:
client.rename("myfile.txt", "myfolder/myfile.txt");
要刪除遠程文件,需要調用:
client.deleteFile(relativeOrAbsolutePath);
在這個例子中:
client.deleteFile("useless.txt");
如果遠程服務給你機會的話,你可以在遠程站點上創建新目錄:
client.createDirectory("newfolder");
你也可以已存在的目錄:
client.deleteDirectory(absoluteOrRelativePath);
在這個例子中:
client.deleteDirectory("oldfolder");
請注意,通常情況下,FTP 服務器只允許刪除空目錄.
FTP 協議并不會提供大量支持方法來獲取工作目錄的完整信息.通常LIST命令會給你想知道的東西,但不辛的是,每個服務器會使用不同樣式的響應. 這意味著某些服務器會返回UNIX樣式的目錄,有些服務器會返回DOS樣式的目錄,其它的服務器又會使用別的樣式.
ftp4j 包可以處理許多的LIST響應格式, 并將它們構建成統一目錄內容的結構對象表示.當前ftp4j可以處理:
這可以通過使用可插拔的parsers來完成.包it.sauronsoftware.ftp4j.listparsers包含了用于處理上述樣式的對象.大多數時間,這些已經夠用了。
要列出當前工作目錄下的文件或文件夾,可調用:
FTPFile[] list = client.list();
如果你收到了FTPListParseException (it.sauronsoftware.ftp4j.FTPListParseException) 異常,這就意味著服務器對LIST命令返回了不可理解的樣式,即它不是上述列出的樣式.因此,你可以嘗試使用listNames() 方法, 但它并不如list()方法有優勢.。為了彌補這種缺陷,你可以構建你自己的LIST響應解析器,以支持你遇到的樣式.你可以實現FTPListParser (it.sauronsoftware.ftp4j.FTPListParser) 接口,然后你可以在client的addListParser()方法使用此實現.
FTPFile (it.sauronsoftware.ftp4j.FTPFile) 對象提供了目錄內容的表示,包括文件,子目錄和連接. 根據服務器的響應,FTPFile對象的某些字段可以是null 的或者是無意義的.請檢查javadocs來了解細節.
在list() 方法中你也可以使用文件過濾參數,如:
FTPFile[] list = client.list("*.jpg");
如果連接服務器明確支持MLSD命令, ftp4j會用其來代替基本的LIST命令。MLSD的響應事實更為標準,準確,更易解析.不幸的是,不是所有服務器都支持這個命令,并且有些服務器支持得非常糟糕.基于這些理由,開發者可以控制ftp4j是否應該使用MLSD命令,即通過調用FTPClient對象的setMLSDPolicy()方法. 合法的值:
FTPClient.MLSD_IF_SUPPORTED
client只在服務器明確支持MLSD命令時,才使用MLSD命令. 這是ftp4j默認的行為.
FTPClient.MLSD_ALWAYS
client總是會使用MLSD命令, 即便服務器沒有明確表明支持MLSD命令.
FTPClient.MLSD_NEVER
client絕不使用MLSD命令,即便服務器明確表明支持MLSD命令.
例如:
client.setMLSDPolicy(FTPClient.MLSD_NEVER);
通常情況下FTPFile對象會告訴你條目的最后修改時間, 但正如上面描述的,這依賴于服務器發回的響應.如果你需要最后的修改時間,但你又不能通過list()方法得到,那么可以嘗試這樣做:
java.util.Date md = client.modifiedDate("filename.ext");
下載遠程文件最簡單的方式是調用download(String, File) 方法:
client.download("remoteFile.ext", new java.io.File("localFile.ext"));
要上傳:
client.upload(new java.io.File("localFile.ext"));
要在已有文件中上傳追加內容:
client.append(new java.io.File("localFile.ext"));
這些是阻塞式調用:它們會在傳輸完成后(或failed, 或 aborted時)才返回. 此外同步鎖是否由客戶端來實施的,因為在每個時間段內只允許有一個常規的FTP通信.在每個時間段內,你可以處理多個傳輸器,即使用多個FTPClient 對象,每個都與服務器建立一個私有連接.
你可以使用FTPDataTransferListener (it.sauronsoftware.ftp4j.FTPDataTransferListener)對象來監控傳輸.你可以自己實現一個:
import it.sauronsoftware.ftp4j.FTPDataTransferListener;
public class MyTransferListener implements FTPDataTransferListener {
public void started() {
// Transfer started
}
public void transferred(int length) {
// Yet other length bytes has been transferred since the last time this
// method was called
}
public void completed() {
// Transfer completed
}
public void aborted() {
// Transfer aborted
}
public void failed() {
// Transfer failed
}
}
現在像下面這樣來下載或上傳:
client.download("remoteFile.ext", new java.io.File("localFile.ext"), new MyTransferListener());
client.upload(new java.io.File("localFile.ext"), new MyTransferListener());
client.append(new java.io.File("localFile.ext"), new MyTransferListener());
當client處理下載或上傳時,傳輸器可以被同一個FTPClient對象的不同線程通過調用 abortCurrentDataTransfer() 方法aborted. 此方法還需要一個boolean參數:true表示執行合法的abort過程(即向服務器發送ABOR命令), false表示實然關閉傳輸器,而不向服務器發送通知:
client.abortCurrentDataTransfer(true); // Sends ABOR
client.abortCurrentDataTransfer(false); // Breaks abruptly
需要注意的是,list()和listNames() 方法暗中包含了數據傳輸器,因abortCurrentDataTransfer() 方法也可以用來中斷其list過程.
當數據傳輸器在download(), upload(), append(), list() and listNames() 方法中中斷時,將會拋出FTPAbortedException (it.sauronsoftware.ftp4j.FTPAbortedException).
下載和上傳操作可通過restartAt 參數來重新恢復:
client.download("remoteFile.ext", new java.io.File("localFile.ext"), 1056);
此操作會文件的第1056個字節處繼續執行下載操作。第一個傳輸的字節將是第1057個.
其它 download(), upload() 和append()方法的變種可以讓你使用流來替代java.io.File對象.因此你可以在數據庫,網絡連接或其它流上來傳輸數據。
客戶端和服務器之間的數據傳輸通道是通過單獨的網絡連接來建立的. 在傳輸通道建立期間,服務器可以是active或passive的. 服務器激活數據傳輸時,工作如下:
active模式需要你的client能夠收到來自服務器的連接.如果你的client處于防火墻, 代理或這兩者混合之后,那么大部分時間都會出現問題,因為它不能收到來外界的連接. 下面是passive數據傳輸模式:
在passive模式中,客戶端連接不要求能收到服務器的連接請求.
在ftp4j中,你可以使用下面的調用來切換active、passive模式:
client.setPassive(false); // Active mode
client.setPassive(true); // Passive mode
ftp4j client passive 標志的默認值為true: 如果你沒有調用setPassive(false) ,你的客戶端在每次傳輸前,都會向服務器請求passive模式.
當使用 passive文件傳輸時,服務器會提供一個 IP地址和一個端口號.作為FTP規范的client,需要使用給定的主機號和端口進行連接.在商業環境中,這種行為可能會經常帶來問題,因為NAT配置可能會阻止對IP地址的連接.這就是為什么FTP clients通常會忽略服務器返回的任何IP地址,進而在通信線路中使用同樣的主機來連接服務器.ftp4j的行為依賴于服務器因素:
在active傳輸模式中,可以設置下面的系統屬性:
ftp4j.activeDataTransfer.hostAddress
主機地址.當服務器請求執行與客戶端連接時,client會跳轉到給定地址的服務器. 此值應該是一個有效的IPv4地址,如:178.12.34.167. 如果沒有提供此值,客戶端會自動解析系統地址.但如果client運行于LAN中,為了激活數據傳輸,將會使用帶端口轉發的路由器來連接外部服務器,那么自動探測到的地址可能不是正確的. 當系統有多個網絡接口時,也可能發生這種情況.通常使用系統屬性,可以解決這種問題
ftp4j.activeDataTransfer.portRange
連接端口范圍. client會在其中挑選一個來進行數據傳輸.此值 必須是start-stop 形式 ,如6000-7000 表示client只會在給定范圍內挑選一個端口來連接服務器.默認情況下沒有指定端口范圍:這表示client會挑選任何一個可用的端口.
ftp4j.activeDataTransfer.acceptTimeout
以毫秒為單位的連接超時時間. 如果服務器不能在給定超時時間內連接client,傳輸會因FTPDataTransferException異常而中斷.0值表示永不超時。默認值30000 (即30秒).
要設置系統屬性,你可以:
用一個或多個 -Dproperty=value參數來啟動JVM.如:
java -Dftp4j.activeDataTransfer.hostAddress=178.12.34.167 -Dftp4j.activeDataTransfer.portRange=6000-7000 -Dftp4j.activeDataTransfer.acceptTimeout=5000 MyClass
直接在代碼中設置系統屬性,如:
System.setProperty("ftp4j.activeDataTransfer.hostAddress", "178.12.34.167");
System.setProperty("ftp4j.activeDataTransfer.portRange", "6000-7000");
System.setProperty("ftp4j.activeDataTransfer.acceptTimeout", "5000");
你可以調用下面的方法選擇你傳輸的類型:
client.setType(FTPClient.TYPE_TEXTUAL);
client.setType(FTPClient.TYPE_BINARY);
client.setType(FTPClient.TYPE_AUTO);
默認的TYPE_AUTO常量 ,會讓client自動來挑選類型:如果文件的擴展名是client能被識別的文本類型標記,那么它會選擇文本傳輸器來執行. 文件擴展名是通過FTPTextualExtensionRecognizer (it.sauronsoftware.ftp4j.FTPTextualExtensionRecognizer) 實例來識別的. 默認擴展識別器是it.sauronsoftware.ftp4j.recognizers.DefaultTextualExtensionRecognizer, 會將下面的視為文本類型:
abc acgi aip asm asp c c cc cc com conf cpp
csh css cxx def el etx f f f77 f90 f90 flx
for for g h h hh hh hlb htc htm html htmls
htt htx idc jav jav java java js ksh list
log lsp lst lsx m m mar mcf p pas php pl pl
pm py rexx rt rt rtf rtx s scm scm sdml sgm
sgm sgml sgml sh shtml shtml spc ssi talk
tcl tcsh text tsv txt uil uni unis uri uris
uu uue vcs wml wmls wsc xml zsh
你可通過實現FTPTextualExtensionRecognizer 接口來實現你自己的識別器,但你可以更喜歡使用 class ParametricTextualExtensionRecognizer(it.sauronsoftware.ftp4j.recognizers.ParametricTextualExtensionRecognizer)便利類.
無論如何,都不要忘記將你的識別器設置在client中:
client.setTextualExtensionRecognizer(myRecognizer);
有些服務器支持數據傳輸壓縮特性-MODE Z. 在傳輸大文件時,此特性可以節省帶寬.一旦client連上服務器并通過認證,就可通過調用下面的方法來檢查是否支持壓縮:
boolean compressionSupported = client.isCompressionSupported();
如果服務器端支持壓縮,就可通過下面的調用來啟用壓縮:
client.setCompressionEnabled(true);
在此調用之后,后續的數據傳輸(下載,上傳,列舉操作)都被將壓縮以節省帶寬.
數據傳輸壓縮可通過下面的調用來禁用:
client.setCompressionEnabled(false);
也可以檢查標記值:
boolean compressionEnabled = client.isCompressionEnabled();
請注意:壓縮數據傳輸只當壓縮支持且啟用了的情況下才會發生.
默認情況下,壓縮是禁用的,即便是服務器支持壓縮. 如果有需要,可以顯示地打開.
假設你的client什么事情都不做,因為在等待用戶輸入. 通常情況下, FTP服務器會自動斷開非活躍客戶端. 為了避免超時,你可以發送NOOP命令.
此命令不會做任何事情,但它會向服務器說明:客戶端仍然還活著,請重圍超時計數器.調用如下:
client.noop();
當非活躍超時發生時,客戶端也可以自動發送NOOPs. 默認情況下,此特性是禁用的。它可以在 setAutoNoopTimeout() 方法中設置超時時間時啟用,并提供一個毫秒為單位的值.如:
client.setAutoNoopTimeout(30000);
使用此值,client會在30秒后發送一個NOOP命令.
NOOP超時可通過設置小于等于0的值來禁用:
client.setAutoNoopTimeout(0);
你可以像下面這樣來發送站點特殊命令:
FTPReply reply = client.sendSiteCommand("YOUR COMMAND");
你也可以發送自定義命令:
FTPReply reply = client.sendCustomCommand("YOUR COMMAND");
sendSiteCommand() 和 sendCustomCommand() 都會返回一個FTPReply (it.sauronsoftware.ftp4j.FTPReply)對象.使用此對象,你可以檢查服務器的響應代碼和消息.
FTPCodes (it.sauronsoftware.ftp4j.FTPCodes) 接口報告了一些通用的FTP響應代碼,因此你可以使用這些包中的某個來進行匹配.
ftp4j 包定義了五種類型的異常: