Netty Writeandflush () method is divided into two steps, first write and then flush
@Override public channelfuture Writeandflush (Object msg, Channelpromise promise) { Defaultchannelhandlercontext Next; Next = Findcontextoutbound (mask_write); Referencecountutil.touch (msg, next); Next.invoker.invokeWrite (Next, MSG, promise); Next = Findcontextoutbound (Mask_flush); Next.invoker.invokeFlush (next); return promise; }
The above is the Writeandflush method in Defaultchannelhandlercontext, which is actually called write first, and then the flush
1. Write
The Write method starts with Tailhandler, passes through the various handler of the middle custom and then arrives at Headhandler, and then calls the member variable of the Headhandler unsafe's write
As follows
@Override public void Write (Object msg, Channelpromise Promise) { Channeloutboundbuffer Outboundbuffer = This.outboundbuffer; if (Outboundbuffer = = null) { //If the outboundbuffer is null we know the channel were closed and so//need to FA Il the future right away. If It is not null the handling of the rest//would be do in flush0 () //See Https://github.com/netty/netty/issue s/2362 safesetfailure (Promise, closed_channel_exception); Release message now to prevent Resource-leak referencecountutil.release (msg); return; } Outboundbuffer.addmessage (msg, promise); }
Will eventually put the need to write MSG and promise (that is, a future, we take the future of the hand, add listener is also this) into the Outboundbuffer, The existence form of MSG and promise in Outboundbuffer is a custom structure entry.
This means that calling the Write method actually does not really write the message, but instead puts the message and the promise of the operation into a queue.
2. Flush
Flush is also starting from tail, finally to head, the final call is also the head of the unsafe Flush0 () method, and then Flush0 () call the Dowrite () method, as follows:
@Override protected void Dowrite (Channeloutboundbuffer in) throws Exception {int writespincount =-1; for (;;) {Object msg = in.current (); if (msg = = NULL) {//wrote all messages. Clearopwrite (); Break } if (msg instanceof bytebuf) {bytebuf buf = (bytebuf) msg; int readablebytes = Buf.readablebytes (); if (readablebytes = = 0) {in.remove (); Continue } Boolean setopwrite = false; Boolean done = false; Long Flushedamount = 0; if (Writespincount = =-1) {writespincount = config (). Getwritespincount (); } for (int i = writeSpinCount-1; I >= 0; I-) {int localflushedamount = dowritebytes (BUF);This is where the actual data is written out if (Localflushedamount = = 0) {Setopwrite = true; Break } Flushedamount + = Localflushedamount; if (!buf.isreadable ()) {done = true; Break }} in.progress (Flushedamount); if (done) {in.remove (); } else {incompletewrite (setopwrite); Break }} else if (msg instanceof fileregion) {fileregion region = (fileregion) msg; Boolean setopwrite = false; Boolean done = false; Long Flushedamount = 0; if (Writespincount = =-1) {writespincount = config (). Getwritespincount (); } for (int i = writeSpinCount-1; I >= 0; i-) {long Localflushedamount = dowritefileregion (region); if (Localflushedamount = = 0) {Setopwrite = true; Break } Flushedamount + = Localflushedamount; if (region.transfered () >= Region.count ()) {done = true; Break }} in.progress (Flushedamount); if (done) {In.remove ();Depending on the number of data written out, determine whether the operation is complete, and if it is done, call In.remove ()} else {incompletewrite (setopwrite); Break }} else {throw new Unsupportedoperationexception ("Unsupported message type:" + Stringutil.sim Pleclassname (msg)); } } }
The Scarlet Letter is the last place to write the data, and here the data is finally called the Gatheringbytechannel write () method, which is a native Java interface that relies on the Java class that implements the interface, such as the Socketcha that invokes NIO. Nnel's Write () method, so far, the actual process of writing data appears, Socketchannel can run in non-blocking mode, that is, non-blocking asynchronous mode, write data will immediately return the amount of data written (not necessarily all the data is written successfully, For all data written, Netty has its own processing logic, that is, the red word in the above code for the loop, specific to the next Socketchannel Javadoc and Netty source).
When all data is written to Socketchannel success, start calling In.remove (), this in is the first step 1. The Outboundbuffer in write, his type is Channeloutboundbuffer, the code is as follows:
Public final Boolean remove () { if (IsEmpty ()) { return false; } Entry e = buffer[flushed]; Object msg = e.msg; if (msg = = null) { return false; } Channelpromise promise = E.promise; int size = E.pendingsize; E.clear (); flushed = flushed + 1 & buffer.length-1; if (!e.cancelled) { //Only release message, notify and decrement if it is not canceled before. Saferelease (msg); Safesuccess (Promise); Here, the Promise Trysuccess () method is called, triggering the listener decrementpendingoutboundbytes (size); } return true; }
Finally, the Promise notifylisteners () operation is called, triggering the listener to complete the asynchronous process
---------
Finally, go back to the code we used when we applied Netty.
@Override public void Channelread (Channelhandlercontext ctx, Object msg) { Ctx.writeandflush (new Object ()). AddListener (New Channelfuturelistener () { @Override public void Operationcomplete (Channelfuture the Future) Throws Exception { if (future.issuccess ()) { //do sth } else { //do Sth }}} ); }
That's the whole process.
Finally mention, Netty Abstractniochannel encapsulated Selectionkey, in the accept socket, the socket will be registered to EventLoop () selector, This selectionkey will be assigned, as follows
Selectionkey = Javachannel (). Register (EventLoop (). Selector, 0, this);
In the later selector select (), this key will be taken to the channel, and then call Abstractchannel in the Defaultchannelpipeline to trigger the Handler Connect, Rea D, write, etc. events ...
Netty Writeandflush () process and async