Netty學習之服務器端建立

1、服務器端開發時序圖

  

  圖片來源:Netty權威指南(第2版)bootstrap

2、Netty服務器端開發步驟

  使用Netty進行服務器端開發主要有如下幾個步驟:數組

  一、建立ServerBootstrap實例

ServerBootstrap b=new ServerBootstrap();

  ServerBootstrap是Netty服務器端的啓動輔助類,提供了一系列的方法用於設置服務器端啓動相關的參數。安全

  二、設置並綁定Reactor線程池

EventLoopGroup bossGruop=new NioEventLoopGroup();//用於服務器端接受客戶端的鏈接
EventLoopGroup workGroup=new NioEventLoopGroup();//用於網絡事件的處理

  Netty的線程池是EventLoopGroup,它其實是EventLoop的數組,EventLoop職責是處理全部註冊到本線程多路複用器Selector上的Channel,Selector的輪詢操做是由綁定的EventLoop線程run方法驅動。服務器

  三、設置並綁定服務器端Channel

b.group(bossGruop, workGroup).channel(NioServerSocketChannel.class)

  Netty對原生的NIO類庫進行封裝,做爲NIO服務端,須要建立ServerSocketChannel,對應的實現是NioServerSocketChannel。網絡

  四、鏈路創建的時候建立並初始化ChannelPipeline

b.group(bossGruop, workGroup).channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>()

  ChannelPipeline的本質是一個負責處理網絡事件的職責鏈,負責管理和執行ChannelHandler。網絡事件以事件流的形式在ChannelPipeline中流轉,由ChannelPipeline根據Channel|Handler的執行策略調度ChannelHandler的執行。典型的網絡事件有:socket

  • 鏈路註冊
  • 鏈路激活
  • 鏈路斷開
  • 接收到請求信息
  • 請求信息接收並處理完畢
  • 發送應答消息
  • 鏈路發生異常
  • 用戶自定義事件

  五、添加並設置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認證ide

  六、綁定並啓動監聽窗口

ChannelFuture f=b.bind(port).sync();

  通過一系列初始化和檢測工做後,會啓動監聽端口,並將ServerSocketChannel註冊到Selector上監聽客戶端鏈接oop

  七、Selector輪詢

  由Reactor線程NioEventLoop負責調度和執行Selector輪詢操做,選擇準備就緒的Channel集合this

  八、當輪詢到準備就緒的Channel以後,就由Reactor線程NioEventLoop執行ChannelPipeline的相應方法,最終調度並執行ChannelHandler

public class HelloServerHandler extends ChannelHandlerAdapter

3、Netty服務器開發示例代碼

   需求:服務器端實現,每鏈接一個客戶端,在服務器控制檯打印客戶端輸入的字符。(注:本代碼使用的netty是netty-all-5.0.0.Alpha1-sources.jar版本)url

  服務器端代碼以下:

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

  

4、參考資料

  一、Netty權威指南(李林峯)

相關文章
相關標籤/搜索