Netty是一个异步的、事件驱动的网络应用框架,可以用来快速开发高性的客户端、服务端程序。示例使用Netty 3.10.5
首先是Server部分的代码:
Server端主程序:
- package com.sean.server;
- import java.net.InetSocketAddress;
- import java.util.concurrent.Executors;
- import org.jboss.netty.bootstrap.ServerBootstrap;
- import org.jboss.netty.channel.Channel;
- import org.jboss.netty.channel.ChannelFactory;
- import org.jboss.netty.channel.group.ChannelGroup;
- import org.jboss.netty.channel.group.ChannelGroupFuture;
- import org.jboss.netty.channel.group.DefaultChannelGroup;
- import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
- public class Server {
- private ChannelFactory factory;
- public static ChannelGroup channelGroup = new DefaultChannelGroup();
- public void start(){
- // NioServerSocketChannelFactory用于创建基于NIO的服务端
- // ServerSocketChannel。本身包含2种线程,boss线程和worker线程。
- // 每个ServerSocketChannel会都会拥有自己的boss线程,
- // 当一个连接被服务端接受(accepted),
- // boss线程就会将接收到的Channel传递给一个worker线程处理,
- // 而worker线程以非阻塞的方式为一个或多个Channel提供非阻塞的读写
- factory = new NioServerSocketChannelFactory(
- Executors.newCachedThreadPool(), // boss线程池
- Executors.newCachedThreadPool(), // worker线程池
- 8); // worker线程数
- // ServerBootstrap用于帮助服务器启动
- ServerBootstrap bootstrap = new ServerBootstrap(factory);
- // 没有child.前缀,则该选项是为ServerSocketChannel设置
- bootstrap.setOption("reuseAddress", true);
- // 有child.前缀,则该选项是为Channel设置
- // bootstrap.setOption("child.tcpNoDelay", true);
- // bootstrap.setOption("child.keepAlive", true);
- // 对每一个连接(channel),server都会调用
- // ChannelPipelineFactory为该连接创建一个ChannelPipeline
- ServerChannelPiplineFactory channelPiplineFactory =
- new ServerChannelPiplineFactory();
- bootstrap.setPipelineFactory(channelPiplineFactory);
- // 这里绑定服务端监听的IP和端口
- Channel channel = bootstrap.bind(new InetSocketAddress("127.0.0.1", 8000));
- Server.channelGroup.add(channel);
- System.out.println("Server is started...");
- }
- public void stop(){
- // ChannelGroup为其管理的Channels提供一系列的批量操作
- // 关闭的Channel会自动从ChannelGroup中移除
- ChannelGroupFuture channelGroupFuture = Server.channelGroup.close();
- channelGroupFuture.awaitUninterruptibly();
- factory.releaseExternalResources();
- System.out.println("Server is stopped.");
- }
- public static void main(String[] args) throws Exception {
- Server server = new Server();
- server.start();
- Thread.sleep(30*1000);
- server.stop();
- }
- }
PipelineFactory:
- package com.sean.server;
- import java.util.concurrent.Executor;
- import org.jboss.netty.channel.ChannelPipeline;
- import org.jboss.netty.channel.ChannelPipelineFactory;
- import org.jboss.netty.channel.Channels;
- import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor;
- import com.sean.server.handler.ServerExecutionHandler;
- import com.sean.server.handler.ServerLogicHandler;
- import com.sean.server.handler.ServerReadDecoder;
- import com.sean.server.handler.ServerWriteEncoder;
- public class ServerChannelPiplineFactory implements ChannelPipelineFactory {
- @Override
- public ChannelPipeline getPipeline() throws Exception {
- ServerReadDecoder serverReadDecoder = new ServerReadDecoder();
- ServerWriteEncoder serverWriteEncoder = new ServerWriteEncoder();
- Executor executor =
- new OrderedMemoryAwareThreadPoolExecutor(4, 200, 200);
- ServerExecutionHandler serverExecutionHandler =
- new ServerExecutionHandler(executor);
- ServerLogicHandler serverLogicHandler = new ServerLogicHandler();
- // ChannelPipeline的源码中的javadoc介绍的非常详细,很有必要看一下
- // ChannelPipeline是一个处理ChannelEvent的handler链
- // 如果为读操作,ChannelEvent事件会从前到后依次被
- // Upstream的handler处理
- // serverReadDecoder -> serverLogicHandler
- // 如果为写操作,ChannelEvent事件会从后至前依次被
- // Downstream的handler处理
- // serverLogicHandler -> serverWriteEncoder
- ChannelPipeline channelPipeline = Channels.pipeline();
- channelPipeline.addLast("1", serverReadDecoder);
- channelPipeline.addLast("2", serverWriteEncoder);
- channelPipeline.addLast("3", serverExecutionHandler);
- channelPipeline.addLast("4", serverLogicHandler);
- System.out.println(channelPipeline.hashCode());
- return channelPipeline;
- }
- }
各个Handler:
- package com.sean.server.handler;
- import java.util.concurrent.Executor;
- import org.jboss.netty.handler.execution.ExecutionHandler;
- // 提供一个线程池
- public class ServerExecutionHandler extends ExecutionHandler{
- public ServerExecutionHandler(Executor executor) {
- super(executor);
- }
- }
- package com.sean.server.handler;
- import org.jboss.netty.channel.Channel;
- import org.jboss.netty.channel.ChannelFuture;
- import org.jboss.netty.channel.ChannelFutureListener;
- 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.SimpleChannelHandler;
- import com.sean.server.Server;
- // SimpleChannelHandler提供了很多基本的handler方法用来重写
- // 通常情况下足够使用了
- public class ServerLogicHandler extends SimpleChannelHandler {
- @Override
- public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
- throws Exception {
- System.out.println("######channelConnected");
- // channel group is thread safe
- Server.channelGroup.add(e.getChannel());
- System.out.println(e.getChannel().toString());
- }
- @Override
- public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
- System.out.println("######messageReceived");
- // 经过了ServerReadDecoder的处理,这里可以直接得到String类型的message
- String msg = (String)e.getMessage();
- System.out.println("The message sent by client is : " + msg);
- Channel ch = e.getChannel();
- String str = "Hi, Client.";
- // 由于IO操作是异步的,当方法返回时并不能保证IO操作一定完成了
- // 因此返回一个ChannelFuture对象实例
- // 该实例中保存了IO操作的状态信息
- ChannelFuture cf = ch.write(str);
- // 为ChannelFuture对象实例添加监听,如果数据发送完毕则关闭连接
- cf.addListener(new ChannelFutureListener(){
- @Override
- public void operationComplete(ChannelFuture future)
- throws Exception {
- Channel ch = future.getChannel();
- ch.close();
- }
- });
- System.out.println("The message has sent to client.");
- }
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
- e.getCause().printStackTrace();
- Channel ch = e.getChannel();
- ch.close();
- }
- }
- package com.sean.server.handler;
- import org.jboss.netty.buffer.ChannelBuffer;
- import org.jboss.netty.channel.Channel;
- import org.jboss.netty.channel.ChannelHandlerContext;
- import org.jboss.netty.handler.codec.string.StringDecoder;
- import org.jboss.netty.util.CharsetUtil;
- // 解决接收流数据时,数据出现碎片化的问题
- public class ServerReadDecoder extends StringDecoder{
- @Override
- protected Object decode(ChannelHandlerContext ctx, Channel channel,
- Object msg) throws Exception {
- System.out.println("######ServerReadDecoder");
- // 从msg中取出的数据类型是ChannelBuffer的
- byte[] buffer = ((ChannelBuffer)msg).array();
- byte last = buffer[buffer.length - 1];
- // 46 is '.'
- if(last == 46)
- // 并将ChannelBuffer转为String
- return new String(buffer, CharsetUtil.UTF_8);
- return null;
- }
- }
- package com.sean.server.handler;
- 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.string.StringEncoder;
- import org.jboss.netty.util.CharsetUtil;
- public class ServerWriteEncoder extends StringEncoder{
- @Override
- protected Object encode(ChannelHandlerContext ctx, Channel channel,
- Object msg) throws Exception {
- System.out.println("######ServerWriteEncoder");
- String str = (String)msg;
- // 通过ChannelBuffers工具,为指定编码的指定字符串分配缓存空间
- // 并将String转为ChannelBuffer
- ChannelBuffer channelBuffer =
- ChannelBuffers.copiedBuffer(str, CharsetUtil.UTF_8);
- return channelBuffer;
- }
- }
然后是Client部分的代码:
Client端主程序:
- package com.sean.client;
- import java.net.InetSocketAddress;
- import java.util.concurrent.Executors;
- import org.jboss.netty.bootstrap.ClientBootstrap;
- import org.jboss.netty.channel.ChannelFactory;
- import org.jboss.netty.channel.ChannelPipeline;
- import org.jboss.netty.channel.ChannelPipelineFactory;
- import org.jboss.netty.channel.Channels;
- import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
- import com.sean.client.handler.ClientLogicHandler;
- import com.sean.client.handler.ClientReadDecoder;
- import com.sean.client.handler.ClientWriteEncoder;
- public class Client {
- public static void main(String[] args){
- // 同服务端相同,只是这里使用的是NioClientSocketChannelFactory
- final ChannelFactory factory = new NioClientSocketChannelFactory(
- Executors.newCachedThreadPool(),
- Executors.newCachedThreadPool(),
- 8);
- // ClientBootstrap用于帮助客户端启动
- ClientBootstrap bootstrap = new ClientBootstrap(factory);
- // 由于客户端不包含ServerSocketChannel,所以参数名不能带有child.前缀
- bootstrap.setOption("tcpNoDelay", true);
- // bootstrap.setOption("keepAlive", true);
- bootstrap.setPipelineFactory(new ChannelPipelineFactory(){
- @Override
- public ChannelPipeline getPipeline() throws Exception {
- ChannelPipeline channelPipeline =
- Channels.pipeline(new ClientReadDecoder(),
- new ClientWriteEncoder(), new ClientLogicHandler());
- System.out.println(channelPipeline.hashCode());
- return channelPipeline;
- }
- });
- // 这里连接服务端绑定的IP和端口
- bootstrap.connect(new InetSocketAddress("127.0.0.1", 8000));
- System.out.println("Client is started...");
- }
- }
各个Handler:
- package com.sean.client.handler;
- import org.jboss.netty.channel.Channel;
- import org.jboss.netty.channel.ChannelFuture;
- import org.jboss.netty.channel.ChannelFutureListener;
- 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.SimpleChannelHandler;
- import org.jboss.netty.channel.WriteCompletionEvent;
- public class ClientLogicHandler extends SimpleChannelHandler {
- @Override
- public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
- throws Exception {
- System.out.println("######channelConnected");
- Channel ch = e.getChannel();
- String msg = "Hi, Server.";
- ch.write(msg);
- }
- @Override
- public void writeComplete(ChannelHandlerContext ctx, WriteCompletionEvent e)
- throws Exception {
- System.out.println("######writeComplete");
- }
- @Override
- public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
- System.out.println("######messageReceived");
- String msg = (String)e.getMessage();
- System.out.println("The message gotten from server is : " + msg);
- ChannelFuture channelFuture = e.getChannel().close();
- channelFuture.addListener(ChannelFutureListener.CLOSE);
- }
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
- e.getCause().printStackTrace();
- Channel ch = e.getChannel();
- ch.close();
- }
- }
- package com.sean.client.handler;
- import org.jboss.netty.buffer.ChannelBuffer;
- import org.jboss.netty.channel.Channel;
- import org.jboss.netty.channel.ChannelHandlerContext;
- import org.jboss.netty.handler.codec.string.StringDecoder;
- import org.jboss.netty.util.CharsetUtil;
- public class ClientReadDecoder extends StringDecoder{
- @Override
- protected Object decode(ChannelHandlerContext ctx, Channel channel,
- Object msg) throws Exception {
- System.out.println("######ClientReadDecoder");
- byte[] buffer = ((ChannelBuffer)msg).array();
- byte last = buffer[buffer.length - 1];
- // 46 is '.'
- if(last == 46)
- return new String(buffer, CharsetUtil.UTF_8);
- return null;
- }
- }
- package com.sean.client.handler;
- 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.string.StringEncoder;
- import org.jboss.netty.util.CharsetUtil;
- public class ClientWriteEncoder extends StringEncoder{
- @Override
- protected Object encode(ChannelHandlerContext ctx, Channel channel,
- Object msg) throws Exception {
- System.out.println("######ClientWriteEncoder");
- String str = (String)msg;
- ChannelBuffer channelBuffer =
- ChannelBuffers.copiedBuffer(str, CharsetUtil.UTF_8);
- return channelBuffer;
- }
- }
运行结果如下:
Server端后台日志:
- Server is started...
- 1257526899
- ######channelConnected
- [id: 0x88120865, /127.0.0.1:58887 => /127.0.0.1:8000]
- ######ServerReadDecoder
- ######messageReceived
- The message sent by client is : Hi, Server.
- ######ServerWriteEncoder
- The message has sent to client.
- Server is stopped.
Client端后台日志:
- 1767582956
- Client is started...
- ######channelConnected
- ######ClientWriteEncoder
- ######writeComplete
- ######ClientReadDecoder
- ######messageReceived
- The message gotten from server is : Hi, Client.