BIO(Blocking I/0) 为阻塞IO,NIO(Non-Blocking I/O)为非阻塞IO。
不推荐以BIO构建生产应用,它有以下特点
- 阻塞式I/O
- 弹性伸缩能力差
- 多线程耗资源
针对以上问题,NIO可以完美解决,下面为学习NIO,使用Java 原生API编写NIO聊天室,上生产的话参考封装NIO的Netty框架。
NioServer.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
| package ml.yihao;
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.nio.charset.Charset; import java.util.Iterator; import java.util.Set;
public class NioServer {
private static final String ENCODING_UTF8 = "UTF-8"; private static final Integer BUFFER_SIZE = 1024;
private static String hostname = "127.0.0.1"; private static Integer port = 8081;
public void start() throws IOException {
Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.bind(new InetSocketAddress(hostname, port)); serverSocketChannel.configureBlocking(false);
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
for (;;){ int select = selector.select(); if(select == 0) { continue; } Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectedKeys.iterator(); while(iterator.hasNext()){
SelectionKey selectionKey = iterator.next(); if(selectionKey.isAcceptable()){ acceptHandler(serverSocketChannel, selector); }
if(selectionKey.isReadable()){ readHandler(selectionKey , selector); }
iterator.remove(); }
} }
public void acceptHandler(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
socketChannel.write(Charset.forName(ENCODING_UTF8).encode("欢迎来到zyh搭建的聊天室!")); }
public void readHandler(SelectionKey selectionKey, Selector selector) throws IOException {
SocketChannel socketChannel = (SocketChannel)selectionKey.channel(); ByteBuffer byteBuffer = ByteBuffer.allocate(BUFFER_SIZE); String result = ""; while(socketChannel.read(byteBuffer) > 0){
byteBuffer.flip();
result += Charset.forName(ENCODING_UTF8).decode(byteBuffer); }
if(result.length() > 0){ broadcast(selector, socketChannel, result); } socketChannel.register(selector, SelectionKey.OP_READ); }
public void broadcast(Selector selector,SocketChannel sourceChannel, String msg) throws IOException {
Set<SelectionKey> selectionKeys = selector.keys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()){ SelectionKey selectionKey = iterator.next(); Channel targetChannel = selectionKey.channel();
if(targetChannel instanceof SocketChannel && targetChannel != sourceChannel){
((SocketChannel)targetChannel).write(Charset.forName(ENCODING_UTF8).encode(msg)); }
} }
public static void main(String[] args) throws IOException { NioServer server = new NioServer(); server.start(); }
}
|
NioClient.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
| package ml.yihao;
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.SocketChannel; import java.nio.charset.Charset; import java.util.Iterator; import java.util.Objects; import java.util.Scanner; import java.util.Set;
public class NioClient {
private static final String ENCODING_UTF8 = "UTF-8"; private static final Integer BUFFER_SIZE = 1024;
private static String hostname = "127.0.0.1"; private static Integer port = 8081;
public void start() throws IOException {
SocketChannel socketChannel = SocketChannel. open(new InetSocketAddress(hostname, port));
Selector selector = Selector.open(); socketChannel.configureBlocking(false); socketChannel.register(selector, SelectionKey.OP_READ); new Thread(new NioClient.ReadThread(selector)).start();
Scanner scanner = new Scanner(System.in); while(scanner.hasNext()){
String line = scanner.next(); socketChannel.write(Charset.forName(ENCODING_UTF8).encode(line)); }
}
public static void main(String[] args) throws IOException {
NioClient nioClient = new NioClient(); nioClient.start(); }
class ReadThread implements Runnable{
private Selector selector;
protected ReadThread(Selector selector){ this.selector = selector; }
@Override public void run() {
for (;;){ try { int select = selector.select(); if(select == 0) continue;
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator(); while(iterator.hasNext()){ SelectionKey selectionKey = iterator.next();
if(selectionKey.isReadable()){ readHanler(selectionKey, selector); }
iterator.remove(); }
} catch (IOException e) { e.printStackTrace(); } } }
public void readHanler(SelectionKey selectionKey, Selector selector) throws IOException {
ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE); SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
String message = null; while(socketChannel.read(buffer) > 0){
buffer.flip();
message += Charset.forName(ENCODING_UTF8).decode(buffer); }
if(Objects.nonNull(message) && message.length() > 0){ System.out.println(message); }
socketChannel.register(selector, SelectionKey.OP_READ); }
} }
|