Java Socket NIO入门
Java Socket、SocketServer的读写、连接事件监听,都是阻塞式的。Java提供了另外一种非阻塞式读写、连接事件监听方式——NIO。本文简单的介绍一个NIO Socket入门例子,原理以及详细用法,参考后续文章
服务端代码
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; /** * * NIO Socket Server * @author coshaho */ public class NIOServer { public static void main(String[] args) throws IOException { // 启动Socket Server Channel ServerSocketChannel server = ServerSocketChannel.open(); server.bind(new InetSocketAddress(8001)); server.configureBlocking(false); // 绑定选择器,注册连接监听事件 Selector selector = Selector.open(); server.register(selector, SelectionKey.OP_ACCEPT); SelectorHandler handler = new SelectorHandler(); while(true) { // 非阻塞监听注册事件 if(selector.select(2000) == 0) { System.out.println("No selection"); continue; } // 发现注册事件,依次处理 Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator(); while(keyIter.hasNext()) { SelectionKey key = keyIter.next(); if(key.isAcceptable()) { handler.doAccept(key); } if(key.isReadable()) { handler.doRead(key); } if(key.isValid() && key.isWritable()) { handler.doWrite(key); } keyIter.remove(); } } } /** * 事件处理器 * @author h00219638 */ public static class SelectorHandler { // 连接请求处理 public void doAccept(SelectionKey key) throws IOException { SocketChannel socket = ((ServerSocketChannel)key.channel()).accept(); socket.configureBlocking(false); // 注册数据读取事件 socket.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(1024)); } public void doRead(SelectionKey key) throws IOException { SocketChannel socket = (SocketChannel)key.channel(); // 也可以临时分配ByteBuffer ByteBuffer buf = (ByteBuffer) key.attachment(); buf.clear(); if(-1 == socket.read(buf)) { socket.close(); } else { System.out.println("Server received: " + new String(buf.array(), 0, buf.limit())); /** * public static final int OP_READ = 1 << 0; public static final int OP_WRITE = 1 << 2; public static final int OP_CONNECT = 1 << 3; public static final int OP_ACCEPT = 1 << 4; */ // 增加写事件,写事件会不断被触发,数据写完后必须取消写事件监听 key.interestOps(key.interestOps() | SelectionKey.OP_WRITE); } } public void doWrite(SelectionKey key) throws IOException { SocketChannel socket = (SocketChannel)key.channel(); ByteBuffer buf = (ByteBuffer) key.attachment(); // 写数据之前注意调用flip方法,重置指针 buf.flip(); System.out.println("Write: " + new String(buf.array(), 0, buf.limit())); socket.write(buf); if(!buf.hasRemaining()) { // 取消写事件监听 key.interestOps(key.interestOps() &~ SelectionKey.OP_WRITE); } buf.compact(); } } }
客户端代码
import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; import org.drools.core.util.StringUtils; /** * * NIO Socket Client * @author coshaho */ public class NIOClient { public static void main(String[] args) throws IOException { SocketChannel socket = SocketChannel.open(); socket.configureBlocking(false); if(!socket.connect(new InetSocketAddress("localhost", 8001))) { System.out.println("Not connect"); // 正真的连接 while(!socket.finishConnect()) { System.out.println("Not finishConnect"); } } ByteBuffer wBuf = ByteBuffer.wrap("Hello, server".getBytes()); while(wBuf.hasRemaining()) { socket.write(wBuf); } ByteBuffer rBuf = ByteBuffer.allocate(8); StringBuffer sBuf = new StringBuffer(); while(-1 != (socket.read(rBuf))) { rBuf.flip(); String s = new String(rBuf.array(), 0, rBuf.limit()); sBuf.append(s); rBuf.clear(); if(StringUtils.isEmpty(s)) { break; } } System.out.println("Client received: " + sBuf.toString()); socket.close(); } }