Netty學習之hello world

(一) 說明:

​ 這是一個使用netty框架的demo,客戶端發送一個消息到服務端,服務端將消息原樣發送到客戶端。java

(二) 服務端:

(1) netty服務器端的ChannelHandler業務核心處理邏輯

​ 服務器端的處理器Handler命名爲HelloWorldChannelHandler,繼承自ChannelInboundHandlerAdapter適配器,有以下重載方法:算法

輸入圖片說明

咱們只實現它的讀取方法channelRead和異常處理方法exceptionCaught。服務器

/**
 * 業務核心處理邏輯
 * @author comeCY
 *
 */
public class HelloWorldServerHandler extends ChannelInboundHandlerAdapter{  
    
    /**
     * 讀取的方法
     * 覆蓋channelRead方法處理全部接收到的數據
     */
    @Override  
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {  
        System.out.println("server channelRead..");  
        //服務端讀取
        System.out.println(ctx.channel().remoteAddress()+"->Server :"+ msg.toString());  
        //服務端轉發
        ctx.write("server write :"+msg);  
        ctx.flush();  //沖刷
    }  
     
    /**
     * 異常處理
     */
    @Override  
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {  
        cause.printStackTrace();  //打印異常堆棧跟蹤
        ctx.close();  //關閉通道
    }  
}

(2) 引導服務器

  • 建立 ServerBootstrap 實例來引導服務器並隨後綁定
  • 建立並分配一個 NioEventLoopGroup 實例來處理事件的處理,如接受新的鏈接和讀/寫數據。
  • 指定本地 InetSocketAddress 給服務器綁定
  • 經過 EchoServerHandler 實例給每個新的 Channel 初始化
  • 最後調用 ServerBootstrap.bind() 綁定服務器
public class HelloWorldServer {  
	  
    private int port;  
      
    public HelloWorldServer(int port) {  
        this.port = port;  
    }  
      
    public void start() throws Exception{  
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);  //建立EventLoopGroup
        EventLoopGroup workerGroup = new NioEventLoopGroup();  
        try {  
            ServerBootstrap sbs = new ServerBootstrap()  
              	.group(bossGroup,workerGroup)  //建立ServerBootstrap
                .channel(NioServerSocketChannel.class)  //指定使用 NIO 的傳輸 Channel
                .localAddress(new InetSocketAddress(port))  //設置 socket 地址使用所選的端口
                .childHandler(new ChannelInitializer<SocketChannel>() {  
                          //添加 到 Channel 的 ChannelPipeline
                        protected void initChannel(SocketChannel ch) throws Exception {  
                          //添加字符串編碼和解碼器
                            ch.pipeline().addLast("decoder", new StringDecoder());  
                            ch.pipeline().addLast("encoder", new StringEncoder());  
                          //添加 HelloWorldSrverHandler
                            ch.pipeline().addLast(new HelloWorldServerHandler());  
                        };  
                          
                    }).option(ChannelOption.SO_BACKLOG, 128)     
                    .childOption(ChannelOption.SO_KEEPALIVE, true);  //啓用心跳保活機制
             // 綁定端口,開始接收進來的鏈接  
             ChannelFuture future = sbs.bind(port).sync();    
               
             System.out.println("Server start listen at " + port );  
             future.channel().closeFuture().sync();  //關閉 channel 和 塊,直到它被關閉
        } finally {
        	bossGroup.shutdownGracefully();  //關閉 EventLoopGroup,釋放全部資源
            workerGroup.shutdownGracefully();
        }  
    }  
    
    
    public static void main(String[] args) throws Exception {  
        int port;  
        if (args.length > 0) {  
            port = Integer.parseInt(args[0]);  
        } else {  
            port = 8080;  
        }  
        new HelloWorldServer(port).start();  //呼叫服務器的start方法
    }  
}

(三) 客戶端:

(1) 客戶端Handler

HelloWorldClientHandler也是繼承自ChannelInboundHandlerAdapter,實現方法channelActive,channelRead,exceptionCaught與服務端相似。框架

public class HelloWorldClientHandler extends ChannelInboundHandlerAdapter{  
    
    @Override  
    public void channelActive(ChannelHandlerContext ctx) {  
      //當被通知該 channel 是活動的時候打印
        System.out.println("HelloWorldClientHandler Active");  
    }  
  
    @Override  
    public void channelRead(ChannelHandlerContext ctx, Object msg) {  
      //記錄接收到的消息
       System.out.println("HelloWorldClientHandler read Message:"+msg);  
    }  
  
   @Override  
   public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {  
       cause.printStackTrace();  //記錄日誌錯誤並關閉 channel
       ctx.close();  
    }  
}

(2) 引導客戶端

  • Bootstrap 被建立來初始化客戶端
  • NioEventLoopGroup 實例被分配給處理該事件的處理,這包括建立新的鏈接和處理 入站和出站數據
  • InetSocketAddress 爲鏈接到服務器而建立
  • HelloWorldClientHandler 將被安裝在 pipeline 當鏈接完成時
  • Bootstrap.connect()被調用鏈接到遠程的服務器
public class HelloWorldClient {  
	
	static final String HOST = System.getProperty("host", "127.0.0.1");
	static final int PORT = Integer.parseInt(System.getProperty("port", "8080"));
  
	public static void main(String[] args) throws Exception {
		EventLoopGroup group = new NioEventLoopGroup();
		
		try {
			Bootstrap b = new Bootstrap();  //建立 Bootstrap
          /* 指定 EventLoopGroup 來處理客戶端事件。因爲咱們使用 NIO 傳輸,因此用到了
NioEventLoopGroup 的實現*/
			b.group(group)  
			    .channel(NioSocketChannel.class)  //使用的 channel 類型是一個用於 NIO 傳輸
			    .option(ChannelOption.TCP_NODELAY, true)  //關閉Nagle算法,實時發送數據
			    .handler(new ChannelInitializer<SocketChannel>() {

				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					// TODO Auto-generated method stub
					ChannelPipeline p = ch.pipeline();
					p.addLast("decoder", new StringDecoder());  
                    p.addLast("encoder", new StringEncoder());
					p.addLast(new HelloWorldClientHandler());
				}
			});
			
			ChannelFuture future = b.connect(HOST, PORT).sync();  //鏈接到遠程;等待鏈接完成
			//客戶端寫入
			future.channel().writeAndFlush("Hello Netty Server, I am a common client...");
			future.channel().closeFuture().sync();  //阻塞直到 Channel 關閉
		} finally {
			group.shutdownGracefully();  //調用 shutdownGracefully() 來關閉線程池和釋放全部資源
		}
	}
}

(四) 運行效果:

(1) 服務端

輸入圖片說明

(2) 客戶端

輸入圖片說明

(五) 結語

學習博客:socket

參考書籍:ide

  • netty in action
相關文章
相關標籤/搜索