1. 程式人生 > >NIO 的示例原始碼(from Thinking in Enterprise Java)

NIO 的示例原始碼(from Thinking in Enterprise Java)

最近在整理NIO相關的知識,我個人毛病,不願意在word裡記錄原始碼,所以直接賦值在這裡了。

(注意:本文不涉及任何講解,程式碼僅示例用,不可直接用於專案)

首先是一段虛擬碼,梳理了怎麼使用selector

package niotest;

//: TIEJ:X1:NonBlockingIO.java
//Socket and selector configuration for non-blocking
//Connects to JabberServer.java
//{RunByHand}
import java.net.*;
import java.nio.channels.*;
import java.util.*;
import java.io.*;

/**As mentioned above you need to create a channel using the open() call.
SocketChannel.open() creates a channel. Since it extends from
AbstractSelectableChannel (DatagramChannel and SocketChannel) it has the
functionality for registering itself to a selector. The register call does this. It takes as
an argument the Selector with to register the channel with and the events that are of
interest to this channel. Here the SocketChannel is shown to be interested in connect,
read and write - hence SelectionKey.OP_CONNECT, SelectionKey.OP_READ and
SelectionKey.OP_WRITE are specified in the register call while registering the
channel with the Selector.
The static call Selector.select() watches all the channels that are registered with it for
the events they registered for (second argument to register). You can have a channel
interested in more than one event. 
*/


/**
 * Aim: Shows how to use selector. No reading/writing just shows the readiness
 * of operations.
 * 
 * PseudoCode: -> Create a selector. -> Create a channel -> Bind the socket
 * associated with this channel to a <client-port> -> Configure the channel as
 * non-blocking -> Register the channel with selector. -> Invoke select() so
 * that it blocks until registered channel is ready. (as opposed to select(long
 * timeout) -> Get the set of keys whose underlying channel is ready for the
 * operation they showed interest when they registered with the selector. ->
 * Iterate through the keys. -> For every key check if the underlying channel is
 * ready for the operation it is interested in. -> If ready print message of
 * readiness.
 * 
 * Notes: -> Need MultiJabberServer running on the local machine. You run it to
 * connect to the local MultiJabberServer -> It causes and exception at
 * MultiJabberServer but this exception is expected.
 */
public class NonBlockingIO {
	public static void main(String[] args) throws IOException {
		if (args.length < 2) {
			System.out.println("Usage: java <client port> <local server port>");
			System.exit(1);
		}
		int cPort = Integer.parseInt(args[0]);
		int sPort = Integer.parseInt(args[1]);
		SocketChannel ch = SocketChannel.open();
		Selector sel = sel = Selector.open();
		try {
			ch.socket().bind(new InetSocketAddress(cPort));
			ch.configureBlocking(false);
			// channel interested in performing read/write/connect
			ch.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE
					| SelectionKey.OP_CONNECT);
			// Unblocks when ready to read/write/connect
			sel.select();
			// Keys whose underlying channel is ready, the
			// operation this channel is interested in can be
			// performed without blocking.
			Iterator it = sel.selectedKeys().iterator();
			while (it.hasNext()) {
				SelectionKey key = (SelectionKey) it.next();
				it.remove();
				// Is underlying channel of key ready to connect?
				// if((key.readyOps() & SelectionKey.OP_CONNECT) != 0) {
				if (key.isConnectable()) {
					InetAddress ad = InetAddress.getLocalHost();
					System.out.println("Connect will not block");
					// You must check the return value of connect to make
					// sure that it has connected. This call being
					// non-blocking may return without connecting when
					// there is no server where you are trying to connect
					// Hence you call finishConnect() that finishes the
					// connect operation.
					if (!ch.connect(new InetSocketAddress(ad, sPort)))
						ch.finishConnect();
				}
				// Is underlying channel of key ready to read?
				// if((key.readyOps() & SelectionKey.OP_READ) != 0)
				if (key.isReadable())
					System.out.println("Read will not block");
				// Is underlying channel of key ready to write?
				// if((key.readyOps() & SelectionKey.OP_WRITE) != 0)
				if (key.isWritable())
					System.out.println("Write will not block");
			}
		} finally {
			ch.close();
			sel.close();
		}
	}
} // /:~

接著是一個客戶端使用Select的形式

package niotest;
/**The next is an example that works like the JabberClient1.java but uses Selector. */
//: TIEJ:X1:JabberClient1.java
//Very simple client that just sends lines to the server
//and reads lines that the server sends.
//{RunByHand}
import java.net.*;
import java.util.*;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;

public class JabberClient1 {
	public static void main(String[] args) throws IOException {
		if (args.length < 1) {
			System.out.println("Usage: java JabberClient1 <client-port>");
			System.exit(1);
		}
		int clPrt = Integer.parseInt(args[0]);
		SocketChannel sc = SocketChannel.open();
		Selector sel = Selector.open();
		try {
			sc.configureBlocking(false);
			sc.socket().bind(new InetSocketAddress(clPrt));
			sc.register(sel, SelectionKey.OP_READ | SelectionKey.OP_WRITE
					| SelectionKey.OP_CONNECT);
			int i = 0;
			// Because of the asynchronous nature you do not know
			// when reading and writing is done, hence you need to
			// keep track of this, boolean written is used to
			// alternate between read and write. Whatever is written
			// is echoed and should be read.
			// boolean done is used to check when to break out of
			// the loop
			boolean written = false, done = false;
			// JabberServer.java to which this client connects writes
			// using BufferedWriter.println(). This method performs
			// encoding according to the defualt charset
			String encoding = System.getProperty("file.encoding");
			Charset cs = Charset.forName(encoding);
			ByteBuffer buf = ByteBuffer.allocate(16);
			while (!done) {
				sel.select();
				Iterator it = sel.selectedKeys().iterator();
				while (it.hasNext()) {
					SelectionKey key = (SelectionKey) it.next();
					it.remove();
					sc = (SocketChannel) key.channel();
					if (key.isConnectable() && !sc.isConnected()) {
						InetAddress addr = InetAddress.getByName(null);
						boolean success = sc.connect(new InetSocketAddress(
								addr, JabberServer.PORT));
						if (!success)
							sc.finishConnect();
					}
					if (key.isReadable() && written) {
						if (sc.read((ByteBuffer) buf.clear()) > 0) {
							written = false;
							String response = cs
									.decode((ByteBuffer) buf.flip()).toString();
							System.out.print(response);
							if (response.indexOf("END") != -1)
								done = true;
						}
					}
					if (key.isWritable() && !written) {
						if (i < 10)
							sc.write(ByteBuffer.wrap(new String("howdy " + i
									+ '\n').getBytes()));
						else if (i == 10)
							sc.write(ByteBuffer.wrap(new String("END\n")
									.getBytes()));
						written = true;
						i++;
					}
				}
			}
		} finally {
			sc.close();
			sel.close();
		}
	}
} // /:~

模擬一個處理多個請求的服務端

package niotest;

//: TIEJ:X1:MultiJabberServer1.java
//Has the same semantics as multi-threaded
//MultiJabberServer
//{RunByHand}
import java.io.*;
import java.net.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
import java.util.*;

/*The next example below shows a simple selector based mechanism for the
MultiJabberServer discussed earlier. This server works the same way as the old one
did but is more efficient in that it does not need a separate thread to handle each
client.*/ 

/**
 * The Server accepts connections in non-blocking fashion. A connection when
 * established, a socket is created, which is registered for read/write with the
 * selector. Reading/Writing is performed on this socket when the selector
 * unblocks. This program works exactly the same way as MultiJabberServer.
 */
public class MultiJabberServer1 {
	public static final int PORT = 8080;

	public static void main(String[] args) throws IOException {
		// Channel read from data will be in ByteBuffer form
		// written by PrintWriter.println(). Decoding of this
		// byte stream requires character set of default encoding.
		String encoding = System.getProperty("file.encoding");
		// Had to initialized here since we do not wish to create
		// a new instance of Charset everytime it is required
		// Charset cs = Charset.forName(
		// System.getProperty("file.encoding"));
		Charset cs = Charset.forName(encoding);
		ByteBuffer buffer = ByteBuffer.allocate(16);
		SocketChannel ch = null;
		ServerSocketChannel ssc = ServerSocketChannel.open();
		Selector sel = Selector.open();
		try {
			ssc.configureBlocking(false);
			// Local address on which it will listen for connections
			// Note: Socket.getChannel() returns null unless a channel
			// is associated with it as shown below.
			// i.e the expression (ssc.socket().getChannel() != null) is true
			ssc.socket().bind(new InetSocketAddress(PORT));
			// Channel is interested in OP_ACCEPT events
			SelectionKey key = ssc.register(sel, SelectionKey.OP_ACCEPT);
			System.out.println("Server on port: " + PORT);
			while (true) {
				sel.select();
				Iterator it = sel.selectedKeys().iterator();
				while (it.hasNext()) {
					SelectionKey skey = (SelectionKey) it.next();
					it.remove();
					if (skey.isAcceptable()) {
						ch = ssc.accept();
						System.out.println("Accepted connection from:"
								+ ch.socket());
						ch.configureBlocking(false);
						ch.register(sel, SelectionKey.OP_READ);
					} else {
						// Note no check performed if the channel
						// is writable or readable - to keep it simple
						ch = (SocketChannel) skey.channel();
						ch.read(buffer);
						CharBuffer cb = cs.decode((ByteBuffer) buffer.flip());
						String response = cb.toString();
						System.out.print("Echoing : " + response);
						ch.write((ByteBuffer) buffer.rewind());
						if (response.indexOf("END") != -1)
							ch.close();
						buffer.clear();
					}
				}
			}
		} finally {
			if (ch != null)
				ch.close();
			ssc.close();
			sel.close();
		}
	}
} // /: