本系列文章主要来自《Netty IN ACTION》

本章主要内容:

  • Java网络编程

  • Netty简介

  • Netty核心组件

Java网络编程

首先来看一个典型的阻塞I/O示例:

//创建一个新的ServerSocket,用于监听指定端口上的连接请求
ServerSocket serverSocket = new ServerSocket(portNumber);//1
//以阻塞的方式来调用accept()方法,直到一个连接建立
Socket clientSocket = serverSocket.accept();//2
//BufferedReader和PrintWriter分别用于从字符输入流中读取文本和将对象格式化到文本输出流
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),true);
String request,response;
//循环对输入流进行判断处理
while((request = in.readLine()) != null){
    if("Done".equals(request)){
        break;
    }
    //服务器处理客户端的请求,并作出响应
    response = processRequest(request);
    //输出响应
    out.println(response);

这里ServerSocket的作用是监听某一路端口是否有连接,如果有连接就会创建一个新的Socket用于客户端和服务器进行通信,而此时ServerSocket并不会断开连接,而是会继续监听传入的连接。

在这里插入图片描述

这里就体现出弊端了,如果有多个并发客户端,那就需要为每个客户端Socket创建一个Thread,弊端有三:

  • 资源浪费,并不是每时每刻都有数据在传输,大部分情况下线程都处在休眠状态;
  • 内存消耗,需要为每个线程的调度栈分配内存;
  • 上下文切换带来的开销大;

Java NIO

使用setSockopt()方法配置套接字,以便读/写调用在没有数据的时候立即返回。

选择器

使用java.nio.channels.Selector这个类,利用事件通知API来确定在一组非阻塞套接字中有哪些已经就绪能够进行I/O相关的操作。

在这里插入图片描述

Netty简介

Netty是一个NIO客户端/服务器框架,支持快速、简单地开发网络应用,如协议服务器和客户端。

Netty核心组件

Netty的主要构建块:

  • Channel
  • 回调
  • Future
  • 事件和Channelhandler

Channel

简单理解就是传入或者传出数据的载体,可以被打开或者被关闭,连接或者断开连接。

回调

回调其实就是一个方法,回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。Netty内部使用了回调来处理事件,当一个回调被触发时,相关的事件被一个ChannelHandler的实现来处理,

看下面一段代码:

public class ConnectHandler extends ChannelInboundHandlerAdapter {
    @Override
    public void channelActive(channelHandlerContect ctx) throws Exception{
        System.out.println("Client " + ctx.channel().remoteAddress() + " connected");
    }
}

当一个新的连接已经被建立时,ChannelHandler的channelActive()回调方法将会被调用,并将打印一条信息。

Future

可以看作是一个异步操作的结果的占位符,可以在未来提供对其结果的访问;

Netty提供了自己的ChannelFuture,用于在执行异步操作的时候使用

public class ConnectExample {
    private static final Channel CHANNEL_FROM_SOMEWHERE = new NioSocketChannel();
    public static void connect() {
        Channel channel = CHANNEL_FROM_SOMEWHERE; 
        //异步地连接到远程节点
        ChannelFuture future = channel.connect(
                new InetSocketAddress("192.168.0.1", 25));
        //注册一个 ChannelFutureListener,以便在操作完成时获得通知
        future.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) {
                //检查操作的状态
                if (future.isSuccess()) {
                    //如果操作是成功的,则创建一个 ByteBuf 以持有数据
                    ByteBuf buffer = Unpooled.copiedBuffer(
                            "Hello", Charset.defaultCharset());
                    //将数据异步地发送到远程节点。返回一个 ChannelFuture
                    ChannelFuture wf = future.channel()
                            .writeAndFlush(buffer);
                    // ...
                } else {
                    //如果发生错误,则访问描述原因的 Throwable
                    Throwable cause = future.cause();
                    cause.printStackTrace();
                }
            }
        });
    }
}

可以看到,回调和Future是相互补充的机制,而ChannelFutureListener可以看作是回调的一个更加精细的版本。

事件和Channelhandler

Netty使用不同的事件来通知我们状态的改变或者是操作的状态,因此我们可以利用事件来进行相应的处理,比如:

  • 记录日志
  • 数据转换
  • 流控制
  • 应用程序逻辑

Netty作为一个网络编程框架,主要事件分为入站事件和出站事件,入站事件包括:

  • 连接已被激活或者连接失活
  • 数据读取
  • 用户事件