概述
InterruptibleChannel:可被異步關閉和中斷的通道
GatheringByteChannel:可從緩衝區序列寫入字節的通道
ScatteringByteChannel:可將字節讀入緩衝區序列的通道
SelectableChannel:可通過 Selector 實現多路複用的通道
傳統的socket總是阻塞方式進行工作,嚴重影響應用的擴展性,這些socket通道類有一個公共的父類SelectableChannel,通過調用父類的configureBlocking和isBlocking方法來設置是否阻塞和檢測是否阻塞。
ServerSocketChannel
監聽套接字通道,與ServerSocket是一個對等體,ServerSocket中的API在這裏也適用,這裏主要看看它的accept方法,accept方法主要做了幾下幾件事:1、確保此通道是打開的且已被綁定,否則會拋出異常;
2、調用本地方法建立一個socket連接,socket有對應的文件描述符和InetSocketAddress
3、進行安全檢查
4、設置爲阻塞模式,構建一個SocketChannel,然後返回
因此,不管此通道的阻塞模式如何,此方法返回的套接字通道(如果有)將處於阻塞模式。
如果此通道處於非阻塞模式,那麼在不存在掛起的連接時,此方法將直接返回 null,這樣可以提高程序的可伸縮性並降低複雜度,檢查是否連接的代碼如下:
ServerSocketChannel ssc = ServerSocketChannel.open( );
ssc.socket().bind (new InetSocketAddress (port));
//非阻塞
ssc.configureBlocking (false);
while (true) {
SocketChannel sc = ssc.accept( );
// 在非阻塞模式下,當沒有連接在等待時會返回null
if (sc == null) {
Thread.sleep(TIME);
else {
doSomeThing();
sc.close();
}
}
SocketChannel
面向連接的套接字通道,其對等體是socket。通過在通道上直接調用connect( )方法或在通道關聯的Socket對象上調用connect( )來將該socket通道連接:(1)如果選擇使用傳統方式進行連接,線程在連接建立好或超時過期之前都將保持阻塞;
(2)如果選擇在通道上直接調用connect( )方法來建立連接並且通道處於阻塞模式(默認模式),那麼連接過程和(1)是一樣的;
(3)如果在非阻塞模式下調用通道的connect()方法進行連接會立即返回,如果返回值是true,說明連接立即建立了,如果連接不能立即建立,connect( )方法會返回false且併發地繼續連接建立過程。後續可以通過調用finishConnect( )方法來完成連接過程,該方法任何時候都可以安全地進行調用。
2、連接建立過程正在進行尚未完成,那麼什麼都不會發生,finishConnect( )方法會立即返回false值;
3、在非阻塞模式下調用connect( )方法之後,SocketChannel又被切換回了阻塞模式,調用線程會阻塞直到連接建立完成,finishConnect( )方法接着就會返回true;
4、調用後剛好連接過程已經完成,那麼SocketChannel對象的內部狀態將被更新到已連接狀態,finishConnect( )方法會返回true;
5、連接已經建立,再次調用什麼都不會發生,finishConnect( )方法會返回true。
這裏提下狀態更新,在非阻塞下調用connect方法時,狀態爲ST_PENDING,在此狀態下isConnected()方法返回false,由於在進行讀寫操作前要確保通道是連接的,所以這個狀態下的通道還不能進行讀寫操作,狀態由ST_PENDING變爲ST_CONNECTED正是在finishConnect方法中完成的。
Socket通道是線程安全的。併發訪問時無需採用特別措施來保護髮起訪問的多個線程。connect( )和finishConnect( )方法是互相同步的(都會鎖住readLock和writeLock),並且只要其中一個操作正在進行,任何讀或寫的方法調用都會被阻塞,即使是在非阻塞模式下。如果不能忍受一個讀或寫操作在某個通道上阻塞,最好先調用isConnected( )方法檢查連接狀態。
DatagramChannel
數據報socket的無狀態性質不需要同遠程系統進行對話來建立連接狀態,由於此原因,DatagramChannel上也就沒有單獨的finishConnect( )方法。我們可以使用isConnected( )方法來測試一個數據報通道的連接狀態。不同於SocketChannel(必須連接了纔有用並且只能連接一次),DatagramChannel對象可以任意次數地進行連接或斷開連接。每次連接都可以到一個不同的遠程地址。調用disconnect( )方法可以配置通道,以便它能再次接收來自安全管理器(如果已安裝)所允許的任意遠程地址的數據或發送數據到這些地址上。
當一個DatagramChannel處於已連接狀態時,發送數據將不用提供目的地址而且接收時的源地址也是已知的。這意味着DatagramChannel已連接時可以使用常規的read( )和write( )方法。
synchronized (stateLock) {
if (!isConnected()) {
if (target == null)
throw new NullPointerException();
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
if (ia.isMulticastAddress()) {
sm.checkMulticast(isa.getAddress());
} else {
sm.checkConnect(isa.getAddress().getHostAddress(),
isa.getPort());
}
}
} else { // Connected case; Check address then write
if (!target.equals(remoteAddress)) {
throw new IllegalArgumentException(
"Connected address not equal to target address");
}
return write(src);
}
}