Netty5用户手册之二:使用netty实现Discard服务器程序

编写一个discard服务器程序

        世界上最简单的协议不是‘hello world’而是discard。他是一个丢弃任何接收到的数据并且无任何响应的协议。
        为了实现dicard协议,我们只需要做一件事就是忽略所有接收到的数据。让我们从handler处理器的实现开始,handler在netty中是一个用来处理io事件的的处理器。
<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;">import io.netty.buffer.ByteBuf;

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

/**
</span></span>
<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;"> * 处理一个服务器通道
 */
public class DiscardServerHandler extends ChannelHandlerAdapter { // (1)

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
        // 直接调用release来释放接收到的数据
        ((ByteBuf) msg).release(); // (3)
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
        // 当异常发生时,关闭处理器
        cause.printStackTrace();
        ctx.close();
    }
}</span></span>
      1.DiscardServerHandler继承了ChannelHandlerAdapter类,这个类是ChannelHandler的一个实现类。其中ChannelHandler提供了各类事件的处理器方法方便我们来覆盖。但是,现在继承ChannelHandlerAdapter类比实现ChannelHandler更方便。
     2.代码中覆盖了channelRead()事件处理器方法。这个方法在无论何时客户端传送给送服务器都会被调用而用来接收客户端
的数据。这个例子中,接收到的数据消息就是ByteBuf对象。
  3.为了实现discard协议,这个处理器对象必须忽略接收到的数据。bytebuf是一个引用计数对象,因此它必须通过realeas方法
进行释放。请记住,处理器的职责就是释放所传递处理器的引用计数对象,通常,channelRead方法的实现就像下面的代码:
<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="font-family:Microsoft YaHei;">@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
   try {
        // Do something with msg
    } finally {
        ReferenceCountUtil.release(msg);
    }
}
</span></span>
          4.exceptionCaught()事件处理器方法主要适用于一个异常发生的时候,这个异常可能是在netty中被一个io错误引起或者一个
handler处理器实现逻辑时发生了异常引起的。在大部分情况下在这个方法中,被捕捉的异常需要被记录到日志中,并且它关联的
channel需要在此处被关闭,然而这个方法的处理方式会在遇到不同异常的情况下有不同的实现,比如你可能想在关闭连接之
前发送一个错误码的响应消息。
        OK,到目前为止,我们已经实现了discard服务器的一半了,下面为剩下的main方法。
<span style="font-family:Microsoft YaHei;font-size:14px;">package com.zzj.nio.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;

/**
 * 抛弃消息服务端
 * @author zzj
 * @date Oct 19, 2016 1:24:05 PM
 */
public class DiscardServer {

	/**端口号**/
	private int port;
	
	/**
	 * 构造函数
	 * @param port
	 */
	public DiscardServer(int port){
		this.port = port;
	}
	
	public static void main(String[] args) {
		int port = 9999;
		try {
			//开启服务端接受消息
			new DiscardServer(port).run();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 开启服务器
	 * @author zzj
	 * @throws InterruptedException 
	 */
	public void run() throws InterruptedException{
		EventLoopGroup bossGroup = new NioEventLoopGroup();//(1)
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap bootstrap = new ServerBootstrap();//(2)
			bootstrap.group(bossGroup, workerGroup)
			.channel(NioServerSocketChannel.class)//(3)
			.childHandler(new ChannelInitializer<SocketChannel>() {//(4)

				@Override
				protected void initChannel(SocketChannel channel) throws Exception {
					channel.pipeline().addLast(new DiscardServerHandler());
				}
			});
			bootstrap.option(ChannelOption.SO_BACKLOG, 128)//(5)
			.childOption(ChannelOption.SO_KEEPALIVE, true);//(6)
			
			//紧接着,绑定并且开始接受请求的连接
			ChannelFuture future = bootstrap.bind(port).sync();
			
			//等待直到服务器socket被关闭,在这里例子中,这个不会发生,但是你可以这样写
			future.channel().closeFuture().sync();
			
		}finally{
			
			//优雅的关闭
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
	}
}
</span>
1.NioEventLoopGroup是一个用户处理io操作的多线程event loop.Netty对于不同种类的传输提供了多种EventLoopGroup实现方式。在这个例
子中我们实现了一个server端的应用程序,并且用到了两个NioEventLoopGroup对象,其中,第一个通常被称为boss,它主要用于接收连接过来的连
接。而第二个通常称为worker,用来处理已经被接受的连接,一旦boss接收到连接,就会把连接信息注册到worker上。如何知道多少个线程已经被
使用,如何映射到已经创建的Channels上都需要依赖于EventLoopGroup的实现,并且可以通过构造函数来配置他们的关系
2.ServerBootStrap是一个帮助的工具类用于配置一个服务器。你可以通过一个Channel直接来配置服务器。然而,请注意这是一个复杂的过程,
而且很多情况下没有必要这么做。
3.这里,我们给出了使用NioServerSocketChannel类用户实例化一个Channel来接收链接过来的连接。
4.这里的处理器类用来处理最近被接受过来的Channel。ChannelInitializer是一个专门的处理器是用于帮助用户配置一个新的Channel。 
也许你想通过增加一些处理类比如DiscardServerHandle来配置一个新的Channel或者其对应的ChannelPipeline来实现你的网络程序。随着应用程序
变得复杂,你可能需要为pipeline添加更多的处理器,然后提取这些匿名类到最顶层的类上。
5. 你可以设置这里指定的通道实现的配置参数。我们正在写一个TCP/IP的服务端,因此我们被允许设置socket的参数选项比如tcpNoDelay和
keepAlive。请参考ChannelOption和详细的ChannelConfig实现的接口文档以此可以对ChannelOptions的有一个大概的认识.
6. 你关注过option()和childOption()吗?option()是提供给NioServerSocketChannel用来接收进来的连接。childOption()是提供给由父管道
ServerChannel接收到的连接,在这个例子中也是NioServerSocketChannel。
 7. 我们继续,剩下的就是绑定端口然后启动服务。这里我们在机器上绑定了机器所有网卡上的8080端口。当然现在你可以多次调用bind()方法(基于不同绑定地址)。

如果,想要在服务器中打印出客户端输入的消息,那么可以使用一下代码:
@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)
    }
}
OK,到此为止discard服务端程序就完成了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值