Netty4 1_netty入门

4 篇文章 0 订阅

那netty是什么东西呢?

Netty 努力提供一个异步的事件驱动网络程序框架和工具,  能快速开发易于维护的高性能,  高可扩展性的协议服务器和客户端.

换句话说,  Netty 是一个NIO 客户端/ 服务器架构,  可以快速和容易的开发网络程序就像协议服务器和客户端.它极大的简化了网络开发,  如TCP 和UDP 套接字服务器的开发.带着来自于大量协议如FTP,SMTP,  HTTP以及各种二进制和基于文本的传统协议的实现的经验,  Netty被精心设计.   所以,  Netty 成功的找到一种方法去实现简易开发,  性能,  稳定性和灵活性不冲突.


废话一下:本人认为netty还是比较深奥的东西,因为涉及到java NIO以及java多线程,虽然这些都被封装在netty里面,但作为一名优秀的程序员,你有必要对netty的源码进行剖析,同时,netty在设计上也有点让人纳闷,比如netty3和netty4的改动就很大,如果你在网上找到一个netty3的学习资料,那我只能很抱歉的告诉你,你可以放弃那资料了,可能在设计理念上没有多大的差别,但是,对原有netty3项目的维护将会带来一个很大的问题,关于netty3、4区别,我推荐你看一下http://www.oschina.net/translate/netty-4-0-new-and-noteworthy


同时,在学习netty之前,我建议可以去先了解一下nio、多线程并发编程,以及socket编程,这在我前面的博客中有讲到,还有未发布上去的会接着发上去


说明一下:本博客主要以入门为主,介绍运行的大致过程,至于源代码里面的细节接下来的博客中再一起深入探究吧


好吧,初步了解了netty之后,我们开始进行netty的初次出门吧

首先看一个最简单的客户端服务器的例子Discard(可以去github上面下载netty4 example)

/**
 * 服务器
 * @author chenx_000
 *
 */
public class DiscardServer
{
	private final int port;

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

	public void run() throws Exception
	{
		// 创建两个EventLoopGroup,一个充当register处理器bossGroup,bossGroup把处理过的通道交给workerGroup进行io操作
		EventLoopGroup bossGroup = new NioEventLoopGroup();
		EventLoopGroup workerGroup = new NioEventLoopGroup();

		try
		{
			// 一个服务器助手类
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup)
			        //用它来建立新accept的连接,用于构造serversocketchannel的工厂类
					.channel(NioServerSocketChannel.class)
					//在serverBootstrap内部用该handler去处理实例化的channel
					.childHandler(new ChannelInitializer<SocketChannel>()
					{
						 //当有新accept的时候,这个方法会调用
						@Override
						public void initChannel(SocketChannel ch)
								throws Exception
						{
							ch.pipeline().addLast(new DiscardServerHandler());
						}
					});

			// 绑定并等待accept到来的连接
			ChannelFuture f = b.bind(port).sync();

			
			//关闭服务器
			f.channel().closeFuture().sync();
		} finally
		{
			workerGroup.shutdownGracefully();
			bossGroup.shutdownGracefully();
		}
	}

	public static void main(String[] args) throws Exception
	{
		int port;
		if (args.length > 0)
		{
			port = Integer.parseInt(args[0]);
		}
		else
		{
			port = 8080;
		}
		new DiscardServer(port).run();
	}
}



我们先看一下红色方法.childHandler(new ChannelInitializer<SocketChannel>()



再看一下ServerBootstrap的属性


上面的childHandler就是childHandler(new ChannelInitializer<SocketChannel>()进行复制,并在ServerBootstrap中init(Channel)方法中

 @Override
    void init(Channel channel) throws Exception {
        final Map<ChannelOption<?>, Object> options = options();
        synchronized (options) {
            channel.config().setOptions(options);
        }

        final Map<AttributeKey<?>, Object> attrs = attrs();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }

        ChannelPipeline p = channel.pipeline();
        if (handler() != null) {
            p.addLast(handler());
        }

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions;
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
        }

        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(Channel ch) throws Exception {
                ch.pipeline().addLast(new ServerBootstrapAcceptor(
                        currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
            }
        });
    }



由红色部分代码可见刚才在类DiscardServer代码部分加入的channelInittiallizer被重新封装成channelinitalizer加入了channle的piplline中。而pipline中channelContext的handler进行IO操作。


在看一下DiscardServer中的实际的io处理类吧

public class DiscardServerHandler extends SimpleChannelInboundHandler<Object> {

    private static final Logger logger = Logger.getLogger(
            DiscardServerHandler.class.getName());
    //读channel中的msg,该例子是一个discard,所以直接摒弃就是了
    @Override
    public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf b = (ByteBuf) msg;
        b.release();
    }
    //当netty发生错误,执行该方法
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,
            Throwable cause) throws Exception {
        // Close the connection when an exception is raised.
        logger.log(
                Level.WARNING,
                "Unexpected exception from downstream.",
                cause);
        ctx.close();
    }
}
这是一个inboundhandler,就是对进来的io进行处理,以后会再讲讲channelhandler的


好吧,这就是服务器端了

好吧,接下来看一下客户端吧

public class DiscardClient
{

	private final String host;
	private final int port;
	private final int firstMessageSize;

	public DiscardClient(String host, int port, int firstMessageSize)
	{
		this.host = host;
		this.port = port;
		this.firstMessageSize = firstMessageSize;
	}

	public void run() throws Exception
	{
		EventLoopGroup group = new NioEventLoopGroup();
		try
		{
			Bootstrap b = new Bootstrap();
			b.group(group).channel(NioSocketChannel.class)
					.handler(new DiscardClientHandler(firstMessageSize));

			// 尝试建立连接
			ChannelFuture f = b.connect(host, port).sync();

			// 等待直到连接断开
			f.channel().closeFuture().sync();
		} finally
		{
			group.shutdownGracefully();
		}
	}

	public static void main(String[] args) throws Exception
	{
		if (args.length < 2 || args.length > 3)
		{
			System.err.println("Usage: " + DiscardClient.class.getSimpleName()
					+ " <host> <port> [<first message size>]");
			return;
		}

		// Parse options.
		final String host = args[0];
		final int port = Integer.parseInt(args[1]);
		final int firstMessageSize;
		if (args.length == 3)
		{
			firstMessageSize = Integer.parseInt(args[2]);
		}
		else
		{
			firstMessageSize = 256;
		}

		new DiscardClient(host, port, firstMessageSize).run();
	}
}

再看一下io处理类

public class DiscardClientHandler extends SimpleChannelInboundHandler<Object>
{

	private static final Logger logger = Logger
			.getLogger(DiscardClientHandler.class.getName());

	private final int messageSize;
	private ByteBuf content;
	private ChannelHandlerContext ctx;

	public DiscardClientHandler(int messageSize)
	{
		if (messageSize <= 0)
		{
			throw new IllegalArgumentException("messageSize: " + messageSize);
		}
		this.messageSize = messageSize;
	}

	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception
	{
		this.ctx = ctx;

		//初始化信息
		content = ctx.alloc().directBuffer(messageSize).writeZero(messageSize);

		// 发送已经初始化的信息
		generateTraffic();
	}

	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception
	{
		content.release();
	}

	@Override
	public void channelRead0(ChannelHandlerContext ctx, Object msg)
			throws Exception
	{
		
	}

	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
			throws Exception
	{
		// 关闭连接当抛出一个异常
		logger.log(Level.WARNING, "Unexpected exception from downstream.",
				cause);
		ctx.close();
	}

	long counter;

	private void generateTraffic()
	{
		//冲洗出站套接字的缓冲区。刷新后,再次生成相同数量的传送。
		ctx.writeAndFlush(content.duplicate().retain()).addListener(
				trafficGenerator);
	}

	private final ChannelFutureListener trafficGenerator = new ChannelFutureListener()
	{
		@Override
		public void operationComplete(ChannelFuture future) throws Exception
		{
			if (future.isSuccess())
			{
				generateTraffic();
			}
		}
	};
}

其实客户端和服务器端处理的流程都是差不多,只是一个是等待连接一节accept,一个是connect


到此为止,一个服务器客户端程序就已经完成了,现在可能知道netty是做什么,但对netty的内部还是很迷茫,这也应该很正常的,以后再继续netty4源码分析,一起进一步了解netty的体系吧









  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值