网络通信框架netty学习笔记

    今天的目标是记录一个框架的内容。 看了看列表,就从netty入手吧! 计划学习netty还是大致从这三个方面入手: 

1.Netty有什么用?   2.netty怎么用?   3. netty的大致原理?

1.Netty是什么?有什么用?

 由于文档是英文的,虽然我可以用插件一键翻译。  介于我并不赶时间,因此便手动翻译吧哈哈哈!

网络通信框架netty学习笔记_第1张图片

网络通信框架netty学习笔记_第2张图片

网络通信框架netty学习笔记_第3张图片

2.Netty怎么用?

       注: netty官网并没有给出maven的依赖。 因此这里给出maven的依赖参考:

       网络通信框架netty学习笔记_第4张图片

    1.写一个disard服务器。 (世界上最简单的协议不是hello world!  而是discard. 这是一种丢掉所有接收到的数据且不做任何响应的协议。)

package com.automannn.example;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;

/**
 * @author [email protected]
 * @time 2018/11/26 14:01
 */

/*
*  该类继承自 信道绑定处理适配器,它是信道保定处理器的一个实现类。
*  信道处理器提供了多种我们能够覆盖的事件处理方法
*  这里只继承信道绑定处理适配器而不是实现处理器就足够了
* */
public class DiscardServerHandler extends ChannelInboundHandlerAdapter {

    /*
    *  我们覆盖了这个 channelRead()方法。
    *  这个方法在接收到消息时调用,无论合适从客户端接收到消息。
    *  在这个例子中,接收到的消息是 字节缓冲(字节数组)
    *  为了实现discard协议,处理器必须忽略接收到的消息。
    *  bytebuf是一个引用计数对象,它必须明确地被释放,通过release()方法。
    *  请记住 释放任何经由这个处理器的引用计数对象并不是处理器的责任。通常通过 ReferenceCountUtil.release()完成
    * */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf in = (ByteBuf) msg;

        try{
            while (in.isReadable()){
                System.out.print((char) in.readByte());
                System.out.println(" you typed one char just now!");
                System.out.flush();
            }
        }finally {
            ReferenceCountUtil.release(msg);
        }
    }

    /*
    *  当一个异常被netty产生便会调用以下方法。 通常可用于响应消息。
    * */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
package com.automannn.example;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @author [email protected]
 * @time 2018/11/26 14:50
 */
public class DiscardServer {
    private int port;

    public DiscardServer(int port){
        this.port = port;
    }

    public void run() throws Exception{
        //Nio事件循环组 是一个 能够处理I/O操作的多线程的事件循环
        //netty提供了不同的事件循环组,是西安了不同种类的传输,我们正在实现的是服务端应用,因此有两个循环组
        //第一个叫做boss的组,用于接收进入的连接。
        //第二个通常叫做worker,处理接受的连接的交通情况,一旦boss接受了连接并且注册这个连接给worker后。
        //有多少个线程被使用,以及他们如何被映射到创建的信道,取决于事件循环组的实现和构造器配置
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            //serverBootstrap是一个用于设置服务器的辅助类。  你可以直接设置信道。
            //但是请注意,这是一个枯燥的过程,我们通常不必这样做
            //这里,我们指定了用NioServerSocketChannel这个类,他可以用于实例新的信道去接受进入的请求
            //ChannelInitializer是一个特殊的处理器,它的目的是帮助用户去配置一个新的信道。 它更像你想要去配置新信道的
            //  的管道,通过加入一些处理器是实现你的联网应用。
            //你也可以指定参数去定制这个信道实现。 我们正在写一个TCP/IP服务器,所以我们可以设置sokcet选项。
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer() {
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new DiscardServerHandler());
                        }
                    }).option(ChannelOption.SO_BACKLOG,128)
                    .childOption(ChannelOption.SO_KEEPALIVE,true);

            ChannelFuture f = b.bind(port).sync();

            f.channel().closeFuture().sync();
        }finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}
package com.automannn.example;

/**
 * @author [email protected]
 * @time 2018/11/26 14:00
 */
public class Main {
    public static void main(String[] args) throws Exception {
        int port = 8080;
        if (args.length>0){
            port = Integer.parseInt(args[0]);
        }

        new DiscardServer(port).run();
    }
}

 运行测试:

  打开命令行:输入命令: telnet localhst 8080;  然后键入数据:

网络通信框架netty学习笔记_第5张图片

网络通信框架netty学习笔记_第6张图片

2.写一个echo服务器

package com.automannn.example.echo;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * @author [email protected]
 * @time 2018/11/26 16:20
 */
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ctx.write("->");
        ctx.write(msg);
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

  修改discard服务器的处理器实现:

网络通信框架netty学习笔记_第7张图片

运行测试:

网络通信框架netty学习笔记_第8张图片

   每个字符被打印了两遍。 其中第一个是命令行本身所显示的。 另一个是回写的。 通过断点调试的时候可以证明。 此外,不知道为什么第一个字符不符合这个规律。 因为它只显示了一遍。 

3.写一个时间服务器

package com.automannn.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;

/**
 * @author [email protected]
 * @time 2018/11/26 16:51
 */
public class TimeServerHandler extends ChannelInboundHandlerAdapter {

    /*
    *  channelActive()方法将一个链接建立并且生成通道的时候被执行。 在当前方法中我们写入了一个32位整型数据代表当前时间
    *
    * */
    @Override
    public void channelActive(final ChannelHandlerContext ctx) throws Exception {
        //为了发送消息,我们需要分配一个新的缓冲,它将装载这个信息。  我们将要发送写入一个32位的整型数据,因此,我们需要
        //  至少4字节容量的ByteBuf。  获得当前的ByteBuf分配器通过alloc()方法,然偶分配
        final ByteBuf time = ctx.alloc().buffer(4);

        //ByteBuf 具有两个指针,一个用于读,一个用于写。 这样对我们来说就方便了很多。因为没有了flip操作
        time.writeInt((int)(System.currentTimeMillis()/1000L + 2208988800L));

        //一个ChannelFuture 代表了一个还没有发发生的I/O操作。 这意味着,任何请求操作都可能还没有形成。因为在netty
        //   中,所有的操作都是异步的。
        final ChannelFuture f = ctx.writeAndFlush(time);

        f.addListener(new ChannelFutureListener() {
            public void operationComplete(ChannelFuture future) throws Exception {
                assert f == future;
                ctx.close();
            }
        });
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

修改discard服务的处理类:

网络通信框架netty学习笔记_第9张图片

运行测试,我们需要用到两个环境:

  1.unix体系操作系统;   2.rdate命令。

网络通信框架netty学习笔记_第10张图片

 注意,此时我将main中的port改为了37,因为这是rdate服务器的默认端口。  具体修改端口的命令试了一些没有成功。于是就只好这样。   

   为了解决这个问题,下面将实现一个time客户端。 

4.写一个时间客户端

package com.automannn.example.timeClient;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

/**
 * @author [email protected]
 * @time 2018/11/26 18:38
 */
public class TimeClient {
    private int port;
    private String host;
    public TimeClient(int port,String host){
        this.port = port;
        this.host = host;
    }

    public void execute() throws InterruptedException {
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            Bootstrap b = new Bootstrap();
            b.group(workerGroup);
            b.channel(NioSocketChannel.class);
            b.option(ChannelOption.SO_KEEPALIVE,true);

            b.handler(new ChannelInitializer() {
                protected void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new TimeClientHandler());
                }
            });

            ChannelFuture f = b.connect(host,port).sync();

            f.channel().closeFuture().sync();
        }finally {
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new TimeClient(8080,"localhost").execute();
    }
}
package com.automannn.example.timeClient;


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

import java.util.Date;

/**
 * @author [email protected]
 * @time 2018/11/26 18:38
 */
public class TimeClientHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf m = (ByteBuf) msg;

        try {
            long currentTimeMillis = (m.readUnsignedInt()-2208988800L)*1000L;
            System.out.println(new Date(currentTimeMillis));
            ctx.close();
        }finally {
            m.release();
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

运行测试:

网络通信框架netty学习笔记_第11张图片

3.Netty的大致原理:

    精力问题,今天累了。   参考协议如何工作。地址在:这里。  具体的应用层代码如何实现我有以下一些疑惑:

   1.服务端工作线程和何时被启动? 怎样启动?  如何释放?

   2.服务端主线程如何运行?线程间如何如何调度?

   3.一个抽象连接的生命周期?

这些问题将在以后进行补充。 今天就这样吧!

你可能感兴趣的:(笔记,java学习)