Netty學習之服務器端創建


一、服務器端開發時序圖

  

  圖片來源: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

  

四、參考資料

  1、Netty權威指南(李林峰)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM