標籤:netty clientbootstrap serverbootstrap channel
什麼是netty?下面是官方文檔的簡介:
The Netty project is an effort to providean asynchronous event-driven network application framework and tools for rapid development of maintainable high performance and high scalability protocol servers and clients. In other words, Netty isa NIO client server framework which enables quick and easy development of network applications such as protocol servers and clients. It greatly simplifies and streamlines network programming such as TCP/IP socket server.
下面寫一個簡單的執行個體:1.用戶端細節分析
ChannelFactory是建立一個通道(和一次具體的通訊實體關聯如網路通訊端)的主要介面,比如NioServerSocketChannelFactory 會建立一個Channel,有基於NIO的服務通訊端作為底層的通訊實體。一旦一個新的通道建立,那麼對應的ChannelPipeline就會開始處理相關的ChannelEvents。
NioClientSocketChannelFactory會建立一個用戶端的基於NIO的SocketChannel,利用非阻塞IO模型來高效處理這些並發的串連。其中有兩種類型的線程, boss thread 和 worker thread,每個NioClientSocketChannelFactory 有一個boss thread,它主要是有請求要發出時試圖進行一次串連,串連成功後,將這個串連的通道交付給一個worker thread,接下來這個worker thread 為一個或多個通道執行非阻塞的讀寫服務。
ClientBootstrap只是一個輔助函數,不會分配或者管理任何資源,管理資源是由構造器中指定的ChannelFactory完成的。所以從同一個ChannelFactory衍生出多個ClientBootstrap是可以的,從而為不同的Channel應用不同的設定。connect()方法會根據指定的SocketAddress試圖建立串連,如果本地地址沒有設定,就會自動分配,等價於:ClientBootstrap b = ....;b.connect(remoteAddress, b.getOption("localAddress"));
靜態方法 Channels.pipeline(ChannelHandler... handlers)用參數所指定的ChannelHandler 來建立一個新的ChannelPipeline,當然它們是有順序的,我們也可以自己一個一個的添加。
public
static ChannelPipeline pipeline(ChannelHandler... handlers) {
if (handlers ==
null) {
throw
new NullPointerException( "handlers"); }
ChannelPipeline newPipeline =
pipeline ();
for (
int i = 0; i < handlers. length; i ++) { ChannelHandler h = handlers[i];
if (h ==
null) {
break; } newPipeline.addLast(ConversionUtil.
toString(i), h); }
return newPipeline; }
2. 伺服器端細節分析
伺服器端構建的基本流程和用戶端類似,只是這裡的ChannelFactory,Bootstrap 都要滿足作為server的特性。NioServerSocketChannelFactory建立伺服器端的,基於NIO的ServerSocketChannel,仍然是非阻塞模式。每個綁定的ServerSocketChannel 有自身的boos thread,比如說開啟監聽了兩個連接埠 80,443,那麼就會有兩個boss thread,各自負責各自連接埠的串連請求,直到那個連接埠解除綁定定,然後將接受的串連請求交給worker thread去處理。
這裡是連線導向傳輸的ClientBootstrap 和 ServerBootstrap ,如果想用UDP的話就選 ConnectionlessBootstrap。
3. ChannelHandler的常見用法就會根據具體的事件類型做出具體的處理,牽扯到讀寫管道,而且有上下流的情況。
一個簡單的netty例子:TimeClientl.java
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;public class TimeClient {public static void main(String[] args) {String host = args[0];int port = Integer.parseInt(args[1]);ChannelFactory factory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool());ClientBootstrap bootstrap = new ClientBootstrap(factory);bootstrap.setPipelineFactory(new ChannelPipelineFactory() {@Overridepublic ChannelPipeline getPipeline() throws Exception {return Channels.pipeline(new TimeClientHandler2());}});bootstrap.connect(new InetSocketAddress(host, port)); //}}
TimeClientHandler.java
import java.util.Date;import org.jboss.netty.buffer.ChannelBuffer;import org.jboss.netty.channel.Channel;import org.jboss.netty.channel.ChannelHandlerContext;import org.jboss.netty.channel.ExceptionEvent;import org.jboss.netty.channel.MessageEvent;import org.jboss.netty.channel.SimpleChannelHandler;public class TimeClientHandler extends SimpleChannelHandler{@Overridepublic void messageReceived(ChannelHandlerContext ctx, MessageEvent e)throws Exception {ChannelBuffer buffer = (ChannelBuffer)e.getMessage();long currentTimeMills = buffer.readInt() * 1000L;System.out.println(new Date(currentTimeMills));e.getChannel().close();}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)throws Exception {e.getCause().printStackTrace();Channel c = e.getChannel();c.close();}}
TimeServer.java
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.ChannelPipeline;import org.jboss.netty.channel.ChannelPipelineFactory;import org.jboss.netty.channel.Channels;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 TimeServer {public static ChannelGroup allChannels = new DefaultChannelGroup("time-server");public static void main(String[] args) {ChannelFactory factory = new NioServerSocketChannelFactory(Executors.newCachedThreadPool(),Executors.newCachedThreadPool());ServerBootstrap bootstrap = new ServerBootstrap(factory);bootstrap.setPipelineFactory(new ChannelPipelineFactory() {@Overridepublic ChannelPipeline getPipeline() throws Exception {return Channels.pipeline(new TimeServerHandler2(), new TimeEncoder());}});bootstrap.setOption("reuseAddr", true);bootstrap.setOption("child.tcpNoDelay", true);bootstrap.setOption("child.keepAlive", true);Channel channel = bootstrap.bind(new InetSocketAddress(8080));allChannels.add(channel);//waitForShutdownCommand(); this is a imaginary logic:for instance //when there is accepted connection we close this server ;if(allChannels.size() >=2){ChannelGroupFuture f = allChannels.close();f.awaitUninterruptibly();factory.releaseExternalResources();}}}
TimeServerHandler.java
import org.jboss.netty.buffer.ChannelBuffer;import org.jboss.netty.buffer.ChannelBuffers;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.SimpleChannelHandler;public class TimeServerHandler extends SimpleChannelHandler{@Overridepublic void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)throws Exception {Channel ch = e.getChannel();ChannelBuffer time = ChannelBuffers.buffer(4); //sizeof inttime.writeInt((int)(System.currentTimeMillis()/1000L + 2208988800L));ChannelFuture cf = ch.write(time);cf.addListener(new ChannelFutureListener() {@Overridepublic void operationComplete(ChannelFuture future) throws Exception {Channel ch = future.getChannel();ch.close();}});}@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)throws Exception {e.getCause().printStackTrace();Channel c = e.getChannel();c.close();}}