vlambda博客
学习文章列表

Netty 异步IO和回调函数

用过JavaScript或者jQuery的同学都知道,JavaScript特别是jQuery中存在大量的回调函数,例如Ajax、jQuery的动画等。

$.get(url, function() {
 doSomething1(); // (3)
}); // (1)
doSomething2();  // (2)

上面的代码是jQuery的Ajax,由于Ajax是异步的,所以在请求URL的过程中并不会阻塞程序,也就是程序运行到(1)并不用等待Ajax请求的结果,就继续往下执行(2)。而$.get的第二个参数是一个回调函数,当Ajax请求完成后,才会调用这个回调函数执行(3)。

这个例子只是用来理解一下异步的概念,没玩过JS看不懂的同学也没关系。

在传统的IO(BIO/阻塞IO)中,所有IO操作都会阻塞当前线程,直到操作完成,所有步骤都是一步一步进行。例如:

OutputStream output = socket.getOutputstream();
out.write(data); // IO操作完成后返回
out.flush();
System.out.println("消息发送完成");

但是在异步网络编程中,由于IO操作是异步的,也就是一个IO操作不会阻塞去等待操作结果,程序就会继续向下执行。

在Netty中,很多网络IO操作都是异步的,比如向网络的另一端write写数据、客户端连接服务器的connect操作等。

例如Netty的write方法(以及writeAndFlush方法),执行完write语句后并不表示数据已经发送出去,而仅仅是开始发送这个数据,程序并不阻塞等待发送完成,而是继续往下执行。所以如果有某些操作想在write完成后再执行,例如write完成后关闭连接,下面这些写法就有问题了,它可能会在数据write出去之前先关闭连接:

Channel ch = ...;
ch.writeAndFlush(message);
ch.close();

那么问题就来了,既然上面的写法不正确,那么如何在IO操作完成后再做一些其他操作?

当异步IO操作完成后,无论成功或者失败,都会再通知程序。至于如何处理发送完成的通知,在Netty中,都会有类似回调函数的实现方式。

在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。

 
   
   
 

如有收获请划至底部

点击“在看”支持,谢

Netty 异步IO和回调函数


关注马士兵

每天分享技术干货





点赞是最大的支持