手把手教你使用netty搭建一個DNS tcp服務器

目錄

  • 簡介
  • 搭建netty服務器
  • DNS服務器的消息處理
  • DNS客戶端消息請求
  • 總結
簡介在前面的文章中,我們提到了使用netty構建tcp和udp的客戶端向已經公布的DNS服務器進行域名請求服務 ?;镜牧鞒淌墙柚趎etty本身的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);            }        });    }

推薦閱讀