Netty(二)Discard Server 与 ChannelInitializer

4 篇文章 0 订阅
4 篇文章 0 订阅

Server端代码:

public class DiscardServer {

	private static final int PORT_NUM = Integer.parseInt(System.getProperty("port", "6767"));

	public void run() throws CertificateException, SSLException {
		
		EventLoopGroup bossGroup = new NioEventLoopGroup(); // 用于accept incomming连接请求
		EventLoopGroup workerGroup = new NioEventLoopGroup(); // 程序将accepted连接请求注册到workerGroup中,并由里面的线程跟踪处理相关“traffic”
		try {
			ServerBootstrap sb = new ServerBootstrap(); // “helper class” 2 set up a server
			sb.group(bossGroup, workerGroup)
			.channel(NioServerSocketChannel.class) // 利用 NioServerSocketChannel class 的初始化Server端的Channel实例来accept连接请求
			.handler(new LoggingHandler(LogLevel.INFO)) // 设置handler,
			.childHandler(new DiscardServerInitializer()); // 设置另外一个handler,childHanlder与handler的不同会在后续博文中描述,Handler在Netty是一个很重要的地位,相当于Servlet中的filter一样,这里用一个ChannelInitializer来统一管理所有handler
			
			// bind and start to accept incoming connections
			ChannelFuture cf = sb.bind(PORT_NUM).sync(); // 绑定端口,并利用sync()方法开始监听端口(等待客户端连接请求),若成功,返回ChannelFuture,ChannelFuture也会在后续详细说明
			
			// Wait until the server socket is closed
			// 本例中,下面一行代码永远不会发生,但可以用于正常关闭server
			// 监听服务关闭监听
			cf.channel().closeFuture().sync();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			bossGroup.shutdownGracefully(); // 相当于是关闭线程池
			workerGroup.shutdownGracefully();
		}
	}
	
	public static void main(String[] args) throws CertificateException, SSLException {
		DiscardServer server = new DiscardServer();
		server.run();
	}
}

Client端代码:

public class DiscardClient {

	private static final String REMOET_HOST = System.getProperty("host", "127.0.0.1");
	private static final int REMOTE_PORT = Integer.parseInt(System.getProperty("port", "6767"));
	public static final int BUFFER_SIZE = Integer.parseInt(System.getProperty("size", "8192"));
	
	public void run() throws SSLException, CertificateException, InterruptedException {
		EventLoopGroup group = new NioEventLoopGroup();
		try {
			Bootstrap bs = new Bootstrap();
			bs.group(group)
			.channel(NioSocketChannel.class)
			.handler(new DiscardClientInitializer());
			
			// make the connection attempt
			ChannelFuture f = bs.connect(REMOET_HOST, REMOTE_PORT).sync();
			
			// Wait until the connection is closed
			f.channel().closeFuture().sync();
		} finally {
			group.shutdownGracefully();
		}
	}
}



Initializer(Server与Client的几乎是通用的):

public class DiscardServerInitializer extends ChannelInitializer<SocketChannel> {
	
	private static final boolean SSL = System.getProperty("ssl") != null;
	private final SslContext sslCtx;
	
	public DiscardServerInitializer() throws SSLException, CertificateException {
		if (SSL) {
			// 如果是需要使用SSL加密传输,则初始化并获取一个self-signed certificate 4 testing
			SelfSignedCertificate ssc = new SelfSignedCertificate();
			// 返回一个openJDK默认的X509临时证书生成文件和密钥文件
			sslCtx = SslContext.newServerContext(ssc.certificate(), ssc.privateKey());
		} else {
			sslCtx = null;
		}
	}

	@Override
	protected void initChannel(SocketChannel ch) throws Exception {
		// 从SocketChannel中获得管道
		ChannelPipeline pipeline = ch.pipeline();
		<pre name="code" class="java">                // 都属于ChannelOutboundHandler,按逆序执行
		if (sslCtx != null) {
			// 安全通信通道
			pipeline.addLast(sslCtx.newHandler(ch.alloc())); // 字符串
		}
		pipeline.addLast("encoder", new StringEncoder());
// 都属于ChannelInboundHandler,顺序执行pipeline.addLast("frame", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));pipeline.addLast("decoder", new StringDecoder()); // 字符串解码// 自定义handlerpipeline.addLast("handler", new DiscardServerHandler());}}
 

ChannelInitializer的主要目的是为SocketChannel注册Handler,也就是当一个SocketChannel被Server accept 进来以后,进来的数据要分别按注册先后顺序执行哪些handler,出去的数据要分别按逆序的顺序执行哪些handler!这里的注册handler的先后顺序十分重要,ChannelInboundHandler都是按顺序执行的,ChanneloutboundHandler都是按逆序的顺序执行的如下图所示:

同时需要说明一点的是:(下面##之间的说法有误,修正在下面)

###########################################################################################################################

client端和server端的Inbound和Outbound顺序应该刚好相反如下所示:

Server:

@Override
	protected void initChannel(SocketChannel ch) throws Exception {
		// 从SocketChannel中获得管道
		ChannelPipeline pipeline = ch.pipeline();
		
		// 都属于ChannelInboundHandler,顺序执行
		pipeline.addLast("frame", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
		pipeline.addLast("decoder", new StringDecoder()); // 字符串解码
		// 自定义handler
		pipeline.addLast("handler", new DiscardServerHandler());
		
		// 都属于ChannelOutboundHandler,按逆序执行
		if (sslCtx != null) {
			// 安全通信通道
			pipeline.addLast(sslCtx.newHandler(ch.alloc())); // 字符串
		}
		pipeline.addLast("encoder", new StringEncoder());
	}
Client:

@Override
	protected void initChannel(SocketChannel ch) throws Exception {
		// 这里要和服务器对应起来
		// 否则无法正常解码和编码
		ChannelPipeline pipeline = ch.pipeline();
		// 都属于ChannelOutboundHandler,按逆序执行
		if (sslCtx != null) {
			// 安全通信通道
			pipeline.addLast(sslCtx.newHandler(ch.alloc())); // 字符串
		}
		pipeline.addLast("encoder", new StringEncoder());
		
		// 从SocketChannel中获得管道
		pipeline.addLast("frame", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
		// 字符串解码、编码
		pipeline.addLast("decoder", new StringDecoder());
		// 自定义handler
		pipeline.addLast("handler", new DiscardClientHandler());
	}


否则程序执行的过程中会出现如下错误:

java.lang.UnsupportedOperationException: unsupported message type: String (expected: ByteBuf, FileRegion)
	at io.netty.channel.nio.AbstractNioByteChannel.filterOutboundMessage(AbstractNioByteChannel.java:281)
	at io.netty.channel.AbstractChannel$AbstractUnsafe.write(AbstractChannel.java:697)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.write(DefaultChannelPipeline.java:1114)
	at io.netty.channel.AbstractChannelHandlerContext.invokeWrite(AbstractChannelHandlerContext.java:705)
	at io.netty.channel.AbstractChannelHandlerContext.write(AbstractChannelHandlerContext.java:763)
	at io.netty.channel.AbstractChannelHandlerContext.writeAndFlush(AbstractChannelHandlerContext.java:753)
	at io.netty.channel.AbstractChannelHandlerContext.writeAndFlush(AbstractChannelHandlerContext.java:788)
	at cn.scut.chiu.netty.examples.DiscardClientHandler.generateTraffic(DiscardClientHandler.java:74)
	at cn.scut.chiu.netty.examples.DiscardClientHandler.channelActive(DiscardClientHandler.java:58)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:212)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelActive(AbstractChannelHandlerContext.java:198)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelActive(ChannelInboundHandlerAdapter.java:64)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:212)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelActive(AbstractChannelHandlerContext.java:198)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelActive(ChannelInboundHandlerAdapter.java:64)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelActive(AbstractChannelHandlerContext.java:212)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelActive(AbstractChannelHandlerContext.java:198)
	at io.netty.channel.DefaultChannelPipeline.fireChannelActive(DefaultChannelPipeline.java:818)
	at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.fulfillConnectPromise(AbstractNioChannel.java:252)
	at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:282)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:528)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
	at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:111)
	at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
	at java.lang.Thread.run(Unknown Source)

###########################################################################################################################

修正:

报错的原因似乎不是因为顺序需要相反,相反,Server和Cilent注册handler时候对应的注册先后顺序应该一样的!!如下所示:

Server和Client都是这样:

@Override
	protected void initChannel(SocketChannel ch) throws Exception {
		// 从SocketChannel中获得管道
		ChannelPipeline pipeline = ch.pipeline();
		
		// 都属于ChannelOutboundHandler,按逆序执行
		if (sslCtx != null) {
			// 安全通信通道
			pipeline.addLast(sslCtx.newHandler(ch.alloc())); // 字符串
		}
		pipeline.addLast("encoder", new StringEncoder());
		
		// 都属于ChannelInboundHandler,按注册先后顺序执行
		pipeline.addLast("frame", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
		// 字符串解码、编码
		pipeline.addLast("decoder", new StringDecoder());
		// 自定义handler
		pipeline.addLast("handler", new DiscardServerHandler());
	}

可以发现的是这里的顺序和之前的说法中Client端的注册顺序是一样的,归结起来题主假设造成之前错误的原因应该是,ChannelOutboundHandler在这种Client主动连接Server的情况下,要先注册ChannelOutboundHandler,再注册ChannelInboundHandler。




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值