Netty5中文用户手册 Part 1.md

问题

现在我们使用通用应用程序或者库与他人进行通信。例如,我们经常使用HTTP客户端库从Web服务器检索信息,并通过Web服务执行一个远程过程调用。
然而,通用协议或其实现有时不能很好地扩展。 这就像我们不使用通用HTTP服务器来交换大量文件,电子邮件消息和近实时消息,如财务信息和多人游戏的数据。 我们需要一种专门用于特殊目的高度优化的协议实现。 例如,您可能希望实现针对基于AJAX的聊天应用程序,媒体流或大型文件传输进行了优化的HTTP服务器。 你甚至可以设计和实施一个全新的协议,这个协议是根据你的需要而定制的。
另一个不可避免的情况是当您必须处理旧版专有协议以确保与旧系统的互操作性。 在这种情况下重要的是我们能够快速实现该协议,而不会牺牲最终应用程序的稳定性和性能。

解决方案

Netty项目是为了快速开发可维护的高性能高可扩展性协议服务器和客户端,提供异步事件驱动的网络应用程序框架和工具为做出的努力。
换句话说,Netty是一个NIO客户端服务器框架,可以快速轻松地开发诸如协议服务器和客户端之类的网络应用程序。它大大简化了网络编程流程,如TCP和UDP套接字服务器开发。
“快速和容易”并不意味着由此产生的应用程序将遭受可维护性或性能问题的困扰。 Netty通过执行许多协议(如FTP,SMTP,HTTP以及各种基于二进制和基于文本的传统协议)获得的经验而进行了精心设计。 因此,Netty成功地找到了一种方法来实现轻松的开发,性能,稳定性和灵活性的目标,而无需妥协。

您将能够立即在Netty之上编写客户端和服务器。
如果您喜欢自上而下的方法来学习某些东西,则可能需要从第2章“架构概述”开始,然后回到这里。

开始之前

运行本章中介绍的例子的最低要求只有两个; 最新版本的Netty和1.6以上版本JDK 。 最新版本的Netty可在项目下载页面中找到。 要下载正确版本的JDK,请参阅您首选的JDK供应商的网站。

在阅读时,您可能对本章中介绍的课程有更多的疑问。 只要您想了解更多关于它们的信息,请参阅API参考。 为方便起见,本文档中的所有类名都链接到在线API参考。 另外,如果您有好想法来改进文档,请随时联系Netty项目社区,让我们知道是否有不正确的信息,语法错误和错字,

写一个丢弃服务

世界上最简单的协议不是“你好,世界!” 但是DISCARD。 这是一种丢弃任何接收到的数据而无任何响应的协议。
要实现DISCARD协议,您唯一需要做的是忽略所有接收到的数据。 让我们从处理器实现开始,它处理由Netty生成的I / O事件。

package io.netty.example.discard;
import io.netty.buffer.ByteBuf;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelHandlerAdapter;

/**
 * Handles a server-side channel.
 */
public class DiscardServerHandler extends ChannelHandlerAdapter { // (1)

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
        // Discard the received data silently.
        ((ByteBuf) msg).release(); // (3)
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }
}

1.DiscardServerHandler继承ChannelHandlerAdapter,它是ChannelHandler的一个实现。 ChannelHandler提供了可以覆盖的各种事件处理程序方法。 现在,只需扩展ChannelHandlerAdapter即可,而不是自己实现处理程序接口 。
2.我们这里重写了事件处理程序方法。每当从客户端收到新数据时,都会使用接收到的消息调用此方法。在这个例子中,接收到的消息的类型是ByteBuf。
3.为了实现DISCARD协议,处理程序必须忽略收到的消息。 ByteBuf是一个引用计数对象,必须通过release()方法显式释放。 请记住,处理程序有责任释放传递给处理程序的引用计数对象。 通常,channelRead()处理方法的实现方式如下:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    try {
        // Do something with msg
    } finally {
        ReferenceCountUtil.release(msg);
    }
}

当一个异常由于IO错误,或者处理器的实现在处理事件抛出异常时,而抛出时,使用Throwable调用exceptionCaught()事件处理程序方法。 在多数情况下,被捕获的异常应该被记录,与此异常相关的通道应该在这里关闭。虽然这个方法的实现可能取决于你想处理异常的情况而有所不同。例如,你可能想在关闭连接之前,发出一个响应,里面包含了一个错误码。到目前为止还挺好。我们实现了丢弃服务器的前半部分。剩下的是写一个main方法,启动服务和丢弃服务处理器。

package io.netty.example.discard;

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

/**
 * Discards any incoming data.
 */
public class DiscardServer {

    private int port;

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

    public void run() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap(); // (2)
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class) // (3)
             .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ch.pipeline().addLast(new DiscardServerHandler());
                 }
             })
             .option(ChannelOption.SO_BACKLOG, 128)          // (5)
             .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

            // Bind and start to accept incoming connections.
            ChannelFuture f = b.bind(port).sync(); // (7)

            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            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();
    }
}

1.NioEventLoopGroup是一个处理I / O操作的多线程事件循环。 Netty为不同类型的传输提供了各种EventLoopGroup实现。在这个例子中,我们正在实现一个服务器端应用程序,因此将使用两个NioEventLoopGroup。第一个,通常称为“老板”,接受传入的连接。第二个,通常称为“工人”,一旦老板接受连接并将接受的连接注册给工作人员,就处理接受的连接的流量。使用多少线程以及它们如何映射到创建的通道取决于EventLoopGroup实现,甚至可以通过构造函数进行配置。

2.ServerBootstrap是一个辅助类,用于设置服务器。您可以直接使用频道设置服务器。但是请注意,这是一个繁琐的过程,在大多数情况下您不需要这样做。
3.在这里,我们指定使用NioServerSocketChannel类来实例化一个新的Channel来接受传入的连接

4.这里指定的处理程序将始终由新接受的通道进行评估。 ChannelInitializer是一个特殊的处理程序,旨在帮助用户配置新的频道。很可能您想通过添加一些处理程序(如DiscardServerHandler)来配置新通道的ChannelPipeline来实现您的网络应用程序。随着应用程序的复杂化,您可能会在管道中添加更多的处理程序,并将这个匿名类最终提取到顶级类中。

您还可以设置特定于Channel实现的参数。 我们正在编写一个TCP / IP服务器,因此我们可以设置套接字选项,如tcpNoDelay和keepAlive。 请参阅ChannelOption的apidocs和特定的ChannelConfig实现,以获得有关支持的ChannelOptions的概述。
你有没有注意到option()和childOption()? option()用于接受传入连接的NioServerSocketChannel。 childOption()用于在这种情况下由父级ServerChannel接受的通道,即NioServerSocketChannel。
我们现在准备好了。 剩下的是绑定到端口并启动服务器。 在这里,我们绑定到机器中所有NIC(网络接口卡)的端口8080。 您现在可以根据需要调用bind()方法多次(具有不同的绑定地址)。
7.我们现在准备好出发了。剩下的工作是绑定端口开启服务。这里,我们绑定8080端口到机器上所有的NICs(网络接口卡片)。现在你可以根据需要多次调用bind()函数(使用不同的地址)。
恭喜,你刚刚使用Netty完成了你的第一台服务器。

深入接收到的数据

现在我们已经写了第一个服务器,我们需要测试它是否真的有效。 测试它的最简单的方法是使用telnet命令。 例如,您可以在命令行中输入telnet localhost 8080并输入一些内容。 但是,我们可以说服务器工作正常吗? 我们不能确定,因为它是一个丢弃服务器。 你根本不会得到任何回应。 为了证明它真的正常工作,让我们修改服务器来打印它已经收到的内容。 我们已经知道,在收到数据时,会调用channelRead()方法。 让我们把一些代码放到DiscardServerHandler的channelRead()方法中:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ByteBuf in = (ByteBuf) msg;
    try {
        while (in.isReadable()) { // (1)
            System.out.print((char) in.readByte());
            System.out.flush();
        }
    } finally {
        ReferenceCountUtil.release(msg); // (2)
    }
}

1.这种低效的循环可以简化为:System.out.println(in.toString(io.netty.util.CharsetUtil.US_ASCII))
2.另外,你可以在这里执行in.release()
如果你再次运行telnet命令,将会看到服务器将接收到的信息打印出来。
完整的丢弃服务源代码在这里

猜你喜欢

转载自blog.csdn.net/u014453515/article/details/77142313