標籤:
一、伺服器端開發時序圖
圖片來源: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;//自訂的ChannelHandlerpublic 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
四、參考資料
1、Netty權威指南(李林峰)
Netty學習之伺服器端建立