手把手教你使用netty搭建一个DNS tcp服务器

目录

  • 简介
  • 搭建netty服务器
  • DNS服务器的消息处理
  • DNS客户端消息请求
  • 总结
简介在前面的文章中,我们提到了使用netty构建tcp和udp的客户端向已经公布的DNS服务器进行域名请求服务 。基本的流程是借助于netty本身的NIO通道,将要查询的信息封装成为DNSMessage,通过netty搭建的channel发送到服务器端,然后从服务器端接受返回数据,将其编码为DNSResponse,进行消息的处理 。
那么DNS Server是否可以用netty实现呢?
答案当然是肯定的,但是之前也讲过了DNS中有很多DnsRecordType,所以如果想实现全部的支持类型可能并现实,这里我们就以最简单和最常用的A类型为例,用netty来实现一下DNS的TCP服务器 。
搭建netty服务器因为是TCP请求,所以这里使用基于NIO的netty server服务 , 也就是NioEventLoopGroup和NioServerSocketChannel,netty服务器的代码如下:
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);        EventLoopGroup workerGroup = new NioEventLoopGroup();        ServerBootstrap bootstrap = new ServerBootstrap().group(bossGroup,                        workerGroup)                .channel(NioServerSocketChannel.class)                .handler(new LoggingHandler(LogLevel.INFO))                .childHandler(new Do53ServerChannelInitializer());        final Channel channel = bootstrap.bind(dnsServerPort).channel();        channel.closeFuture().sync();【手把手教你使用netty搭建一个DNS tcp服务器】因为是服务器,所以我们需要两个EventLoopGroup,一个是bossGroup,一个是workerGroup 。
将这两个group传递给ServerBootstrap,并指定channel是NioServerSocketChannel,然后添加自定义的Do53ServerChannelInitializer即可 。
Do53ServerChannelInitializer中包含了netty自带的tcp编码解码器和自定义的服务器端消息处理方式 。
这里dnsServerPort=53,也是默认的DNS服务器的端口值 。
DNS服务器的消息处理Do53ServerChannelInitializer是我们自定义的initializer,里面为pipline添加了消息的处理handler:
class Do53ServerChannelInitializer extends ChannelInitializer<Channel> {    @Override    protected void initChannel(Channel ch) throws Exception {        ch.pipeline().addLast(                new TcpDnsQueryDecoder(),                new TcpDnsResponseEncoder(),                new Do53ServerInboundHandler());    }}这里我们添加了两个netty自带的编码解码器 , 分别是TcpDnsQueryDecoder和TcpDnsResponseEncoder 。
对于netty服务器来说,接收到的是ByteBuf消息,为了方便服务器端的消息读取,需要将ByteBuf解码为DnsQuery,这也就是TcpDnsQueryDecoder在做的事情 。
public final class TcpDnsQueryDecoder extends LengthFieldBasedFrameDecoderTcpDnsQueryDecoder继承自LengthFieldBasedFrameDecoder,也就是以字段长度来区分对象的起始位置 。这和TCP查询传过来的数据结构是一致的 。
下面是它的decode方法:
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {        ByteBuf frame = (ByteBuf)super.decode(ctx, in);        return frame == null ? null : DnsMessageUtil.decodeDnsQuery(this.decoder, frame.slice(), new DnsQueryFactory() {            public DnsQuery newQuery(int id, DnsOpCode dnsOpCode) {                return new DefaultDnsQuery(id, dnsOpCode);            }        });    }

推荐阅读