用過JavaScript或者jQuery的同窗都知道,JavaScript特別是jQuery中存在大量的回調函數,例如Ajax、jQuery的動畫等。react
$.get(url, function() { doSomething1(); // (3) }); // (1) doSomething2(); // (2)
上面的代碼是jQuery的Ajax,因爲Ajax是異步的,因此在請求URL的過程當中並不會阻塞程序,也就是程序運行到(1)並不用等待Ajax請求的結果,就繼續往下執行(2)。而$.get的第二個參數是一個回調函數,當Ajax請求完成後,纔會調用這個回調函數執行(3)。git
這個例子只是用來理解一下異步的概念,沒玩過JS看不懂的同窗也不要緊。github
在傳統的IO(BIO/阻塞IO)中,全部IO操做都會阻塞當前線程,直到操做完成,全部步驟都是一步一步進行。例如:編程
OutputStream output = socket.getOutputstream(); out.write(data); // IO操做完成後返回 out.flush(); System.out.println("消息發送完成");
可是在異步網絡編程中,因爲IO操做是異步的,也就是一個IO操做不會阻塞去等待操做結果,程序就會繼續向下執行。服務器
在MINA、Netty、Twisted中,不少網絡IO操做都是異步的,好比向網絡的另外一端write寫數據、客戶端鏈接服務器的connect操做等。網絡
例如Netty的write方法(以及writeAndFlush方法),執行完write語句後並不表示數據已經發送出去,而僅僅是開始發送這個數據,程序並不阻塞等待發送完成,而是繼續往下執行。因此若是有某些操做想在write完成後再執行,例如write完成後關閉鏈接,下面這些寫法就有問題了,它可能會在數據write出去以前先關閉鏈接:session
Channel ch = ...;
ch.writeAndFlush(message);
ch.close();
那麼問題就來了,挖掘機...既然上面的寫法不正確,那麼如何在IO操做完成後再作一些其餘操做?異步
當異步IO操做完成後,不管成功或者失敗,都會再通知程序。至於如何處理髮送完成的通知,在MINA、Netty、Twisted中,都會有相似回調函數的實現方式。socket
Netty:ide
在Netty中,write及writeAndFlush方法有個返回值,類型是ChannelFuture。ChannelFuture的addListener方法能夠添加ChannelFutureListener監聽器。ChannelFutureListener接口的抽象方法operationComplete會在write完成(不管成功或失敗)時被調用,咱們只須要實現這個方法便可處理一些write完成後的操做,例如write完成後關閉鏈接。
@Override public void channelRead(final ChannelHandlerContext ctx, Object msg) throws UnsupportedEncodingException { // 讀操做省略... // 發送數據到客戶端 ChannelFuture future = ctx.writeAndFlush("message"); // 返回值類型爲ChannelFuture future.addListener(new ChannelFutureListener() { // write操做完成後調用的回調函數 @Override public void operationComplete(ChannelFuture future) throws Exception { if(future.isSuccess()) { // 是否成功 System.out.println("write操做成功"); } else { System.out.println("write操做失敗"); } ctx.close(); // 若是須要在write後關閉鏈接,close應該寫在operationComplete中。注意close方法的返回值也是ChannelFuture } }); }
上面代碼中,close操做是在operationComplete中進行,這樣能夠保證不會在write完成以前close鏈接。注意close關閉鏈接一樣是異步的,其返回值也是ChannelFuture。
MINA:
MINA和Netty相似,session.write返回值是WriteFuture類型。WriteFuture也能夠經過addListener方法添加IoFutureListener監聽器。IoFutureListener接口的抽象方法operationComplete會在write完成(不管成功或失敗)時被調用。若是須要在write完成後進行一些操做,只需實現operationComplete方法。
@Override public void messageReceived(IoSession session, Object message) throws Exception { // 讀操做省略... // 發送數據到客戶端 WriteFuture future = session.write("message"); future.addListener(new IoFutureListener<WriteFuture>() { // write操做完成後調用的回調函數 @Override public void operationComplete(WriteFuture future) { if(future.isWritten()) { System.out.println("write操做成功"); } else { System.out.println("write操做失敗"); } } }); }
MINA和Netty不一樣在於關閉鏈接的close方法。其中無參數的close()方法已經棄用,而是使用帶一個boolean類型參數的close(boolean immediately)方法。immediately爲true則直接關閉鏈接,爲false則是等待全部的異步write完成後再關閉鏈接。
因此下面這種寫法是正確的:
session.write("message"); session.close(false); // 雖然write是異步的,可是immediately參數爲false會等待write完成後再關閉鏈接
Twisted:
在Twisted中,twisted.internet.defer.Deferred相似於MINA、Netty中的Future,能夠用於添加在異步IO完成後的回調函數。可是Twisted並無在write方法中返回Deffered,雖然write方法也是異步的。下面就用Twisted實現一個簡單的TCP客戶端來學習Deffered的用法。
# -*- coding:utf-8 –*- from twisted.internet import reactor from twisted.internet.protocol import Protocol from twisted.internet.endpoints import TCP4ClientEndpoint, connectProtocol class ClientProtocol(Protocol): def sendMessage(self): self.transport.write("Message") # write也是異步的 self.transport.loseConnection() # loseConnection會等待write完成後再關閉鏈接 # 鏈接服務器成功的回調函數 def connectSuccess(p): print "connectSuccess" p.sendMessage() # 鏈接服務器失敗的回調函數 def connectFail(failure): print "connectFail" point = TCP4ClientEndpoint(reactor, "localhost", 8080) d = connectProtocol(point, ClientProtocol()) # 異步鏈接到服務器,返回Deffered d.addCallback(connectSuccess) # 設置鏈接成功後的回調函數 d.addErrback(connectFail) # 設置鏈接失敗後的回調函數 reactor.run()
在Twisted中,關閉鏈接的loseConnection方法會等待異步的write完成後再關閉,相似於MINA中的session.close(false)。Twisted中想要直接關閉鏈接可使用abortConnection,相似MINA中的session.close(true)。
MINA、Netty、Twisted一塊兒學(一):實現簡單的TCP服務器
MINA、Netty、Twisted一塊兒學(二):TCP消息邊界問題及按行分割消息
MINA、Netty、Twisted一塊兒學(三):TCP消息固定大小的前綴(Header)
MINA、Netty、Twisted一塊兒學(四):定製本身的協議
MINA、Netty、Twisted一塊兒學(五):整合protobuf
MINA、Netty、Twisted一塊兒學(六):session
MINA、Netty、Twisted一塊兒學(七):發佈/訂閱(Publish/Subscribe)
MINA、Netty、Twisted一塊兒學(八):HTTP服務器
MINA、Netty、Twisted一塊兒學(九):異步IO和回調函數
MINA、Netty、Twisted一塊兒學(十):線程模型
MINA、Netty、Twisted一塊兒學(十一):SSL/TLS
MINA、Netty、Twisted一塊兒學(十二):HTTPS