Netty框架入门

1:下载netty的jar和官方文档:http://www.jboss.org/netty/。现在的稳定版netty-3.6.3.Final-dist.tar.bz2

2:将一个jar(netty-3.6.3.Final.jar)加到项目中,另一个源码包(netty-3.6.3.Final-sources.jar)也可以加进去。

3:例子实现

《服务端程序》

(一)首先编写解码器

MessageDecoder.java

/**
 * 解码器的类
 */
package test;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.frame.FrameDecoder;

/**
 * @author 桑叶
 *
 */
public class MessageDecoder extends FrameDecoder{

	@Override
	protected Object decode(ChannelHandlerContext ctx, Channel channel,
			ChannelBuffer buffer) throws Exception {
		// TODO 自动生成的方法存根
		if (buffer.readableBytes() < 4) {
			return null;
		}
		int dataLength = buffer.getInt(buffer.readerIndex());
		if (buffer.readableBytes() < dataLength+4) {
			return null;
		}
		buffer.skipBytes(4);
		byte[] decoded = new byte[dataLength];
		buffer.readBytes(decoded);
		String msg = new String(decoded);
		return msg;
	}

	
}
MessageDecoder继承自FrameDecoder,FrameDecoder是Netty code包中的辅助类,它是个ChannelUpstreamHandler,decode方法是FrameDecoder子类需要实现的。在上面的代码中有:

(1)检查ChannelBuffer中的字节数,如果ChannelBuffer可读的字节数少于4,则返回null等待下次读事件。

(2)继续检查ChannelBuffer中的字节数,如果ChannelBuffer可读的字节数少于dataLength+4,则返回null等待下次读事件。

(3)越过dataLength的字节。

(4)构造解码的字符串返回。
(二)编码器

/**
 * 编码器
 */
package test;

import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.handler.codec.oneone.OneToOneEncoder;

/**
 * @author 桑叶
 *
 */
public class MessageEncoder extends OneToOneEncoder {

	@Override
	protected Object encode(ChannelHandlerContext ctx, Channel channel,
			Object msg) throws Exception {
		// TODO 自动生成的方法存根
		if (!(msg instanceof String)) {
			return msg;
		}
		String res = (String)msg;
		byte[] data = res.getBytes();
		int dataLength = data.length;
		ChannelBuffer buffer = ChannelBuffers.dynamicBuffer();
		buffer.writeInt(dataLength);
		buffer.writeBytes(data);
		return buffer;
	}
}
MessageEncoder是个ChannelDownstreamHandler。对该类的注解如下:

(1)如果编码的msg不是合法的类型,就直接返回该msg,之后OneToOneEncoder会调用ctx.sendDownstream(evt);来调用下一个ChannelDownstreamHandler。对于该例子来说,这种情况不应该出现的。

(2)开发者创建ChannelBuffer的用武之地就是这儿了,通常使用dynamicBuffer即可,表示得到的ChannelBuffer可动态增加大小。

(3)返回编码后的ChannelBuffer之后,OneToOneEncoder会调用Channels.write将数据写回客户端。

(三)服务端业务处理

/**
 * 服务端业务处理
 */
package test;

import java.util.logging.Level;
import java.util.logging.Logger;

import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;

/**
 * @author 桑叶
 *
 */
public class MessageServerHandler extends SimpleChannelUpstreamHandler {

	private static final Logger logger = Logger.getLogger(MessageServerHandler.class.getName());
	
	@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
		if (!(e.getMessage() instanceof String)) {
			return ;
		}
		String msg = (String)e.getMessage();
		System.err.println("got msg:"+msg);
		e.getChannel().write(msg);
	}
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
		logger.log(
				Level.WARNING,
				"Unexpected exception from downstream.",
				e.getCause());
		e.getChannel().close();
	}
	
	public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
		
	}
	
}
MessageServerHandler是服务端业务处理handler,其继承自SimpleChannelUpstreamHandler,并主要实现messageReceived事件。 对该类的注解如下:

(1)该upstream事件流中,首先经过MessageDecoder,其会将decoder返回的解码后的数据结构造成MessageEvent.getMessage(),所以在handler上下文关系中,MessageEvent.getMessage()并不一定都返回ChannelBuffer
(2)MessageServerHandler只是简单的将得到的msg再写回给客户端。

e.getChannel().write(msg);操作将触发DownstreamMessageEvent事件,也就是调用上面的MessageEncoder将编码的数据返回给客户端。

(三)服务端主程序

/**
 * 服务端
 */
package test;

import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;

/**
 * @author 桑叶
 *
 */
public class MessageServer {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws Exception {
		// TODO 自动生成的方法存根
		ServerBootstrap bootstrap = new ServerBootstrap(
				new NioServerSocketChannelFactory(
						Executors.newCachedThreadPool(),
						Executors.newCachedThreadPool()));
		
		bootstrap.getPipeline().addLast("decoder", new MessageDecoder());
		bootstrap.getPipeline().addLast("encoder", new MessageEncoder());
		bootstrap.getPipeline().addLast("handler", new MessageServerHandler());
		
		bootstrap.bind(new InetSocketAddress(9550));
	}
}

(1)启动服务端主程序

(2)创建了3个ChannelHandler,需要将他们注册到ChannelPipeline,而ChannelPipeline又是和Channel对应的(是全局单例还是每个Channel对应一个ChannelPipline实例依赖于实现)。

(3)可以实现ChannelPipline的工厂接口ChannelPiplineFactory实现该目的。
(4)最后绑定端口号

《客户端程序》

(一)客户端处理

/**
 * 客户端处理
 */
package test;


import java.util.logging.Level;
import java.util.logging.Logger;


import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;


/**
 * @author 桑叶
 *
 */
public class MessageClientHandler extends SimpleChannelUpstreamHandler {


	private static final Logger logger = Logger.getLogger(MessageClientHandler.class.getName());
	
	@Override
	public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
		String message = "hello kafka0102";
		e.getChannel().write(message);
	}
	
	@Override
	public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
		System.out.println("messageReceived send message "+e.getMessage());
		try {
			Thread.sleep(3000);
		} catch (InterruptedException ex) {
			// TODO: handle exception
			ex.printStackTrace();
		}
		e.getChannel().write(e.getMessage());
	}
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
		logger.log(
				Level.WARNING,
				"Unexpected exception from downstream.",
				e.getCause());
		e.getChannel().close();
	}
}
(1)客户端是先发送数据到服务端(downstream事件流),然后是处理从服务端接收的数据(upstream事件流)。

(2)这里有个问题是,怎么把需要发送的数据送到downstream事件流里呢?

这就用到了ChannelUpstreamHandler的channelConnected事件了。
(二)客户端主程序

/**
 * 客户端
 */
package test;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.concurrent.Executors;

import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;

/**
 * @author 桑叶
 *
 */
public class MessageClient {

	/**
	 * @param args
	 */
	public static void main(String[] args) throws IOException {
		// TODO 自动生成的方法存根
		create();
	}
	
	public static void create() {
		String host = "127.0.0.1";
		int port = 9550;
		//实例化一个客户端Bootstrap实例,其中NioClientSocketChannelFactory实例由Netty提供
		ClientBootstrap bootstrap = new ClientBootstrap(
				new NioClientSocketChannelFactory(
						Executors.newCachedThreadPool(),
						Executors.newCachedThreadPool()));
		//设置PipelineFactory,由客户端自己实现
		bootstrap.getPipeline().addLast("decoder", new MessageDecoder());
		bootstrap.getPipeline().addLast("encoder", new MessageEncoder());
		bootstrap.getPipeline().addLast("handler", new MessageClientHandler());
		
		//开始请求连接
		ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port));
		//等待连接关闭 或 请求连接失败
		future.getChannel().getCloseFuture().awaitUninterruptibly();
		
		//释放外部资源
		bootstrap.releaseExternalResources();
	}
}

《调试》

首先运行 MessageServer 服务端程序,然后再运行 MessageClient 客户端程序.

结果如下:

messageReceived send message hello kafka0102
messageReceived send message hello kafka0102
messageReceived send message hello kafka0102

......


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值