搞懂Netty(三)-写一个Discard服务器

在这里插入图片描述

在这篇文章,我实现一个最简单的协议-DISCARD协议。它把收到的请求数据立即抛弃,而且不做任何回复。
我们知道,Netty是一个事件驱动的网络程序框架,因此我们先从编写我们自己的事件处理器开始。

编写事件处理器

package netty.example.discard;

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) throws Exception { // (2)
        // Discard the received data silently.
        ((ByteBuf)msg).release(); // (3)
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { // (4)
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }
}
  1. DiscardServerHandler继承ChannelInboundHandlerAdapterChannelInboundHandlerAdapter简单实现了接口ChannelInboundHandler中的所有方法。ChannelInboundHandler提供了很多种可被重写的事件处理方法。由于我们的程序简单,我们只需继承ChannelInboundHandlerAdapter,然后重学我们需要自定义的方法即可。
  2. 这里我们重写了channelRead()方法。当有客户端发来请求的时候,这个方法会被调用,请求携带的数据会作为参数传进来。在这里,收到的数据的类型是ByteBuf
  3. 鉴于我们实现的是DISCARD协议,这里忽略收到的数据。另外需要注意的是,ByteBuf是携带引用计数的,需要显示的调用release()释放引用计数。我们在编写处理器是,要时刻想着返回前要释放传进来的任何带引用计数的对象。通常情况下, channelRead() 方法会遵循如下方式编写:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    try {
        // Do something with msg
    } finally {
        ReferenceCountUtil.release(msg);
    }
}
  1. 当I/O发生错误,或者事件处理器执行过程中发生错误,会触发exceptionCaught()方法,该方法携带一个Throwable的参数。通常情况下,异常信息写到日志,关闭关联的channel。当然,根据具体情况会有不同的实现。比如,返回一个带有错误码的响应信息。
    当目前为止,一切都很好,我们已经实现了一半的需求。接下来,我们编写main()方法启动服务器。

编写main()方法启动服务器

package 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;

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
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new DiscardServerHandler());
                        }
                    })
                    .option(ChannelOption.SO_BACKLOG, 128) // (5)
                    .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

            // 绑定端口并等待连接
            ChannelFuture f = b.bind(port).sync(); // (7)

            // 阻塞直到 server socket 关闭。
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        }

        new DiscardServer(port).run();
    }
}
  1. NioEventLoopGroup是一个多线程的,处理I/O的事件循环。Netty为不同的传输类型提供了很多种 EventLoopGroup的实现。我们这里因为实现的是服务器端,因此需要两个 NioEventLoopGroup,第一个称之为’boss’,负责接受连接。第二个称之为’worker’,负责已创建的并注册到worker的连接的数据交换。不同的 EventLoopGroup的实现决定使用多少线程以及哪个线程如何分配到创建的Channel
  2. ServerBootstrap是一个helper class,负责配置启动服务器。
  3. 这里,我们指定NioServerSocketChannel用于实例化Channel以接受新的连接。
  4. 这里指定的处理器被已已创建的连接使用。ChannelInitializer是一个特殊的处理器,用于配置 ChannelPipeline,增加新的处理器,如DiscardServerHandler。随着程序的复杂度的增加,在 ChannelPipeline加入更多的处理器,你可能会把这个匿名类抽出来成为top-level的类。
  5. 你可以为不同的Channel实现配置响应的选项。我们正在写基于TCP/IP的服务器,因此我们可以设置socket选项:tcpNoDelaykeepAlive。参考apidocs ChannelOption和特定的ChannelConfig 实现。
  6. option() 用于接受连接的NioServerSocketChannelchildOption() 用于已经被ServerChannel创建连接的Channel,即NioSocketChannel
  7. 最后,绑定端口并启动服务。这里我们绑定机器所有网卡的8080端口,你可以多次调用bind(),绑定不同的地址。
    到这,恭喜你,你已经完成了第一个基于Netty的网络程序。
  • 45
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Netty一个基于Java的高性能网络编程框架,它提供了一种简单而强大的方式来构建可扩展的网络应用程序。如果你想编一个Netty游戏服务器,可以按照以下步骤进行: 1. 导入Netty库:首先,在你的项目中导入Netty库。你可以在Maven或Gradle中添加相应的依赖项。 2. 创建服务器引导类:创建一个服务器引导类,用于配置和启动你的Netty服务器。这个类将负责设置服务器的各种参数,如端口号、线程模型等。 3. 配置ChannelHandler:Netty使用ChannelHandler来处理网络事件和数据。你需要编自定义的ChannelHandler来处理游戏逻辑,如接收和处理客户端的请求、发送游戏状态等。 4. 实现业务逻辑:根据你的游戏需求,实现相应的业务逻辑。这可能涉及到游戏规则、玩家管理、战斗逻辑等方面。 5. 处理网络事件:在你的ChannelHandler中,重相应的方法来处理网络事件,如连接建立、数据接收、连接断开等。你可以根据需要进行相应的处理,如解析数据、更新游戏状态等。 6. 启动服务器:在服务器引导类中,调用相应的方法来启动你的Netty服务器。一旦服务器启动,它将开始监听指定的端口,并等待客户端的连接。 7. 测试和调试:在服务器启动后,你可以使用相应的客户端程序来测试和调试你的游戏服务器。确保服务器能够正确地接收和处理客户端的请求,并返回正确的响应。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值