搞懂Netty(五)- 写一个TIME服务

在这里插入图片描述

这次我们来实现TIME协议。它不同于之前的协议,它会在创建连接之后发送一个32位的整数,然后一旦数据发送成功就马上关闭连接。这篇文章,你将学习如何构造和发送信息,并在发送完成后关闭连接。
因为我们会忽略任何接收到的信息,在连接成功时就发送数据,所以我们这次不会重写channelRead()方法。而是,重写channelActive() 方法。

package netty.example.time;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class TimeServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception { // (1)
        ByteBuf time = ctx.alloc().buffer(4); // (2)
        time.writeInt((int)(System.currentTimeMillis() / 1000L + 2208988800L));

        ChannelFuture f = ctx.writeAndFlush(time); // (3)
        f.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                assert f == channelFuture;
                ctx.close();
            }
        }); // (4)
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
  1. 这个channelActive() 方法会在连接被建立并准备好传输数据时触发。接下来就在这个方法中发送一个32位的整数。
  2. 要发送数据,我们需要先创建一个buffer,来包含这个32位的整数。因此我们需要一个至少可以包含4个字节的ByteBuf。首先通过ChannelHandlerContext.alloc()获取当前的ByteBufAllocator,然后分配一个buffer。
  3. 然后,我们可以把构造好的消息发出去…
    等一下,怎么没有flip()呢?在NIO中,我们不是需要在发送信息之前调用java.nio.ByteBuffer.flip()吗?Netty中没有相关的方法,是因为Netty的ByteBuf实现中包含了两个指针。一个负责读,一个负责写。当向buffer写入数据时,writer指针会增肌,而reader指针不变。reader指针和writer指针分别代表了消息的开始与结束。
    相反,NIO的buffer就没有这个明确表示消息开始和结束,所以必须调用flip()方法。你如果不调用flip()方法,会导致或者没有数据被发送,或者发送了错误的数据。而很显然Netty就不会了,它会让你的工作变的很轻松。
    另外一个要注意的地方是:ChannelHandlerContext.write()或者writeAndFlush()会返回一个 ChannelFuture。一个 ChannelFuture代表一个还没有发生的I/O操作。在Netty中调用任何的操作,返回后都有可能没有完成,因为Netty中的所有操作都是异步的。例如下面的代码,关闭操作可能会发生在发送消息之前。
    Channel ch = ...;
    ch.writeAndFlush(message);
    ch.close();
    
    因此,你需要在write()方法返回的ChannelFuture完成之后调用close()方法,另外,ChannelFuture完成后会通知它的监听者。同样也要注意,close()也不会立即完成,也会返回ChannelFuture
  4. 我们怎么获得write()完成的通知呢?只要在操作返回的ChannelFuture中加入ChannelFutureListener即可。这里,我们创建了一个匿名的ChannelFutureListener,当写入操作完成后关闭连接。
    另外,也可以用简化的listener
    f.addListener(ChannelFutureListener.CLOSE);
    

如果要测试服务程序是否正确,可以在linux终端执行命令rdate -p <host>。另外服务器需要监听37端口。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值