(译)java NIO 示例代码

来源:互联网 发布:mac怎么制作手机铃声 编辑:程序博客网 时间:2024/05/20 09:47

NIO的学习实例

****************************************************************************************************************************************************************************************

1, IO机制,所谓IO就是从外设中把数据读入内存,外设可以是磁盘,和网卡等。

传统机制是没来一个IO请求需要创建一个线程来处理该请求,并在读入过程中CPU处于闲置状态,这就是传统的阻塞IO.

2,在互联网高并发的场景下,会有大量的线程,这些线程的切换回耗费大量的资源,从而导致了系统整体的低效,非阻塞IO就是高并发应用场景IO的解决方案。

核心为核心为利用一个线程轮询各个通道。

3,send 方法及RspHandler方法的目的。

4,由于NIO本身的复杂性,导致了在大部分情况下采用Netty框架


NioServer.java

import java.io.IOException;import java.net.InetAddress;import java.net.InetSocketAddress;import java.net.Socket;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.nio.channels.spi.SelectorProvider;import java.util.*;public class NioServer implements Runnable {// The host:port combination to listen onprivate InetAddress hostAddress;private int port;// The channel on which we'll accept connectionsprivate ServerSocketChannel serverChannel;// The selector we'll be monitoringprivate Selector selector;// The buffer into which we'll read data when it's availableprivate ByteBuffer readBuffer = ByteBuffer.allocate(8192);private EchoWorker worker;// A list of PendingChange instances, 这个是用来做什么的呢?private List pendingChanges = new LinkedList();// Maps a SocketChannel to a list of ByteBuffer instancesprivate Map pendingData = new HashMap();public NioServer(InetAddress hostAddress, int port, EchoWorker worker) throws IOException {this.hostAddress = hostAddress;this.port = port;this.selector = this.initSelector();this.worker = worker;}//这个方法中有什么机制呢?或者说这个方法是在什么条件下调用的呢public void send(SocketChannel socket, byte[] data) {synchronized (this.pendingChanges) {// Indicate we want the interest ops set changedthis.pendingChanges.add(new ChangeRequest(socket, ChangeRequest.CHANGEOPS, SelectionKey.OP_WRITE));// And queue the data we want writtensynchronized (this.pendingData) {List queue = (List) this.pendingData.get(socket);if (queue == null) {queue = new ArrayList();this.pendingData.put(socket, queue);}queue.add(ByteBuffer.wrap(data));}}// Finally, wake up our selecting thread so it can make the required changesthis.selector.wakeup();}public void run() {while (true) {try {// Process any pending changessynchronized (this.pendingChanges) {Iterator changes = this.pendingChanges.iterator();while (changes.hasNext()) {ChangeRequest change = (ChangeRequest) changes.next();switch (change.type) {case ChangeRequest.CHANGEOPS:SelectionKey key = change.socket.keyFor(this.selector);key.interestOps(change.ops);}}this.pendingChanges.clear();}// Wait for an event one of the registered channelsthis.selector.select();// Iterate over the set of keys for which events are availableIterator selectedKeys = this.selector.selectedKeys().iterator();while (selectedKeys.hasNext()) {SelectionKey key = (SelectionKey) selectedKeys.next();selectedKeys.remove();if (!key.isValid()) {continue;}// Check what event is available and deal with itif (key.isAcceptable()) {this.accept(key);} else if (key.isReadable()) {this.read(key);} else if (key.isWritable()) {this.write(key);}}} catch (Exception e) {e.printStackTrace();}}}private void accept(SelectionKey key) throws IOException {// For an accept to be pending the channel must be a server socket channel.ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();// Accept the connection and make it non-blockingSocketChannel socketChannel = serverSocketChannel.accept();Socket socket = socketChannel.socket();socketChannel.configureBlocking(false);// Register the new SocketChannel with our Selector, indicating// we'd like to be notified when there's data waiting to be read//对应于阻塞IO时的多线程处理方式socketChannel.register(this.selector, SelectionKey.OP_READ);}//这个部分当在高并发的场景下时需要放在线程池中来处理private void read(SelectionKey key) throws IOException {SocketChannel socketChannel = (SocketChannel) key.channel();// Clear out our read buffer so it's ready for new datathis.readBuffer.clear();// Attempt to read off the channelint numRead;try {numRead = socketChannel.read(this.readBuffer);} catch (IOException e) {// The remote forcibly closed the connection, cancel// the selection key and close the channel.key.cancel();socketChannel.close();return;}if (numRead == -1) {// Remote entity shut the socket down cleanly. Do the// same from our end and cancel the channel.key.channel().close();key.cancel();return;}// Hand the data off to our worker thread,this.worker.processData(this, socketChannel, this.readBuffer.array(), numRead);}private void write(SelectionKey key) throws IOException {SocketChannel socketChannel = (SocketChannel) key.channel();synchronized (this.pendingData) {List queue = (List) this.pendingData.get(socketChannel);// Write until there's not more data ...while (!queue.isEmpty()) {ByteBuffer buf = (ByteBuffer) queue.get(0);socketChannel.write(buf);if (buf.remaining() > 0) {// ... or the socket's buffer fills upbreak;}queue.remove(0);}if (queue.isEmpty()) {// We wrote away all data, so we're no longer interested// in writing on this socket. Switch back to waiting for// data.key.interestOps(SelectionKey.OP_READ);}}}private Selector initSelector() throws IOException {// Create a new selectorSelector socketSelector = SelectorProvider.provider().openSelector();// Create a new non-blocking server socket channelthis.serverChannel = ServerSocketChannel.open();serverChannel.configureBlocking(false);// Bind the server socket to the specified address and portInetSocketAddress isa = new InetSocketAddress(this.hostAddress, this.port);serverChannel.socket().bind(isa);// Register the server socket channel, indicating an interest in // accepting new connectionsserverChannel.register(socketSelector, SelectionKey.OP_ACCEPT);return socketSelector;}public static void main(String[] args) {try {EchoWorker worker = new EchoWorker();new Thread(worker).start();new Thread(new NioServer(null, 9090, worker)).start();} catch (IOException e) {e.printStackTrace();}}}
EchoWorker.java

import java.nio.channels.SocketChannel;import java.util.LinkedList;import java.util.List;//public class EchoWorker implements Runnable {private List queue = new LinkedList();public void processData(NioServer server, SocketChannel socket, byte[] data, int count) {byte[] dataCopy = new byte[count];System.arraycopy(data, 0, dataCopy, 0, count);synchronized(queue) {queue.add(new ServerDataEvent(server, socket, dataCopy));queue.notify();}}public void run() {ServerDataEvent dataEvent;while(true) {// Wait for data to become availablesynchronized(queue) {while(queue.isEmpty()) {try {queue.wait();} catch (InterruptedException e) {}}dataEvent = (ServerDataEvent) queue.remove(0);}// Return to senderdataEvent.server.send(dataEvent.socket, dataEvent.data);}}}
ServerDataEvent.java


import java.nio.channels.SocketChannel;class ServerDataEvent {public NioServer server;public SocketChannel socket;public byte[] data;public ServerDataEvent(NioServer server, SocketChannel socket, byte[] data) {this.server = server;this.socket = socket;this.data = data;}}


ChangeRequest.java

import java.nio.channels.SocketChannel;public class ChangeRequest {public static final int REGISTER = 1;public static final int CHANGEOPS = 2;public SocketChannel socket;public int type;public int ops;public ChangeRequest(SocketChannel socket, int type, int ops) {this.socket = socket;this.type = type;this.ops = ops;}}

NioClient.java
</pre><pre code_snippet_id="1679074" snippet_file_name="blog_20160511_7_4823212" name="code" class="java"><pre name="code" class="java">import java.io.IOException;import java.net.InetAddress;import java.net.InetSocketAddress;import java.net.Socket;import java.nio.ByteBuffer;import java.nio.channels.SelectionKey;import java.nio.channels.Selector;import java.nio.channels.SocketChannel;import java.nio.channels.spi.SelectorProvider;import java.util.*;public class NioClient implements Runnable {// The host:port combination to connect toprivate InetAddress hostAddress;private int port;// The selector we'll be monitoringprivate Selector selector;// The buffer into which we'll read data when it's availableprivate ByteBuffer readBuffer = ByteBuffer.allocate(8192);// A list of PendingChange instancesprivate List pendingChanges = new LinkedList();// Maps a SocketChannel to a list of ByteBuffer instancesprivate Map pendingData = new HashMap();// Maps a SocketChannel to a RspHandlerprivate Map rspHandlers = Collections.synchronizedMap(new HashMap());public NioClient(InetAddress hostAddress, int port) throws IOException {this.hostAddress = hostAddress;this.port = port;this.selector = this.initSelector();}//这个的作用是什么?public void send(byte[] data, RspHandler handler) throws IOException {// Start a new connectionSocketChannel socket = this.initiateConnection();// Register the response handlerthis.rspHandlers.put(socket, handler);// And queue the data we want writtensynchronized (this.pendingData) {List queue = (List) this.pendingData.get(socket);if (queue == null) {queue = new ArrayList();this.pendingData.put(socket, queue);}queue.add(ByteBuffer.wrap(data));}// Finally, wake up our selecting thread so it can make the required changesthis.selector.wakeup();}public void run() {while (true) {try {// Process any pending changessynchronized (this.pendingChanges) {Iterator changes = this.pendingChanges.iterator();while (changes.hasNext()) {ChangeRequest change = (ChangeRequest) changes.next();switch (change.type) {case ChangeRequest.CHANGEOPS:SelectionKey key = change.socket.keyFor(this.selector);key.interestOps(change.ops);break;case ChangeRequest.REGISTER:change.socket.register(this.selector, change.ops);break;}}this.pendingChanges.clear();}// Wait for an event one of the registered channelsthis.selector.select();// Iterate over the set of keys for which events are availableIterator selectedKeys = this.selector.selectedKeys().iterator();while (selectedKeys.hasNext()) {SelectionKey key = (SelectionKey) selectedKeys.next();selectedKeys.remove();if (!key.isValid()) {continue;}// Check what event is available and deal with itif (key.isConnectable()) {this.finishConnection(key);} else if (key.isReadable()) {this.read(key);} else if (key.isWritable()) {this.write(key);}}} catch (Exception e) {e.printStackTrace();}}}private void read(SelectionKey key) throws IOException {SocketChannel socketChannel = (SocketChannel) key.channel();// Clear out our read buffer so it's ready for new datathis.readBuffer.clear();// Attempt to read off the channelint numRead;try {numRead = socketChannel.read(this.readBuffer);} catch (IOException e) {// The remote forcibly closed the connection, cancel// the selection key and close the channel.key.cancel();socketChannel.close();return;}if (numRead == -1) {// Remote entity shut the socket down cleanly. Do the// same from our end and cancel the channel.key.channel().close();key.cancel();return;}// Handle the responsethis.handleResponse(socketChannel, this.readBuffer.array(), numRead);}private void handleResponse(SocketChannel socketChannel, byte[] data, int numRead) throws IOException {// Make a correctly sized copy of the data before handing it// to the clientbyte[] rspData = new byte[numRead];System.arraycopy(data, 0, rspData, 0, numRead);// Look up the handler for this channelRspHandler handler = (RspHandler) this.rspHandlers.get(socketChannel);// And pass the response to itif (handler.handleResponse(rspData)) {// The handler has seen enough, close the connectionsocketChannel.close();socketChannel.keyFor(this.selector).cancel();}}private void write(SelectionKey key) throws IOException {SocketChannel socketChannel = (SocketChannel) key.channel();synchronized (this.pendingData) {List queue = (List) this.pendingData.get(socketChannel);// Write until there's not more data ...while (!queue.isEmpty()) {ByteBuffer buf = (ByteBuffer) queue.get(0);socketChannel.write(buf);if (buf.remaining() > 0) {// ... or the socket's buffer fills upbreak;}queue.remove(0);}if (queue.isEmpty()) {// We wrote away all data, so we're no longer interested// in writing on this socket. Switch back to waiting for// data.key.interestOps(SelectionKey.OP_READ);}}}private void finishConnection(SelectionKey key) throws IOException {SocketChannel socketChannel = (SocketChannel) key.channel();// Finish the connection. If the connection operation failed// this will raise an IOException.try {socketChannel.finishConnect();} catch (IOException e) {// Cancel the channel's registration with our selectorSystem.out.println(e);key.cancel();return;}// Register an interest in writing on this channelkey.interestOps(SelectionKey.OP_WRITE);}private SocketChannel initiateConnection() throws IOException {// Create a non-blocking socket channelSocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false);// Kick off connection establishmentsocketChannel.connect(new InetSocketAddress(this.hostAddress, this.port));// Queue a channel registration since the caller is not the // selecting thread. As part of the registration we'll register// an interest in connection events. These are raised when a channel// is ready to complete connection establishment.synchronized(this.pendingChanges) {this.pendingChanges.add(new ChangeRequest(socketChannel, ChangeRequest.REGISTER, SelectionKey.OP_CONNECT));}return socketChannel;}private Selector initSelector() throws IOException {// Create a new selectorreturn SelectorProvider.provider().openSelector();}public static void main(String[] args) {try {NioClient client = new NioClient(InetAddress.getByName("www.google.com"), 80);Thread t = new Thread(client);t.setDaemon(true);t.start();RspHandler handler = new RspHandler();client.send("GET / HTTP/1.0\r\n\r\n".getBytes(), handler);handler.waitForResponse();} catch (Exception e) {e.printStackTrace();}
</pre><pre code_snippet_id="1679074" snippet_file_name="blog_20160511_7_4823212" name="code" class="java">}
<span style="font-family: Arial, Helvetica, sans-serif;">public class RspHandler {</span>
private byte[] rsp = null;public synchronized boolean handleResponse(byte[] rsp) {this.rsp = rsp;this.notify();return true;}public synchronized void waitForResponse() {while(this.rsp == null) {try {this.wait();} catch (InterruptedException e) {}}System.out.println(new String(this.rsp));}}





0 0
原创粉丝点击