Netty入门实例-编写服务器端程序

2019-04-20 15:39|来源: 网路

按照以前的套路,一般学习一项新的IT技术,首先得来个 ‘Hello,World!‘之类的,以向世界宣告你要学习某项技术了。但在世界上最简单的协议不是’Hello,World!‘而是 DISCARD。它是一种丢弃任何接收到的数据而没有任何响应的协议(暂时就叫它”装死协议”吧)。

要实现DISCARD协议,只需要忽略所有接收到的数据。让我们从处理程序实现直接开始,这个处理程序实现处理Netty生成的I/O事件。先撸下面几串代码吧 -

package cn.netty;

import io.netty.buffer.ByteBuf;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * 处理服务器端通道
 */
public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
        // 以静默方式丢弃接收的数据
        ((ByteBuf) msg).release(); // (3)
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
        // 出现异常时关闭连接。
        cause.printStackTrace();
        ctx.close();
    }
}


一些重要的解释:

  1. DiscardServerHandler扩展了ChannelInboundHandlerAdapter,它是ChannelInboundHandler的一个实现。ChannelInboundHandler提供了可以覆盖的各种事件处理程序方法。 现在,它只是扩展了ChannelInboundHandlerAdapter,而不是自己实现处理程序接口。

  2. 我们在这里覆盖channelRead()事件处理程序方法。每当从客户端接收到新数据时,使用该方法来接收客户端的消息。 在此示例中,接收到的消息的类型为ByteBuf

  3. 要实现DISCARD协议,处理程序必须忽略接收到的消息。ByteBuf是引用计数的对象,必须通过release()方法显式释放。请记住,处理程序负责释放传递给处理程序的引用计数对象。 通常,channelRead()处理程序方法实现如下:

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

    当Netty由于I/O错误或由处理事件时抛出的异常而导致的处理程序实现引发异常时,使用Throwable调用exceptionCaught()事件处理程序方法。 在大多数情况下,捕获的异常会被记录,并且其相关的通道应该在这里关闭,这种方法的实现可以根据想要什么样的方式来处理异常情况而有所不同。 例如,您可能希望在关闭连接之前发送带有错误代码的响应消息。


到现在如果没有问题,我们已经实现了DISCARD服务器的前半部分。 现在剩下的就是编写main()方法,并使用DiscardServerHandler启动服务器。

package cn.netty;

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。 第一个通常称为“boss”,接受传入连接。 第二个通常称为“worker”,当“boss”接受连接并且向“worker”注册接受连接,则“worker”处理所接受连接的流量。 使用多少个线程以及如何将它们映射到创建的通道取决于EventLoopGroup实现,甚至可以通过构造函数进行配置。

  2. ServerBootstrap是一个用于设置服务器的助手类。 您可以直接使用通道设置服务器。 但是,请注意,这是一个冗长的过程,在大多数情况下不需要这样做。

  3. 在这里,我们指定使用NioServerSocketChannel类,该类用于实例化新的通道以接受传入连接。

  4. 此处指定的处理程序将始终由新接受的通道计算。 ChannelInitializer是一个特殊的处理程序,用于帮助用户配置新的通道。 很可能要通过添加一些处理程序(例如DiscardServerHandler)来配置新通道的ChannelPipeline来实现您的网络应用程序。 随着应用程序变得复杂,可能会向管道中添加更多处理程序,并最终将此匿名类提取到顶级类中。

  5. 还可以设置指定Channel实现的参数。这里编写的是一个TCP/IP服务器,所以我们允许设置套接字选项,如tcpNoDelaykeepAlive。 请参阅ChannelOption的apidocs和指定的ChannelConfig实现,以了解关于ChannelOptions

  6. 你注意到option()childOption()没有? option()用于接受传入连接的NioServerSocketChannelchildOption()用于由父ServerChannel接受的通道,在这个示例中为NioServerSocketChannel

  7. 现在准备好了。剩下的是绑定到端口和启动服务器。 这里,我们绑定到机器中所有NIC(网络接口卡)的端口:8080。 现在可以根据需要多次调用bind()方法(使用不同的绑定地址)。
    恭喜!这就完成了一个基于 Netty 的第一个服务器。

查看接收的数据

现在我们已经编写了第一个服务器,还需要测试它是否真的有效地运行工作。测试它的最简单的方法是使用telnet命令。 例如,可以在命令行中输入telnet localhost 8080并键入内容。

但是,能验证服务器工作正常吗? 其实不能真正知道,因为它是一个”丢弃“(什么也不处理)服务器。所以发送什么请求根本不会得到任何反应。 为了证明它是真的在运行工作,我们还修改一点服务器端上的代码 - 打印它收到了什么东西。

前面我们已经知道,只要接收到数据,就调用channelRead()方法。现在把一些代码放到DiscardServerHandlerchannelRead()方法中,如下所示:

@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)
    }
    // 或者直接打印
    System.out.println("Yes, A new client in = " + ctx.name());
}


这个低效的循环实际上可以简化为:

System.out.println(in.toString(io.netty.util.CharsetUtil.US_ASCII))


或者,可以在这里写上:in.release()

运行 DiscardServer ,输出结果如下 -

三月 01, 2017 00:02:07 上午 io.netty.handler.logging.LoggingHandler channelRegistered
信息: [id: 0x4f99602f] REGISTERED
三月 01, 2017 00:02:07 上午 io.netty.handler.logging.LoggingHandler bind
信息: [id: 0x4f99602f] BIND: 0.0.0.0/0.0.0.0:8080
三月 01, 2017 00:02:07 上午 io.netty.handler.logging.LoggingHandler channelActive
信息: [id: 0x4f99602f, L:/0:0:0:0:0:0:0:0:8080] ACTIVE


如果再次运行telnet localhost 8080命令,将会看到服务器打印接收到的内容。

三月 01, 2017 2:14:14 上午 io.netty.handler.logging.LoggingHandler channelRegistered
信息: [id: 0xd63646da] REGISTERED
三月 01, 2017 2:14:14 上午 io.netty.handler.logging.LoggingHandler bind
信息: [id: 0xd63646da] BIND: 0.0.0.0/0.0.0.0:8080
三月 01, 2017 2:14:14 上午 io.netty.handler.logging.LoggingHandler channelActive
信息: [id: 0xd63646da, L:/0:0:0:0:0:0:0:0:8080] ACTIVE
三月 01, 2017 2:14:32 上午 io.netty.handler.logging.LoggingHandler channelRead
信息: [id: 0xd63646da, L:/0:0:0:0:0:0:0:0:8080] RECEIVED: [id: 0x452d2ebd, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:58248]
Yes, A new client in = DiscardServerHandler#0
Yes, A new client in = DiscardServerHandler#0


上面的输出证明,这个服务器程序是可以正常运行工作的。


相关问答

更多
  • 我给你一个类似的代码,你自己改一下就可以了,我不给你该了。希望对你有所帮助! <----------------------服务器端-------------------------------> //实现多线程的网络连接 package cn.socket; import java.io.*; import java.net.*; public class SocketServerTrue { public SocketServerTrue() { try { //服务器开启一个端口 System.out ...
  • 可以用android http访问方式访问java客户端。java客户端可以使用action进行连接。。又或者对socket比较熟悉的话,可以使用mina进行socket通信。也是可以的,不过这些都需要时间。没有进行尝试,可以试着写写。至于其他的,参考普通的webproject即可。 1,先google一下下载一个tomcat, 2,再次google一下百度也可以,下载一个eclipse的tomcat插件,解压之后直接放入eclipse的plugin文件夹下面,然后重启eclipse(之后觉的这个步骤好像没 ...
  • Java主要用于开发服务器端的Java应用程序、移动设备上嵌入式系统的应用开发。 1、Java可以开发Web程序,互联网应用、网页开发等。 2、Java也可以开发桌面应用程序、游戏、管理系统等。 3、Java还可以开发在Android平台上的移动应用。
  • 你写的程序放在服务器电脑上运行,叫做服务器端编程。 既然是叫做服务器,也说明你写的程序要可以接受客户端的互动。 你写的程序放在客户端上运行,叫做客户端编程。
  • 引自网页 MySQL数据库的安装调试和VC实现 作者:张国富 (合肥工业大学 计算机与信息学院,合肥 230009) 源代码下载 (调试此Demo需要将目录里的mydb子目录拷到MySQL安装目录的data子目录下(我的是:D:\Program Files\MySQL\MySQL Server 5.0\data) 摘要:本文详细阐述了如何进行MySQL的安装、调试,以及如何用VC进行编译,实现数据的“添加、修改、删除”等功能。 一、MySQL的安装 可以考虑安装mysql-5.0.41-win32(可到 h ...
  • 通信信息在网络上都是以byte传输的。只要客户端的消息格式符合netty的规则就可以通信。跟客户端用不用netty没有关系
  • 这和netty没啥关系吧,服务器端不能主动和客户端建立连接,除非客户端在监听,并且服务器端知道客户端IP和端口
  • 回复 1# 的帖子 nginx默认就是支持ssi的,在配置文件中开启即可。在虚拟主机location 段里加上(加在http,server段里也可以)ssi on;ssi_silent_errors on;ssi_types text/shtml;这3行,再/usr/local/nginx/sbin/nginx -s reload 重启nginx
  • 什么是服务器端[2021-03-14]

    简单的说,服务器端是为客户端服务的,服务的内容诸如向客户端提供资源,保存客户端数据等等.客户端可以是任意的一台电脑,只要它和服务器端存在连接,并且得到了服务器端的授权,就可以使用服务器端的服务.象现在就可以理解为百度的网站是服务器端,我们现在使用的电脑就是客户端.我们可以使用它的服务. 通常的服务器端都是服务器级的高级PC,以便多客户访问时不会造成延时甚至数据溢出.