java-Netty学习(2)

原创 2016年05月30日 22:37:53

任务:

创建开发环境
编写一个Echo服务器和客户端
编译测试应用

框架:

这里写图片描述
同时连到一个服务器的多个客户端。理论上,可支持的客户端数量是受可支配系统资源限制的(以及在用的JDK版本带来的限制)。Echo客户端和服务器之间的交互非常简单; 客户端建立一个连接后,它送一条或者多条消息到服务器,然后服务器再将该消息送回客户端。虽然这个交互看起来不那么实用,但却是一个典型的客户端/服务器系统请求-响应交互过程的示例。

服务器端:

所有的Netty服务器都需要:

至少一个ChannelHandler—这个组件实现了服务器如何处理从客户端收到的数据—它的业务逻辑。
Bootstrapping—这是配置服务器的启动代码。至少要做的是,它把服务器绑到一个可以监听连接请求的端口上。
ChannelInboundHandler:定义了作用于输入事件的方法。这个简单的应用只需要这个接口的一些方法,所以用子类ChannelInboundHandlerAdapter就足够了,这个子类提供了ChannelInboundHandler的缺省实现。

channelRead()—每次收到消息时被调用
channelReadComplete()—用来通知handler上一个ChannelRead()是被这批消息中的最后一个消息调用
exceptionCaught()—在读操作异常被抛出时被调用

ChannelInboundHandlerAdapter有简明直接的API,每个方法都可以被重写(overridden),钩挂到(hook into)event生命周期的适当时间点。因为你需要处理所有收到的数据,所以你得重写channelRead()。在这个服务器里,你只要简单地把收到的消息送回远端。
重写exceptionCaught()让你可以对任何Throwable的子类型做出反应—在这里,你打印了这个异常然后关闭了这个连接。一个更加复杂的应用也许会试着从异常中恢复,但是在这个例子中,仅仅把连接关闭就向远端发出了错误已经发生的信号。
如果异常没有被捕获会发生什么?
每个Channel有一个对应的ChannelPipeline,这个ChannelPipeline有一串ChannelHandler实例。默认情况下,一个handler会传递某个handler方法的调用到下一个handler。所以,如果在这个传递链中exceptionCaught()没有实现,异常会一直走到ChannelPipeline的终点,然后被载入日志。因为这个原因,你的应用应该提供至少一个实现了exceptionCaught()的handler。
除了ChannelInboundHandlerAdapter,还有许多其他ChannelHandler子类和实现可以学习。ChannelHandlers被不同类型的events调用
应用程序通过实现或者扩展ChannelHandlers来钩挂到event的生命周期,并且提供定制的应用逻辑在结构上,ChannelHandlers解耦你的业务逻辑和网络代码。这会简化开发过程,因为代码会随着需求的变化而变化。

Bootstrapping服务器:
绑定到一个端口,服务器在这个端口上监听并且接收新的连接请求
配置Channels,来通知EchoServerHandler实例收到的消息
传输:传输层用来提供端对端或者主机对主机通信的服务。互联网通信是建立在TCP传输的基础上的。NIO传输(NIO transport)指的是一个类似于TCP传输的方式,不过它服务器端的性能增强由Java NIO的实现带来的。
EchoServerHandler实现业务逻辑
main()方法启动(bootstrap)服务器
以下是Bootstrapping中的必需步骤:
创建一个ServerBootstrap实例来启动和绑定服务器
创建并且分配一个NioEventLoopgroup实例来处理event,比如接受新的连接和读/写数据
指定本地InetSocketAddress到服务器绑定的端口
用一个EchoServerHandler实例来初始化每个新的Channel
调用ServerBootstrap.bind()来绑定服务器。

Echo客户端

连接到服务器
送出一条或者多条消息
对每条消息,等待并且从服务器收回一条同样的消息
关闭连接
和服务器一样,客户端会有一个ChannelInboundHandler来处理数据。扩展SimpleChannelInbondHandler类来处理所有的任务。
channelActive()—和服务器的连接建立起来后被调用
channelRead0()—从服务器收到一条消息时被调用
exceptionCaught()—处理过程中异常发生时被调用

SimpleChannelInboundHandler对比ChannelInboundHandler

你也许想知道为什么我们在客户端用了SimpleChannelInboundHandler,而不是EchoServerHandler中用的ChannelInboundHandlerAdapter。这和两个因素有关:业务逻辑如何处理消息,以及Netty如何管理资源。
在客户端,当channelRead0()完成时,你收到输入消息并且已经处理完成。当这个方法返回时,SimpleChannelInboundHandler会负责释放用于存放消息的ByteBuf的内存引用。
在EchoServerHandler中,你仍然要把输入的消息送回给发送端,而write()操作是异步的,直到channelRead()返回可能都没有完成(如代码清单2.1所示)。所以,EchoServerHandler扩展了ChannelInboundHandlerAdapter,不会在channelRead()返回的时候释放消息的内存引用。在writeAndFlush()被调用时,这个消息的内存引用会在EchoServerHandler的channelReadComplete()方法中被释放

bootstrapping一个客户端和bootstrapping一个服务器类似,不同于服务器绑定到一个监听端口,客户端用主机和端口参数来连接到一个远程地址,这里就是指Echo服务器.跟之前一样,使用的是NIO传输方式。注意,你在客户端和服务器中可以用不同的传输方式;比如,服务器端用NIO,在客户端用OIO。在第四章我们会分析一些因素和情景,让你为一个特定的使用案例选择一个特定传输方式。创建一个Bootstrap实例来初始化客户端
分配一个NioEventLoopgroup实例来处理事件,包括创建新的连接和处理输入输出数据
创建一个InetSocketAddress用于连接到服务器
当连接建立,一个EchoClientHandler会被装入pipeline
所有东西都创建完毕后,调用Bootstrap.connet()连接到远端。
完成客户端开发后,你可以接着编译和测试这个系统。

代码:

package server;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

@Sharable
//可以被channel共享
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx,Object msg) throws java.lang.Exception {
        ByteBuf in = (ByteBuf) msg; 
        System.out.println("接收到:"+in.toString(CharsetUtil.UTF_8));
        ctx.write(in);
    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx)throws java.lang.Exception {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);//将收到的消息写入发送,不刷新输出消息
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx,Throwable cause) throws java.lang.Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
package server;

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

import java.net.InetSocketAddress;


public class EchoServer {
    private final int port;
    public EchoServer(int port) {
        this.port = port;
    }
    public static void main(String[] args) {
        if(args.length != 1){
            System.err.println("使用:"+EchoServer.class.getSimpleName()+"<Port>");
        }
        int port = Integer.parseInt(args[0]);
        try {
            new EchoServer(port).start();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    private void start() throws InterruptedException {
        final EchoServerHandler serverHandler = new EchoServerHandler();
        NioEventLoopGroup group = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            //配置
            bootstrap.group(group)
                .channel(NioServerSocketChannel.class)
                .localAddress(new InetSocketAddress(port))
                .childHandler(new ChannelInitializer<SocketChannel>(){
                    @Override
                    protected void initChannel(SocketChannel ch)throws java.lang.Exception {
                        ch.pipeline().addLast(serverHandler);//sharable
                    }
                });
            ChannelFuture f = bootstrap.bind().sync();//异步绑定
            f.channel().closeFuture().sync();//阻塞当前线程知道关闭操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally{
            group.shutdownGracefully().sync();//释放资源
        }

    }   
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

一起学Netty(十二)之 Netty心跳简单Demo

前面简单地了解了一下IdleStateHandler,我们现在写一个简单的心跳demo: 1)服务器端每隔5秒检测服务器端的读超时,如果5秒没有接受到客户端的写请求,也就说服务器端5秒没有收到读事件,...
  • linuu
  • linuu
  • 2016年05月14日 10:15
  • 6348

Netty4学习笔记(5)-- NioEventLoopGroup继承层次结构

NioEventLoopGroup继承层次结构
  • SpiderDog
  • SpiderDog
  • 2013年12月30日 15:38
  • 12951

一起学Netty(八)之 浅析ByteToMessageDecoder

上一节一起学习了几个解码器,用于解决TCP协议网络传输过程中粘包和拆包的问题,用过Netty的人总会说一句话“用Netty一定要了解一下它的底层原理,这样才敢用”,其实很有感悟,Netty in ac...
  • linuu
  • linuu
  • 2016年05月11日 19:43
  • 4520

netty5学习笔记-内存池2-PoolSubpage

前面我们讲过了负责内存分配的类PoolChunk,它最小的分配单位为page, 而默认的page size为8K。在实际的应用中,会存在很多小块内存的分配,如果小块内存也占用一个page明显很浪费,针...
  • youaremoon
  • youaremoon
  • 2015年08月25日 21:39
  • 1911

netty学习笔记(2)_tcp拆包/粘包问题

tcp是个流协议,所谓流,就是没有界限的一串数据。tcp底层并不了解上层业务的具体含义,它会根据tcp缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被tcp拆分为多个包进行发送,也...
  • sddh1988
  • sddh1988
  • 2017年04月09日 20:48
  • 123

Netty权威指南 第2版学习笔记2——NIO入门

传统的BIO编程网络编程的基本模型是Client/Server模型,通过三次扬建立连接,如果连接建立成功,双方就可以通过网络套接字进行通信。BIO通信模型图采用BIO通信模型的服务端,通常由一个独立的...
  • xundh
  • xundh
  • 2017年01月01日 00:34
  • 482

Netty学习2—传统多线程服务端

1 代码 import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import ja...
  • woshixuye
  • woshixuye
  • 2016年12月18日 19:52
  • 589

netty4&5之学习之路_2

直接进入正题,今天只要想总结的是tcp数据传输方式和netty的解码器。                  对于TCP的传输方式,感觉如果是为了学习netty那先了解,想要完全知道那等以后学完nett...
  • JFree_Wolf
  • JFree_Wolf
  • 2016年09月09日 12:00
  • 246

Netty学习笔记<2>--简单的NIO例子

NIO属于异步非阻塞(严格来讲它是同步非阻塞),采用I/O多路复用的技术实现的,底层是使用的epoll代替select/poll(它们之间的区别可以查看http://www.cnblogs.com/A...
  • xia744510124
  • xia744510124
  • 2016年10月02日 13:08
  • 279

netty 学习 (2)Handler的执行顺序

netty4 实现一个断点上传大文件功能 您的评价:        收藏该经验     主要用于手机端,网络...
  • gaoqiao1988
  • gaoqiao1988
  • 2015年12月29日 10:43
  • 726
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:java-Netty学习(2)
举报原因:
原因补充:

(最多只允许输入30个字)