Netty从零到一之Echo和Time

Echo和Time服务

Echo

在上一节中,我们实现了一个丢弃所有数据的DISCARD服务,但是一个服务通常来说都会对请求进行响应。现在让我们开始写一个Echo服务–返回收到的数据,来学习如何写入响应信息。
DISCARD和Echo唯一不同的地方时DISCARD是将收到的数据打印到控制台当中,而Echo是将数据返回。所以只需要对DISCARD中的channelRead()稍作修改即可。

 @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ctx.write(msg); // (1)
        ctx.flush(); // (2)
    }
  1. ChannelHandlerContext 对象提供了多种操作,可以让我们来出发各种I/O事件和操作。在这,我们执行了writer(object)将收到的数据写进去。请注意,在这里我们没有释放数据对像–这是因为Netty会自动释放写出的对象。
  2. ctx.write(msg)这行代码并没有确保数据已经写出–数据被缓存在里面,在调用ctx.flush()之后数据才被‘冲刷’出去。另外,ctx.writeAndFlush(msg)是另一种更简洁的方案。
    修改完成之后,运行程序,访问本地的服务,你就能看到结果了。
Time

接下来我们要说的是一个Time服务。和前面两个例子不同的是,Time服务在不接受请求的情况下,发送一个32位整数,并且在信息发送完成后关闭连接。在这个例子里,会学到如何去构造和发送信息,并在完成时关闭连接。
因为我们会忽略所有收到的信息,并在连接建立时发送信息,所以在这里ChannelRead()方法不再适用了。下面我们介绍ChannelActive()方法。代码如下:

public class TimeServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelActive(final ChannelHandlerContext ctx) { // (1)
       ByteBuf time = ctx.alloc().buffer(32);//(2)
        String l = String.valueOf(System.currentTimeMillis());
        time.writeBytes(l.getBytes());
        ChannelFuture f = ctx.writeAndFlush(time);//(3)
        f.addListener(new ChannelFutureListener() {
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                ctx.close();
            }
        });//(4)
    }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}
  1. channelActive() 方法在连接建立完成时调用。数据就是在这个方法里写入的。
  2. 在发送信息之前,我们先要建造一个用来存放信息的缓存。因为我门要发送的是32位的整数,所以需要一个至少4字节的ByteBuf,通过ctx对象可以获取到当前的ByteBufAllocator对象–它可以给你新的缓存区。
  3. 我们和以前一样,将构造的信息写进去。

    注意一下这里,我们并没有像NIO里一样,在发送信息前调用flip方法,因为ByteBuf 并没有这样的方法,而是有两个指针。一个用来做读操作,另一个用来写。当你向ByteBuf中写入数据的时候,写指针的索引就会增加,而读指针的索引不会改变。读指针和写指针分别相当于信息的开始和结束。

    相比之下,NIO的Buffer 没有提供一个清晰的方式去指明一条消息内容的开始和结束–如果不主动调用flip。当你忘记调用flip方法的时候,你就会陷入莫名其妙的麻烦中–因为发送了空或者错误的信息。而在Netty中这些都不会发生–因为那两个指针。

    另外一点需要说明的是ChannelHandlerContext.write()(writeAndFlush)方法返回了一个ChannelFuture对象。ChannelFuture 相当于一个还没有被创建的IO操作对象。这意味着,任何已请求的操作,在全异步的情况下,无法保证操作已经被执行。比如在下面的情况下,可能在消息发送前就关闭了连接。

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

所以,我们必须在ChannelFuture创建完成之后去调用close方法–在写操作完成之后,它会通知它的监听器,所以在监听器里执行close是一个很好的选择。请注意,close()方法也不见得会立马关闭连接,因为它返回的也是一个ChannelFuture。
4. 当一个请求完成的时候,我们如何才能得到通知呢?最简单的方式就是给返回的ChannelFuture加一个ChannelFutureListener。在这里,我们用匿名类的方式,在操作完成时关闭该通道。
5. 代码也可以简化成如下形式–使用了一个预定义的监听器。

f.addListener(ChannelFutureListener.CLOSE);

为了方便测试,我写了一个简单的python脚本用于发送消息和回显消息,如有需要请到https://github.com/oHao/Python-Learning/blob/master/Script/TCP/SendMessage.py下载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值