一、服務器端開發時序圖
圖片來源:Netty權威指南(第2版)
二、Netty服務器端開發步驟
使用Netty進行服務器端開發主要有以下幾個步驟:
1、創建ServerBootstrap實例
ServerBootstrap b=new ServerBootstrap();
ServerBootstrap是Netty服務器端的啟動輔助類,提供了一系列的方法用於設置服務器端啟動相關的參數。
2、設置並綁定Reactor線程池
EventLoopGroup bossGruop=new NioEventLoopGroup();//用於服務器端接受客戶端的連接 EventLoopGroup workGroup=new NioEventLoopGroup();//用於網絡事件的處理
Netty的線程池是EventLoopGroup,它實際上是EventLoop的數組,EventLoop職責是處理所有注冊到本線程多路復用器Selector上的Channel,Selector的輪詢操作是由綁定的EventLoop線程run方法驅動。
3、設置並綁定服務器端Channel
b.group(bossGruop, workGroup).channel(NioServerSocketChannel.class)
Netty對原生的NIO類庫進行封裝,作為NIO服務端,需要創建ServerSocketChannel,對應的實現是NioServerSocketChannel。
4、鏈路建立的時候創建並初始化ChannelPipeline
b.group(bossGruop, workGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>()
ChannelPipeline的本質是一個負責處理網絡事件的職責鏈,負責管理和執行ChannelHandler。網絡事件以事件流的形式在ChannelPipeline中流轉,由ChannelPipeline根據Channel|Handler的執行策略調度ChannelHandler的執行。典型的網絡事件有:
- 鏈路注冊
- 鏈路激活
- 鏈路斷開
- 接收到請求信息
- 請求信息接收並處理完畢
- 發送應答消息
- 鏈路發生異常
- 用戶自定義事件
5、添加並設置ChannelHandler
b.group(bossGruop, workGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel arg0) throws Exception { arg0.pipeline().addLast(new HelloServerHandler()); } }).option(ChannelOption.SO_BACKLOG, 1024);
ChannelHandler是Netty提供給用戶定制和擴展的接口,例如消息編解碼、心跳、安全認證、TSL/SSL認證
6、綁定並啟動監聽窗口
ChannelFuture f=b.bind(port).sync();
經過一系列初始化和檢測工作后,會啟動監聽端口,並將ServerSocketChannel注冊到Selector上監聽客戶端連接
7、Selector輪詢
由Reactor線程NioEventLoop負責調度和執行Selector輪詢操作,選擇准備就緒的Channel集合
8、當輪詢到准備就緒的Channel之后,就由Reactor線程NioEventLoop執行ChannelPipeline的相應方法,最終調度並執行ChannelHandler
public class HelloServerHandler extends ChannelHandlerAdapter
三、Netty服務器開發示例代碼
需求:服務器端實現,每連接一個客戶端,在服務器控制台打印客戶端輸入的字符。(注:本代碼使用的netty是netty-all-5.0.0.Alpha1-sources.jar版本)
服務器端代碼如下:
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; //Netty服務器端 public class HelloServer { private int port; public HelloServer(int port) { super(); this.port = port; } private void bind() throws InterruptedException { EventLoopGroup bossGruop=new NioEventLoopGroup();//用於服務器端接受客戶端的連接 EventLoopGroup workGroup=new NioEventLoopGroup();//用於網絡事件的處理 try { ServerBootstrap b=new ServerBootstrap(); b.group(bossGruop, workGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel arg0) throws Exception { arg0.pipeline().addLast(new HelloServerHandler()); } }).option(ChannelOption.SO_BACKLOG, 1024);//指定此套接口排隊的最大連接個數 ChannelFuture f=b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { bossGruop.shutdownGracefully(); workGroup.shutdownGracefully(); } } public static void main(String[] args) throws InterruptedException { new HelloServer(8080).bind(); } }
import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; //自定義的ChannelHandler public class HelloServerHandler extends ChannelHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("客戶端連上了..."); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { ByteBuf buf=(ByteBuf) msg; byte[] req=new byte[buf.readableBytes()]; buf.readBytes(req); System.out.println("服務器端接收的消息:"+new String(req)); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); } }
客戶端:使用telnet模擬客戶端輸入,
按住“ctrl+]”,然后輸入指令send a
四、參考資料