Mina、Netty、Twisted一块儿学(九):异步IO和回调函数

用过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一块儿学系列

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

源码

https://github.com/wucao/mina-netty-twisted

相关文章
相关标签/搜索