/**
* Fly_m at 2009-5-20
*/
package com.m_ylf.study.nio;
import java.io.File;
import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.Callable;
/** @author Fly_m */
//類比下載服務
public class DownloadServer<T> implements Callable<T>{
private Selector selector;//建立全域selector
private Map<SocketChannel, Handle> map = new HashMap<SocketChannel, Handle>();//socketChannel和handle之間的映射
//建立一個伺服器serverSocketChannel,並與selector進行註冊
public DownloadServer() throws Exception {
selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(new InetSocketAddress(1234));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
//對selector.select進行迭代,並依次進行處理
public T call() throws Exception {
System.out.println("startTo listen in 1234....");
for(; {
selector.select();
Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
while(keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if(key.isValid())
handle(key);
keyIterator.remove();
}
}
}
//處理每個key,對於acceptable的key,由主類進行處理,而其他事件,則由內部類進行處理
private void handle(final SelectionKey key) throws Exception {
if(key.isAcceptable()) {
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = channel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);//註冊讀事件
map.put(socketChannel, new Handle());//把socket和handle進行綁定
}
//用map中的handle處理read和write事件,以類比多個檔案同時進行下載
if(key.isReadable() || key.isWritable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
final Handle handle = map.get(socketChannel);
if(handle != null)
handle.handle(key);
}
}
//內部類,類比一個內部類處理一個檔案下載服務,多個類可以處理多個檔案下載服務
private class Handle{
private StringBuilder message;
private boolean writeOK = true;
private ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
private FileChannel fileChannel;
private String fileName;
private void handle(SelectionKey key) throws Exception {
if(key.isReadable()) {
SocketChannel socketChannel = (SocketChannel) key.channel();
if(writeOK)
message = new StringBuilder();
while(true) {
byteBuffer.clear();
int r = socketChannel.read(byteBuffer);
if(r == 0)
break;
if(r == -1) {
socketChannel.close();
key.cancel();
return;
}
message.append(new String(byteBuffer.array(), 0, r));
}
//將接收到的資訊轉化成檔案名稱,以映射到伺服器上的指定檔案
if(writeOK && invokeMessage(message)) {
socketChannel.register(selector, SelectionKey.OP_WRITE);
writeOK = false;
}
}
//向用戶端寫資料
if(key.isWritable()) {
if(!key.isValid())
return;
SocketChannel socketChannel = (SocketChannel) key.channel();
if(fileChannel == null)
fileChannel = new FileInputStream(fileName).getChannel();
byteBuffer.clear();
int w = fileChannel.read(byteBuffer);
//如果檔案已寫完,則關掉key和socket
if(w <= 0) {
fileName = null;
fileChannel.close();
fileChannel = null;
writeOK = true;
socketChannel.close();
key.channel();
return;
}
byteBuffer.flip();
socketChannel.write(byteBuffer);
}
}
//將資訊轉化成檔案名稱
private boolean invokeMessage(StringBuilder message) {
String m = message.toString();
try {
File f = new File(m);
if(!f.exists())
return false;
fileName = m;
return true;
} catch(Exception e) {
return false;
}
}
}
public static void main(String[] args) throws Exception {
/*
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(new DownloadServer<Object>());
executorService.shutdown();
*/
new DownloadServer().call();
}
}
/** * Fly_m at 2009-5-20 */package com.m_ylf.study.nio;import java.io.RandomAccessFile;import java.nio.ByteBuffer;import java.nio.channels.FileChannel;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;import java.util.Iterator;import java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.net.InetSocketAddress;/** @author Fly_m *///類比下載用戶端public class DownloadClient<T> implements Callable<T>{private FileChannel fileChannel;private static Selector selector;private ByteBuffer byteBuffer = ByteBuffer.allocate(1024);private String serverFileName;//伺服器上的檔案private String localFileName;//下載到用戶端的檔案名稱public DownloadClient(String serverFileName, String localFileName) {this.serverFileName = serverFileName;this.localFileName = localFileName;}public T call() throws Exception {//開啟selector,並建立socket到指定連接埠的串連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);//進行資訊讀取for(; {selector.select();Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();while(keyIterator.hasNext()) {SelectionKey key = keyIterator.next();keyIterator.remove();//串連事件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);}//讀事件if(key.isReadable()) {SocketChannel socketChannel = (SocketChannel) key.channel();byteBuffer.clear();if(!socketChannel.isConnected())return null;//向本機下載檔案建立檔案channelif(fileChannel == null)fileChannel = new RandomAccessFile(localFileName, "rw").getChannel();int r = socketChannel.read(byteBuffer);//如果檔案下載完畢,則關掉channel,同時關掉socketChannelif(r <= 0) {if(fileChannel != null)fileChannel.close();channel.close();key.cancel();return null;}byteBuffer.flip();//寫到下載檔案中fileChannel.write(byteBuffer);}}}}//用戶端用10個線程向伺服器端下載檔案,並儲存為不同的檔案public static void main(String[] args) throws Exception {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();}}