[TOC]
Java Asynchronous nonblocking io NiO uses the NIO implementation of the Code Analysis Timeserver program to implement the complete code
Timeserver program from the book "Netty Authoritative Guide", NiO code is really a bit difficult to understand (this is one of the reasons behind the need to use Netty), but I commented on the code, so that the concept of NIO and basic use will have a very clear understanding:
Service-side Programs
Timeserver.java:
package cn.xpleaf.nio;public class TimeServer { public static void main(String[] args) { int port = 8080; if (args != null && args.length > 0) { try { port = Integer.valueOf(port); } catch (Exception e) { // 采用默认值 } } new Thread(new MultiplexerTimeServer(port)).start(); }}
Multiplexertimeserver.java:
Package Cn.xpleaf.nio;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.sql.Date;import Java.util.iterator;import Java.util.set;public class Multiplexertimeserver implements Runnable {private Selector Selec Tor Private Serversocketchannel Servchannel; Private volatile Boolean stop; /** * Initializing multiplexer, binding listening port */public multiplexertimeserver (int port) {try {//create multiplexer Selector selector = Selector.open (); Create Serversocketchannel, which is equivalent to the parent pipe Servchannel = Serversocketchannel.open () for all client connections; Set the Serversocketchannel to asynchronous non-blocking servchannel.configureblocking (false); The bound listening port, backlog 1024, indicates that the maximum number of clients Serverchannel accommodates is 1024 (the result of personal lookup data is not necessarily accurate) servchannel.socket (). bind (New INETSOC KetaddreSS (Port), 1024); Register the Serversocketchannel on the selector and listen for selectionkey.op_accept operation Bit Servchannel.register (selector, Selectionkey.op _accept); System.out.println ("The time server is start in port:" + port); } catch (IOException e) {e.printstacktrace (); System.exit (1); }} public void Stop () {this.stop = true; @Override public void Run () {while (!stop) {try {//timeout-if positive, waiting for a channel to be prepared is blocked for up to timeout milliseconds, if zero, is blocked indefinitely, must be non-negative (API documentation)//sleep time is 1s, regardless of whether there are read or write events, selector every 1s is awakened once sele Ctor.select (1000); set<selectionkey> Selectedkeys = Selector.selectedkeys (); Iterator<selectionkey> it = Selectedkeys.iterator (); Selectionkey key = null; while (It.hasnext ()) {///Gets the key value, by manipulating the key, you can obtain the channel to which it is registered to selector//initially there is only one A ServersockeThe corresponding key for TChannel, which is the Servchannel created earlier, is equivalent to the server of the parent pipe//NIO of all client connections through which it creates a connection to the client, because the current code only listens for it selectio nkey.op_accept operation Bit key = It.next (); The key value is removed from the Selectedkeys set It.remove (); Handle the key value of try {handleinput (key); } catch (Exception e) {if (key! = null) {Key.cancel (); if (key.channel () = null) {Key.channel (). Close (); }}}}} catch (Throwable t) {//To Do auto-generated catch block T.printstacktrace (); }///multiplexer closed, all resources registered on the channel and pipe are automatically registered and closed, so there is no need to re-release resources if (selector! = null) {try {Selector.close (); } catch (IoexceptiOn e) {e.printstacktrace (); }}}/** * Processing key * * @param key * @throws IOException */public void Handleinput ( Selectionkey key) throws IOException {//Process new Access Request message if (Key.isvalid ()) {///connection established if (Key.isacceptable ()) {//Receive new connection Serversocketchannel SSC = (serversocketchannel) key.channel (); Socketchannel sc = ssc.accept (); Set Socketchannel to asynchronous non-blocking sc.configureblocking (false); Register the new connection to the multiplexer selector, monitor the selectionkey.op_read operation bit Sc.register (selector, selectionkey.op_read); }//Read data if (key.isreadable ()) {//through key gets to its channel SOC registered on selector Ketchannel sc = (socketchannel) key.channel (); Allocates a new byte buffer with a size of 1024KB, which is 1MB bytebuffer readbuffer = bytebuffer.allocate (1024); Because the S is already in front of theOcketchannel is set to asynchronous non-blocking mode, so its read non-blocking//return value is read to the number of bytes//return value is different, meaning different:/** * Size 0: Read bytes, encode bytes to 0: not read to Byte, belong to normal scene, ignored as-1: Link closed, need to close socketchannel, release resources */I NT Readbytes = Sc.read (Readbuffer); if (Readbytes > 0) {//read to Byte, decode operation//set current limit of buffer to position,position set to 0 for subsequent Read operations on buffers (I think this is defined in the API) Readbuffer.flip (); Creates a byte array based on the number of bytes that are readable by the buffer byte[] bytes = new byte[readbuffer.remaining ()]; Copies the buffer-readable byte array into the newly created byte array readbuffer.get (bytes); Converts a byte array to a string in utf-8 form, BODY = new string (bytes, "Utf-8"); System.out.println ("The time server receive order:" + body); Parses the instructions sent by the client while constructing the return result String currenttime = "QUERY time ORDER". Equalsignorecase (Body) ? New Date (System.currenttimemillis ()). ToString (): "Bad ORDER"; Asynchronously sends the reply message to the client Dowrite (SC, currenttime); } else if (Readbytes < 0) {//Peer link off key.cancel (); Sc.close (); } else {;//read to 0 bytes ignored}}}}/** * Asynchronously sends the reply message to the client * * @param Channel * @param response * @throws IOException */public void Dowrite (Socketchannel channel, STR ing response) throws IOException {if (response! = null && Response.trim (). Length () > 0) {// Encodes a string into a byte array byte[] bytes = Response.getbytes (); Creates bytebuffer bytebuffer WriteBuffer = bytebuffer.allocate (bytes.length) based on the capacity of the byte array; Copies the byte array to the buffer Writebuffer.put (bytes); Flip Operation Writebuffer.flip (); WillA byte array of buffers is sent out channel.write (WriteBuffer); /** * Note that this does not deal with half-pack issues, as described in the Netty authoritative guide (P35) * It is important to note that because Socketchannel is asynchronous and non-blocking, it does not guarantee that a byte array that needs to be sent can be sent out at once, There is a half-packet problem at this point. * We need to register the write operation, constantly polling selector will not send the finished Bytebuffer sent complete, and then through the Bytebuffer hasremain () method * To determine whether the message is sent. Here is just a simple entry-level routine that does not demonstrate how to handle the "write half-Package" scenario, which is explained in detail in subsequent chapters. */ } }}
Client programs
Timeclient.java:
package cn.xpleaf.nio;public class TimeClient { public static void main(String[] args) { int port = 8080; if(args != null && args.length > 0) { try { port = Integer.valueOf(port); } catch (Exception e) { // 采用默认值 } } new Thread(new TimeClientHandle("127.0.0.1", port)).start(); }}
Timeclienthandle.java:
Package Cn.xpleaf.nio;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.util.iterator;import Java.util.set;public class Timeclienthandle implements Runnable {private String host; private int port; Private Selector Selector; Private Socketchannel Socketchannel; Private volatile Boolean stop; /** * Initialize multiplexer, set the server port address and port of the connection * * @param host * @param port */public Timeclienthandle (String host, int port) {This.host = host = = null? "127.0.0.1": host; This.port = port; try {//create multiplexer Selector selector = Selector.open (); Create Socketchannel to connect to server Socketchannel = Socketchannel.open (); Set the Socketchannel to asynchronous non-blocking socketchannel.configureblocking (false); } catch (IOException e) {e.printstacktRace (); System.exit (1); }} @Override public void Run () {//First attempt to connect directly to try {doconnect (); } catch (IOException e) {e.printstacktrace (); System.exit (1); }//Of course, here can also be the above direct connection code comments, and then use the following two lines of code///Just need to note that if the first attempt to connect, then even if the registration of listening connection is meaningless//at this time no connection request, the server is simply No response//Socketchannel.connect (new Inetsocketaddress (host, port)); Socketchannel.register (selector, selectionkey.op_connect); while (!stop) {try {///timeout-if positive, blocks up to timeout milliseconds when waiting for a channel to be ready, or if zero, blocks indefinitely; must be non-negative (API documentation) Sleep time is 1s, regardless of whether there are read and write events occur, selector every 1s is awakened once selector.select (1000); Get all the Ready Channel key set<selectionkey> Selectedkeys = Selector.selectedkeys (); Iterator<selectionkey> it = Selectedkeys.iterator (); Selectionkey key = null; while (It.hasnext ()) { Gets the key value, which can be obtained by manipulating the key to the channel on which it is registered to the selector//originally a key corresponding to only one Serversocketchannel , which is the Servchannel created earlier, which is equivalent to the server of all client connections to the parent pipeline//NIO, which is to create a connection to the client, because the current code only listens to the Selectionkey.op_accept Bit key = It.next (); The key value is removed from the Selectedkeys set It.remove (); Handle the key value of try {handleinput (key); } catch (Exception e) {if (key! = null) {Key.cancel (); if (key.channel () = null) {Key.channel (). Close (); }}}}} catch (Throwable t) {T.pri Ntstacktrace (); }///multiplexer closed, all resources registered on the channel and pipe are automatically registered and closed, so there is no need to re-release resources if (selector! = null) {try { Selector.close (); } catch (IOException e) {e.printstacktrace (); }}}/** * Processing key * * @param key * @throws IOException */public void Handleinput ( Selectionkey key) throws IOException {if (Key.isvalid ()) {//via key get to Socketchannel Socketch Annel sc = (socketchannel) key.channel (); Isconnectable is to determine whether the connection state//If it is, the service side has returned an ACK reply message, the subsequent need to determine the connection results if (key.isconnectable ()) { Determine the connection result if (Sc.finishconnect ()) {//register Socketchannel to multiplexer selector and listen for SEL Ectionkey.op_read operation Bit Sc.register (selector, selectionkey.op_read); Dowrite (SC); } else {//connection failed, process exited system.exit (1); }}//Read data if (key.isreadable ()) {//Allocate a new byte buffer, size 1024KB, 1MB Bytebuffer Readbuffer = bytebuffer.allocate (1024); Because the Socketchannel has been set to asynchronous nonblocking mode earlier, its read non-blocking//return value is different from the number of bytes read//return value, different meanings: /** * Size 0: Read bytes, encode and decode bytes equal to 0: not read to bytes, belong to normal scene, ignored as-1: Link closed, need to close socketchannel, release resources */ int readbytes = Sc.read (Readbuffer); if (Readbytes > 0) {//read to Byte, decode operation//set current limit of buffer to position,position set to 0 for subsequent Read operations on buffers (I think this is defined in the API) Readbuffer.flip (); Creates a byte array based on the number of bytes that are readable by the buffer byte[] bytes = new byte[readbuffer.remaining ()]; Copies the buffer-readable byte array into the newly created byte array readbuffer.get (bytes); Converts a byte array to a string in utf-8 form, BODY = new string (bytes, "Utf-8"); System.out.println ("Now:" + body); } else if (Readbytes < 0) {//Peer link off Key.cancel (); Sc.close (); } else {;//read to 0 bytes ignored}}}}/** * Connect to Server * * @thr OWS IOException */private void Doconnect () throws IOException {//If direct connection is successful, register to multiplexer, send request message, read answer if (Socketchannel.connect (New Inetsocketaddress (host, port))) {Socketchannel.register (selector, selectionkey.op_read); Dowrite (Socketchannel); } else {Socketchannel.register (selector, selectionkey.op_connect); }}/** * Write operation * * @throws IOException */private void Dowrite (Socketchannel SC) throws Ioexcepti On {//encode string as byte array byte[] req = "QUERY time ORDER". GetBytes (); Creates bytebuffer bytebuffer WriteBuffer = bytebuffer.allocate (req.length) based on the capacity of the byte array; Copies the byte array to the buffer writebuffer.put (req); Flip Operation Writebuffer.flip (); Send a byte array of the buffer out SC.WRITE (WriteBuffer); if (!writebuffer.hasremaining ()) {System.out.println ("Send order 2 server succeesd."); }//is also not handling the "half-packet write" problem, you can view the server-side program code comment description}}
Program Testing
Service-Side Execution:
The time server is start in port : 8080
Client execution:
Send order 2 server succeesd.Now : 2018-02-10
Then look at the output from the server:
The time server is start in port : 8080The time server receive order : QUERY TIME ORDER
Java asynchronous non-blocking IO NiO usage and Code Analysis