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();//释放资源
        }

    }   
}
目录
相关文章
|
2月前
|
编解码 Oracle Java
java9到java17的新特性学习--github新项目
本文宣布了一个名为"JavaLearnNote"的新GitHub项目,该项目旨在帮助Java开发者深入理解和掌握从Java 9到Java 17的每个版本的关键新特性,并通过实战演示、社区支持和持续更新来促进学习。
85 3
|
2月前
|
XML Java 编译器
Java学习十六—掌握注解:让编程更简单
Java 注解(Annotation)是一种特殊的语法结构,可以在代码中嵌入元数据。它们不直接影响代码的运行,但可以通过工具和框架提供额外的信息,帮助在编译、部署或运行时进行处理。
95 43
Java学习十六—掌握注解:让编程更简单
|
1月前
|
Java 大数据 API
14天Java基础学习——第1天:Java入门和环境搭建
本文介绍了Java的基础知识,包括Java的简介、历史和应用领域。详细讲解了如何安装JDK并配置环境变量,以及如何使用IntelliJ IDEA创建和运行Java项目。通过示例代码“HelloWorld.java”,展示了从编写到运行的全过程。适合初学者快速入门Java编程。
|
2月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
36 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
1月前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
|
2月前
|
小程序 Oracle Java
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
这篇文章是关于JVM基础知识的介绍,包括JVM的跨平台和跨语言特性、Class文件格式的详细解析,以及如何使用javap和jclasslib工具来分析Class文件。
56 0
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
|
2月前
|
前端开发 Java 应用服务中间件
Javaweb学习
【10月更文挑战第1天】Javaweb学习
34 2
|
2月前
|
存储 安全 Java
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
【用Java学习数据结构系列】探索顺序表和链表的无尽秘密(附带练习唔)pro
26 3
|
2月前
|
存储 安全 Java
【用Java学习数据结构系列】探索栈和队列的无尽秘密
【用Java学习数据结构系列】探索栈和队列的无尽秘密
33 2
|
2月前
|
存储 Java 编译器
【用Java学习数据结构系列】初识泛型
【用Java学习数据结构系列】初识泛型
22 2