JAVA中的BIO、NIO、EPOLL
前几天听了清华周老师讲的nio的课,感觉讲得很清楚,所以记录一下,一方面自己理解一次,加深印象,另一方面,防止后面忘记,时长回顾;以下是自己的理解内容,表达能力有限,仅供个人参考:
一、BIO
BIO:同其名字,阻塞IO,accept、read、connect、write操作都会阻塞;

BIO代码示例:
public class NIOExample {
final static int PORT = 8088;
final static ThreadPoolExecutor executor =
new ThreadPoolExecutor(3,3,3L, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(100));
public static void main(String[] args) {
final ConcurrentLinkedQueue<SocketChannel> clients = new ConcurrentLinkedQueue();
try {
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(PORT));
//配置非阻塞
serverSocketChannel.configureBlocking(false);
InetSocketAddress local = (InetSocketAddress)serverSocketChannel.getLocalAddress();
System.out.println(local.getAddress());
//一个线程负责处理有效连接上的数据
new Thread(new Runnable(){
public void run() {
connectionHandle(clients);
}
}).start();
//一个线程负责接收连接
while(true){
//非阻塞,没有客户端连接进来,返回-1,BIO会一直阻塞
SocketChannel client = serverSocketChannel.accept();
if(client != null){
client.configureBlocking(false);
clients.add(client);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void connectionHandle(ConcurrentLinkedQueue<SocketChannel> clients) {
while(true){
final SocketChannel client = clients.poll();
final ByteBuffer byteBuffer = ByteBuffer.allocate(512);
if(client != null){
executor.execute(new Runnable() {
public void run() {
try {
InetSocketAddress remoteAddress = (InetSocketAddress)client.getRemoteAddress();
System.out.println("收到消息,对方端口号:"+remoteAddress.getPort());
client.read(byteBuffer);
byteBuffer.flip();
System.out.println("收到消息:"+new String(byteBuffer.array()));
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
}
}
三、epoll
在系统内核中select、poll、epoll都是多路复用器,java中对多路复用器的封装是在Selector中;

selector代码示例:
public class SelectorExample { public static void main(String[] args) throws IOException { Selector selector = Selector.open(); ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //selector多路复用器必须是非阻塞的 serverSocketChannel.configureBlocking(false); serverSocketChannel.bind(new InetSocketAddress("127.0.0.1",8088)); serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); ByteBuffer readBuff = ByteBuffer.allocate(1024); ByteBuffer writeBuff = ByteBuffer.allocate(1024); while(iterator.hasNext()){ try { SelectionKey selectionKey = iterator.next(); if(selectionKey.isAcceptable()){ //接收到连接。做什么事 // 创建新的连接,并且把连接注册到selector上,而且, // 声明这个channel只对读操作感兴趣。 SocketChannel socketChannel = serverSocketChannel.accept(); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); }else if(selectionKey.isReadable()){ //客户端有数据发送过来,读数据 SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); readBuff.clear(); socketChannel.read(readBuff); readBuff.flip(); System.out.println("received : " + new String(readBuff.array())); selectionKey.interestOps(SelectionKey.OP_WRITE); }else if(selectionKey.isWritable()){ writeBuff.rewind(); SocketChannel socketChannel = (SocketChannel) selectionKey.channel(); socketChannel.write(writeBuff); selectionKey.interestOps(SelectionKey.OP_READ); } } finally { iterator.remove(); } } } }
epoll的总结:
1.Server向selector注册事件
2.selector调用系统内核,系统内核调用epoll_create方法为绑定的端口分配一块内存
3.当用channel连接进来时,会记录channel的元数据信息,调用内核方法epoll_ctl方法,将信道元数据保存到内存中,
4.当信道有数据传送过来时,系统内核将传送的数据保存到channel对应的queue中
5.selector调用系统内核epoll_wait方法,对queue中存放的数据进行读取

浙公网安备 33010602011771号