java nio Selector的使用-客戶端
阿新 • • 發佈:2018-12-30
接上一篇,客戶端的程式就相對於簡單了,只需要負責連線,傳送下載檔名,再讀資料就行了。主要步驟就是註冊->連線伺服器->傳送下載請求->讀資料->斷開連線。
第一步:註冊,並註冊connect事件。
Java程式碼- if(selector == null)
- selector = Selector.open();
- SocketChannel channel = SocketChannel.open();
- channel.configureBlocking(false);
- channel.connect(new InetSocketAddress(
- channel.register(selector, SelectionKey.OP_CONNECT);
if(selector == null) selector = Selector.open(); SocketChannel channel = SocketChannel.open(); channel.configureBlocking(false); channel.connect(new InetSocketAddress("localhost", 1234)); channel.register(selector, SelectionKey.OP_CONNECT);
以上步驟,在實際中發現configure和connet並不能隨意交換位置,即不能將connect放在configureBlocking的前面,更不能在open中直接新增InetSocketAddress引數了。在官方doc中,open(InetAddress)的解釋是“這種便捷方法的工作方式就像以下過程一樣:呼叫 open() 方法、在得到的套接字通道上呼叫 connect 方法、向其傳遞 remote,然後返回該通道。”,但不知道為什麼在connect之後,就不能配置block了,導致無法進行資料下載和通訊。
第二步:處理connect事件
Java程式碼- //連線事件
- if
- SocketChannel socketChannel = (SocketChannel) key.channel();
- if(socketChannel.isConnectionPending())
- socketChannel.finishConnect();
- socketChannel.write(ByteBuffer.wrap(serverFileName.getBytes()));//向伺服器發信息,資訊中即伺服器上的檔名
- socketChannel.register(selector, SelectionKey.OP_READ);
- }
//連線事件
if(key.isConnectable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
if(socketChannel.isConnectionPending())
socketChannel.finishConnect();
socketChannel.write(ByteBuffer.wrap(serverFileName.getBytes()));//向伺服器發信息,資訊中即伺服器上的檔名
socketChannel.register(selector, SelectionKey.OP_READ);
}
在以上步驟中,要完成連線之後,向伺服器端傳送了下載的檔名的資料資訊,並註冊read事件。伺服器端在接收到相應檔名之後,就開啟write事件向客戶端進行傳送資料了,客戶端此時就可以進行資料讀取了。
第三步:處理read事件
Java程式碼- if(key.isReadable()) {
- SocketChannel socketChannel = (SocketChannel) key.channel();
- byteBuffer.clear();
- if(!socketChannel.isConnected())
- returnnull;
- //向本機下載檔案建立檔案channel
- if(fileChannel == null)
- fileChannel = new RandomAccessFile(localFileName, "rw").getChannel();
- int r = socketChannel.read(byteBuffer);
- //如果檔案下載完畢,則關掉channel,同時關掉socketChannel
- if(r <= 0) {
- if(fileChannel != null)
- fileChannel.close();
- channel.close();
- key.cancel();
- returnnull;
- }
- byteBuffer.flip();
- //寫到下載檔案中
- fileChannel.write(byteBuffer);
- }
if(key.isReadable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
byteBuffer.clear();
if(!socketChannel.isConnected())
return null;
//向本機下載檔案建立檔案channel
if(fileChannel == null)
fileChannel = new RandomAccessFile(localFileName, "rw").getChannel();
int r = socketChannel.read(byteBuffer);
//如果檔案下載完畢,則關掉channel,同時關掉socketChannel
if(r <= 0) {
if(fileChannel != null)
fileChannel.close();
channel.close();
key.cancel();
return null;
}
byteBuffer.flip();
//寫到下載檔案中
fileChannel.write(byteBuffer);
}
就是處理讀資訊,如果資料已經讀取完畢,則完成相應下載儲存檔案的檔案流,並退出程式。
這樣,整個客戶端就完成了,在執行時,我同時啟用10個執行緒來向伺服器端讀同一個檔案,並儲存為不同的檔案備份,以達到模擬資料傳輸的功能。如下所示:
Java程式碼- ExecutorService executorService = Executors.newSingleThreadExecutor();
- for(int i = 0; i < 10; i++) {
- executorService.submit(new DownloadClient<Object>("d:/log4j.log", "d:/down" + i + ".log"));
- }
- executorService.shutdown();
ExecutorService executorService = Executors.newSingleThreadExecutor();
for(int i = 0; i < 10; i++) {
executorService.submit(new DownloadClient<Object>("d:/log4j.log", "d:/down" + i + ".log"));
}
executorService.shutdown();
整個selector僅是一個作為練習用的小例子,如果用在實際程式碼中,還需要處理不同的異常和相應的邏輯等。對於學習還是有一定的幫助的。希望對你有用。
隨附客戶端程式碼。