网络编程之NIO
NIO是一种多路复用机制,它的思想是一个线程处理一个请求,通过将所有连接存在一个多路复用器中,然后通过轮询这个多路复用器查看是否有连接发送了事件,当发现了有事件发生时(如IO操作),则调用线程来处理这个连接,相比BIO能处理的连接数大大提高,并且线程利用率也高了很多。这里要注意NIO底层是epoll实现的,相对于select有更大的优势。
代码相关:
我这里为了编程简单,体现NIO的大体思路,还没有使用到线程池,而是基于单线程来处理连接请求和读写操作,真正开发中应该是使用一个线程来处理连接请求,然后一个线程池来处理读写操作。
大体思路:
- 建立
ServerSocketChannel
绑定端口和IP - 给每个连接插口设置一个key
- 轮询这些key,将发生状态变化的key保存到一个Set中
- 轮询这个Set,判断这些key是发生了连接请求还是读写操作,并作相应处理…
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class Server {
public static void main(String[] args) throws IOException {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.socket().bind(new InetSocketAddress("127.0.0.1", 8888));
ssc.configureBlocking(false);
System.out.println("server started, listening on :" + ssc.getLocalAddress());
//建立多路复用器
Selector selector = Selector.open();
//在上面为每个插口设置key
ssc.register(selector, SelectionKey.OP_ACCEPT);
while(true) {
//阻塞方法,当有连接请求时继续运行
selector.select();
//轮询发生了状态变化的key提取出来
Set<SelectionKey> keys = selector.selectedKeys();
Iterator<SelectionKey> it = keys.iterator();
while(it.hasNext()) {//轮询发生事件的key,查看时出现了连接请求还是读写操作
SelectionKey key = it.next();
it.remove();
handle(key);
}
}
}
private static void handle(SelectionKey key) {
if(key.isAcceptable()) {//发生了建立连接
try {
//初始化通道
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
//建立连接
SocketChannel sc = ssc.accept();
//设置为非阻塞模式
sc.configureBlocking(false);
sc.register(key.selector(), SelectionKey.OP_READ );
} catch (IOException e) {
e.printStackTrace();
} finally {
}
} else if (key.isReadable()) { //flip 发生了读写事件
SocketChannel sc = null;
try {
sc = (SocketChannel)key.channel();
ByteBuffer buffer = ByteBuffer.allocate(512);
buffer.clear();
int len = sc.read(buffer);
if(len != -1) {
System.out.println(new String(buffer.array(), 0, len));
}
ByteBuffer bufferToWrite = ByteBuffer.wrap("HelloClient".getBytes());
sc.write(bufferToWrite);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(sc != null) {
try {
sc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
总结:
NIO相对于BIO能处理更多的连接数,并发性能更好,不过编程难度也大很多,在实际开发中,可以使用Netty来开发,它实现了很多封装并对NIO中的很多可能出现的问题进行了处理。