流通道有兩個變體:SocketChannel 和ServerSocketChannel。像其對應的Socket 一樣,SocketChannel 是相互串連的終端進行通訊的通道。 SocketChannel: 建立,串連和關閉 static SocketChannel open(SocketAddress remote) static SocketChannel open() boolean connect(SocketAddress remote) boolean isConnected() void close() boolean isOpen() Socket socket() 調用SocketChannel 的靜態Factory 方法open()可以建立一個執行個體。open()方法的第一種形式以SocketAddress(見第2 章)為參數,返回一個串連到指定伺服器的SocketChannel 執行個體。注意,該方法可能會無限期地阻塞下去。open()的無參數形式用於建立一個沒有串連的SocketChannel 執行個體,該執行個體可以通過調用connect()方法串連到指定終端。當使用完SocketChannel 後,需要調用close()方法將其關閉。有一點很重要,即每個SocketChannel執行個體都"包裹"了一個基本Java Socket,並可以通過socket()方法對該Socket 進行訪問。這就可以通過基本的Socket 方法進行綁定、設定通訊端選項等操作。一個SocketChannel 的建立、串連和關閉的例子,見TCPEchoClientNonblocking.java(第113-114 頁)。 在建立並串連SocketChannel 後,就可以調用該通道的讀寫方法進行I/O 操作。 SocketChannel: 讀和寫 int read(ByteBuffer dst) long read(ByteBuffer[] dsts) long read(ByteBuffer[] dsts, int offset, int length) int write(ByteBuffer src) long write(ByteBuffer[] srcs) long write(ByteBuffer[] srcs, int offset, int length) 讀操作的最基本形式以一個ByteBuffer 為參數,並將讀取的資料填入該緩衝區所有的剩餘位元組空間中。另一種形式以多個ByteBuffer 為參數(ByteBuffer 數組),並根據其在數組中的順序,將讀取的資料依次填入每個緩衝區的剩餘位元組空間中。這種方法稱為散射式讀,因為它將讀入的位元組分散到了多個緩衝區中。需要注意重要的一點,散射式讀不一定會將所有緩衝區填滿,這些緩衝區的總空間大小隻是一個上限。 寫操作的最基本形式以一個ByteBuffer 為參數,並試圖將該緩衝區中剩餘的位元組寫入通道。另一種形式以一個ByteBuffer 數組作為參數,並試圖將所有緩衝區中的剩餘位元組都寫入通道。這種方法稱為聚集式寫,因為它把多個緩衝區中的位元組聚集起來,一起發送出去。讀 寫操作的例子見TCPEchoClientNonblocking.java和TCPServerSelector.java 。 與其對應的ServerSocket 一樣,ServerSocketChannel 是用來偵聽用戶端串連的通道。 ServerSocketChannel: 建立,接受和關閉 static ServerSocketChannel open() ServerSocket socket() SocketChannel accept() void close() boolean isOpen() 調用靜態Factory 方法open()可以建立一個ServerSocketChannel 執行個體。每個執行個體都包裹了一個ServerSocket 執行個體,並可以通過socket()方法對其訪問。正如前面的例子所表明的,必須通過訪問底層的ServerSocket 執行個體來實現綁定指定連接埠,設定通訊端選項等操作。在建立了通道執行個體並綁定連接埠後,就可以調用accept()方法來準備接收用戶端的串連請求。串連成功則返回一個新的已串連的SocketChannel。在用完ServerSocketChannel 後,需要調用close()方法將其關閉。使用ServerSocket 的例子見TCPServerSelector.java(第116-117 頁)。 如前文提到的那樣,阻塞式通道除了能夠(必須)與Buffer 一起使用外,對於普通通訊端來說幾乎沒有優點。因此,可能總是需要將通道設定成非阻塞式的。 SocketChannel, Server SocketChannel: 設定阻塞行為 SelectableChannel configureBlocking(boolean block) boolean isBlocking() 通過調用configureBlocking(false)可以將SocketChannel 或ServerSocketChannel 設定為非阻塞模式。configureBlocking()方法將返回一個SelectableChannel,它是SocketChannel 和ServerSocketChannel 父類。 考慮為SocketChannel 設定串連的情況。如果傳給SocketChannel 的Factory 方法open()一個遠程地址,對該方法的調用則將阻塞等待,直到成功建立了串連。要避免這種情況,可以使用open()方法的無參數形式,配置通道為非阻塞模式,再調用connect()方法,指定遠程終端地址。如果在沒有阻塞的情況下串連已經建立,connect()方法返回true;否則需要有檢查通訊端是否串連成功的方法。 SocketChannel: 測試連接性 boolean finishConnect() boolean isConnected() boolean isConnectionPending() 對於非阻塞SocketChannel 來說,一旦已經發起串連,底層通訊端可能即不是已經串連,又不是沒有串連,而是串連"進行中"。由於底層協議的工作機制(見第6 章),通訊端可能會在這個狀態一直保持下去。finishConnect()方法可以用來檢查在非阻塞通訊端上試圖進行的串連的狀態,還可以在阻塞通訊端建立串連的過程中阻塞等待,直到串連成功建立。例如,你可能需要將通道配置成非阻塞模式,通過connect()方法發起串連,做完一些其他工作後,又將通道配置成阻塞模式,然後調用finishConnect()方法等待串連建立完成。或者可以讓通道保持在非阻塞模式,並反覆調用finishConnect()方法,如TCPEchoClientNonblocking.java 中所示。 isConnected()用於檢查通訊端是否已經建立了串連,從而避免在進行其他動作時拋出NotYetConnectedException 異常(如調在用read()或write()時)。還可以使用isConnectionPending()方法來檢查是否有串連在該通道上發起。知道是否有串連發起是必要的,因為如果沒有的話,finishConnect()方法將拋出NoConnectionPendingException 異常。