Service side (Serverbootstrap)
The sample code is as follows:
Import Io.netty.bootstrap.serverbootstrap;import Io.netty.channel.channelfuture;import Io.netty.channel.channelinitializer;import Io.netty.channel.channeloption;import Io.netty.channel.EventLoopGroup ; Import Io.netty.channel.nio.nioeventloopgroup;import Io.netty.channel.socket.socketchannel;import io.netty.channel.socket.nio.nioserversocketchannel;/** * Created by Yaojiafeng on 16/1/17. */public class Simpleserver {public void bind (int port) throws Exception {//Configure the NIO thread group on the service side Eventloopgrou P bossgroup = new Nioeventloopgroup (1); Eventloopgroup Workergroup = new Nioeventloopgroup (1); try {serverbootstrap b = new Serverbootstrap (); B.group (Bossgroup, Workergroup). Channel (nioserversocketchannel.class). Option (Chann Eloption.so_backlog, 1024x768). Childhandler (New Childchannelhandler ()); Bind port, synchronization waits successfully channelfuture F = b.bind (port). sync (); Wait for the server to listen on the portClose F.channel (). Closefuture (). sync (); } finally {//gracefully exits, releasing thread pool resource bossgroup.shutdowngracefully (); Workergroup.shutdowngracefully (); }} Private class Childchannelhandler extends Channelinitializer<socketchannel> {@Override prot ected void Initchannel (Socketchannel arg0) throws Exception {Arg0.pipeline (). AddLast (New Simpleserverhandler () ); }}/** * @param args * @throws Exception */public static void main (string[] args) throws Exception {int port = 8081; if (args! = null && args.length > 0) {try {port = integer.valueof (Args[0]); } catch (NumberFormatException e) {//Takes default}} new Simpleserver (). Bind (Port ); }}
1.1. Set Eventloopgroup
First create 2 Eventloopgroup, one parentgroup (for accepting new connections), Childgroup (for performing read-write events), The Nioeventloopgroup internally creates an Nioeventloop array of the corresponding size based on the Neventloops parameter set, and each nioeventloop uses a Forkjoinpool thread by default. So Nioeventloop is called a single-threaded event loop.
1.2. Construction Serverbootstrap
Constructs the Serverbootstrap object and sets the Eventloopgroup,channel (Nioserversocketchannel service-side socket), some option such as Channeloption.so_backlog , Childhandler (after the client is connected, the Channelhandler is set in the pipeline chain)
1.3. Synchronizing the bound port
b.bind(port).sync()
1.3.1 Validate method
The Validate method verifies that Parentgroup and ChannelFactory cannot be null
1.3.2 Initandregister method
The ChannelFactory method internally uses a Nioserversocketchannel instance object constructed by reflection, and the Nioserversocketchannel instance object constructs the internal serversocketchannel primarily. Defaultchannelid (Identity uniqueness), Unsafe (all IO operations are in this class), Defaultchannelpipeline (channel processor pipeline chain, custom Channelhandler are here), Nioserversocketchannelconfig (some configuration information).
Constructs a call to init initialization nioserversocketchannel, including setting custom Channelhandler,serverbootstrapacceptor (specifically used to accept new connections from the client, Initialize the Niosocketchannel and register into the Childgroup for read-write listening).
ChannelFuture regFuture = group().register(channel)
Asynchronously registers the Nioserversocketchannel to the Nioeventloop in Parentgroup.
Because the registration process is executed asynchronously in Nioeventloop, the Register method is analyzed directly first
1.3.3 Asynchronous Register
channel.unsafe().register(this, promise)
Registration will call the above method, delegated to the unsafe register method, Internally, the Nioserversocketchannel field EventLoop initialized (Nioserversocketchannel associated with a unique Nioeventloop), which is then called
eventLoop.execute(new OneTimeTask() { @Override public void run() { register0(promise); } });
This will open the Nioeventloop event loop thread and put the task into taskqueue as an asynchronous execution Register0 method.
The Register0 method invokes the Doregister method of the external class (Nioserversocketchannel).
protected void Doregister () throws Exception {Boolean selected = false; for (;;) {try {Selectionkey = Javachannel (). Register (((Nioeventloop) EventLoop (). Unwrap ()). Selector, 0, this); Return } catch (Cancelledkeyexception e) {if (!selected) {//force the Selector-select now As the "canceled" Selectionkey may still is//cached and not removed because no select.select (..) ope Ration was called yet. ((Nioeventloop) EventLoop (). Unwrap ()). Selectnow (); selected = true; } else {//We forced a select operation on the selector before and the Selectionkey is still cached For whatever reason. JDK bug? Throw e; } } } }
The NIO API is used here to register the Serversocketchannel in the Nioserversocketchannel with selector in the Nioserversocketchannel associated Nioeventloop.
The next safesetsuccess will set the listener for the main thread, setting the BIND task.
1.3.4 Performing BIND operations
private static void doBind0( final ChannelFuture regFuture, final Channel channel, final SocketAddress localAddress, final ChannelPromise promise) { // This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up // the pipeline in its channelRegistered() implementation. channel.eventLoop().execute(new Runnable() { @Override public void run() { if (regFuture.isSuccess()) { channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } else { promise.setFailure(regFuture.cause()); } } }); }
In the case of a successful registration, the bind operation is performed (Nioserversocketchannel's Bind method), traced to
unsafe.bind(localAddress, promise);
Unsafe bind method, internally called Nioserversocketchannel's Dobind method
protected void doBind(SocketAddress localAddress) throws Exception { javaChannel().socket().bind(localAddress, config.getBacklog()); }
and set the Pipeline.firechannelactive () task, the Firechannelactive task calls the Channel.read () method, which is internally called to the Unsafe.beginread () method, The final call is Nioserversocketchannel's Dobeginread method, Reset the Selectionkey of interest to the event Readinterestop (Nioserversocketchannel constructed when the selectionkey.op_accept is determined) and start receiving new connections.
protected void doBeginRead() throws Exception { // Channel.read() or ChannelHandlerContext.read() was called if (inputShutdown) { return; } final SelectionKey selectionKey = this.selectionKey; if (!selectionKey.isValid()) { return; } readPending = true; final int interestOps = selectionKey.interestOps(); if ((interestOps & readInterestOp) == 0) { selectionKey.interestOps(interestOps | readInterestOp); } }
1.4. Nioeventloop Event Loop accepting new connections
Nioeventloop continuously performs its asrunnable task through Forkjoinpool (asrunnable is reset to Forkjoinpool by each execution of the task).
Starting with the Run method of the asrunnable, the internal first executes the selector select operation, then calls the Processselectedkeys () method first, obtains to the active Selectedkeys array, here if has the new connection to come in, Then there is a selectionkey, get it's attachment (Nioserversocketchannel), and then call the Processselectedkey method.
private static void Processselectedkey (Selectionkey k, Abstractniochannel ch) {Final Abstractniochannel.niounsafe unsafe = Ch.unsafe (); if (!k.isvalid ()) {//Close the channel if the key is not valid anymore Unsafe.close (Unsafe.voidpro Mise ()); Return } try {int readyops = K.readyops (); Also Check for Readops of 0 to workaround possible JDK bugs which may otherwise leads//to a spin loop if (Readyops & (Selectionkey.op_read | selectionkey.op_accept))! = 0 | | Readyops = = 0) {unsafe.read (); if (!ch.isopen ()) {//Connection already closed-no need to handle write. Return }} if ((Readyops & selectionkey.op_write)! = 0) {//call Forceflush which would Also take care of the clear the op_write once there are nothing left to WRITE Ch.unsafe (). fOrceflush (); } if ((Readyops & selectionkey.op_connect)! = 0) {//Remove Op_connect as otherwise selecto R.select (..) 'll always return without blocking//See https://github.com/netty/netty/issues/924 int ops = K.interestops (); OPS &= ~selectionkey.op_connect; K.interestops (OPS); Unsafe.finishconnect (); }} catch (Cancelledkeyexception ignored) {Unsafe.close (Unsafe.voidpromise ()); } }
1.4.1 Execute Get New connection method
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) { unsafe.read(); if (!ch.isOpen()) { // Connection already closed - no need to handle write. return; } }
When Readyops equals Selectionkey.op_accept call Unsafe.read (), this is called to the Abstractniomessagechannel inner class niomessageunsafe of the Read method.
The Read method loops through the new connection and accepts 16 connections at a time by default, calling the Doreadmessages method.
protected int doReadMessages(List<Object> buf) throws Exception { SocketChannel ch = javaChannel().accept(); try { if (ch != null) { buf.add(new NioSocketChannel(this, ch)); return 1; } } catch (Throwable t) { logger.warn("Failed to create a new channel from an accepted socket.", t); try { ch.close(); } catch (Throwable t2) { logger.warn("Failed to close a socket.", t2); } } return 0; }
Here call NiO's Api,accept method to get Socketchannel, and encapsulate it into Niosocketchannel (Niosocketchannel's construction field is similar to Nioserversocketchannel, Just niosocketchannel The default interest event is Selectionkey.op_read).
Accept the connection complete and loop through the Pipeline.firechannelread () method.
The Channelread method of 1.4.2 Serverbootstrapacceptor
The above pipeline calls the Firechannelread method, which calls Channelhandler's Channelread method sequentially through the chain of responsibility, the most important of which is the Channelread method of Serverbootstrapacceptor.
This method sets the Childhandler to Niosocketchannel (new connection) in the pipeline chain, and then asynchronously registers Niosocketchannel into Childgroup nioeventloop, The registration process is broadly consistent with the previous 1.3 chapters and is also the event loop asynchronous registration that initiates the Nioeventloop in the Childgroup. Just because it's niosocketchannel some implementations are different, the code executed is a bit different, and the final registration completion also calls the pipeline Firechannelactive () method.
1.4.3 Firechannelactive method
public ChannelPipeline fireChannelActive() { head.fireChannelActive(); if (channel.config().isAutoRead()) { channel.read(); } return this; }
The Read method of the channel eventually delegates the call to the Unsafe.beginread () method, and then again the Niosocketchannel Dobeginread method, The Selectionkey interest event is reset to Selectionkey.op_read (the default value of Niosocketchannel). Here the connection is established, and the client connection read event is opened for listening.