java-Netty学习(2)

简介: 任务:创建开发环境编写一个Echo服务器和客户端编译测试应用框架:同时连到一个服务器的多个客户端。理论上,可支持的客户端数量是受可支配系统资源限制的(以及在用的JDK版本带来的限制)。

任务:

创建开发环境
编写一个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();//释放资源
        }

    }   
}
目录
相关文章
|
15天前
|
消息中间件 前端开发 Java
java学习路径
【4月更文挑战第9天】java学习路径
17 1
|
1月前
|
安全 Java 程序员
学习Java类加载机制
在Java的世界里,每一个类或者接口,在经历编译器后,都会生成一个个.class文件。
18 0
|
1月前
|
Java 关系型数据库 MySQL
37、一篇文章学习 Java 中的日期相关类(Date 和 Calendar),非常常用
37、一篇文章学习 Java 中的日期相关类(Date 和 Calendar),非常常用
27 0
|
2月前
|
Java 数据库连接 开发工具
正式开始JAVA学习之旅
正式开始JAVA学习之旅
39 0
|
1月前
|
存储 安全 Java
24、使用 Java 官方教程学习:① 类变量和类方法详解;② 深入介绍 main() 方法
24、使用 Java 官方教程学习:① 类变量和类方法详解;② 深入介绍 main() 方法
38 1
|
15天前
|
设计模式 前端开发 安全
Java是一种广泛使用的编程语言,其学习路径可以大致分为以下几个阶段
【4月更文挑战第9天】Java是一种广泛使用的编程语言,其学习路径可以大致分为以下几个阶段
15 1
|
1月前
|
Java 索引
Java中String方法学习总结_kaic
Java中String方法学习总结_kaic
|
5天前
|
JavaScript Java 测试技术
基于Java的驾考自主学习预约平台的设计与实现(源码+lw+部署文档+讲解等)
基于Java的驾考自主学习预约平台的设计与实现(源码+lw+部署文档+讲解等)
16 0
|
5天前
|
JavaScript Java 测试技术
基于Java的精品课程在线学习系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的精品课程在线学习系统的设计与实现(源码+lw+部署文档+讲解等)
25 1
|
6天前
|
JavaScript Java 测试技术
基于Java的中文学习系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的中文学习系统的设计与实现(源码+lw+部署文档+讲解等)
20 0