1. 程式人生 > >Netty入門官方例子

Netty入門官方例子

學習分散式,正好看到Netty 是一個基於NIO的客戶、伺服器端程式設計框架,所以本著學習的態度去官網看了一下,官網例子,本著以後可以翻出來再看看的心態,把官網的第一個例子貼出來,也希望自己以後有一個可以複習的地方,第一次使用部落格功能,還有很多不懂的地方

一.jar包
<!-- Netty開始 -->
<!-- https://mvnrepository.com/artifact/io.netty/netty-all -->
<dependency>
   <groupId>io.netty</groupId>
   <artifactId>netty-all</artifactId>
   <version>4.1.6.Final</version>
</dependency>
<!-- Netty結束 -->
二.DEMO
官方並沒有使用Hello World來作為一個例子,而是採用RFC的DISCARD,這個協議定義了就是接收到請求後什麼也不幹。

第一步編寫DiscardServerHandler類:

package io.netty.example.discard;
 
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
//ChannelInboundHandlerAdapter實現自ChannelInboundHandler
//ChannelInboundHandler提供了不同的事件處理方法你可以重寫
public class DiscardServerHandler extends ChannelInboundHandlerAdapter {
    /* 
     * @作者:CJY
     * @說明:該方法用於接收從客戶端接收的資訊
     * @時間:2017-4-2下午12:25:05
     * @see io.netty.channel.ChannelInboundHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object)
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        //Discard the received data silently
        //ByteBuf是一個引用計數物件實現ReferenceCounted,他就是在有物件引用的時候計數+1,無的時候計數-1,當為0物件釋放記憶體
        ByteBuf in=(ByteBuf)msg;
        try {
            while(in.isReadable()){
                System.out.println((char)in.readByte());
                System.out.flush();
            }
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }
    
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}


第二步編寫DiscardServer:

package io.netty.example.discard;
 
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;
 
public class DiscardServer {
    private int port;
    public DiscardServer(int port){
        this.port = port;
    }
    
    public void run() throws Exception{
        //Group:群組,Loop:迴圈,Event:事件,這幾個東西聯在一起,相比大家也大概明白它的用途了。
        //Netty內部都是通過執行緒在處理各種資料,EventLoopGroup就是用來管理排程他們的,註冊Channel,管理他們的生命週期。
        //NioEventLoopGroup是一個處理I/O操作的多執行緒事件迴圈
        //bossGroup作為boss,接收傳入連線
        //因為bossGroup僅接收客戶端連線,不做複雜的邏輯處理,為了儘可能減少資源的佔用,取值越小越好
        EventLoopGroup bossGroup=new NioEventLoopGroup(1);
        //workerGroup作為worker,處理boss接收的連線的流量和將接收的連線註冊進入這個worker
        EventLoopGroup workerGroup=new NioEventLoopGroup();
        try {
            //ServerBootstrap負責建立服務端
            //你可以直接使用Channel去建立服務端,但是大多數情況下你無需做這種乏味的事情
            ServerBootstrap b=new ServerBootstrap();
            b.group(bossGroup, workerGroup)
            //指定使用NioServerSocketChannel產生一個Channel用來接收連線
            .channel(NioServerSocketChannel.class)
            //ChannelInitializer用於配置一個新的Channel
            //用於向你的Channel當中新增ChannelInboundHandler的實現
            .childHandler(new ChannelInitializer<SocketChannel>() {
                public void initChannel(SocketChannel ch) throws Exception {
                    //ChannelPipeline用於存放管理ChannelHandel
                    //ChannelHandler用於處理請求響應的業務邏輯相關程式碼
                    ch.pipeline().addLast(new DiscardServerHandler());
                };
            })
            //對Channel進行一些配置
            //注意以下是socket的標準引數
            //BACKLOG用於構造服務端套接字ServerSocket物件,標識當伺服器請求處理執行緒全滿時,用於臨時存放已完成三次握手的請求的佇列的最大長度。如果未設定或所設定的值小於1,Java將使用預設值50。
            //Option是為了NioServerSocketChannel設定的,用來接收傳入連線的
            .option(ChannelOption.SO_BACKLOG, 128)
            //是否啟用心跳保活機制。在雙方TCP套接字建立連線後(即都進入ESTABLISHED狀態)並且在兩個小時左右上層沒有任何資料傳輸的情況下,這套機制才會被啟用。
            //childOption是用來給父級ServerChannel之下的Channels設定引數的
            .childOption(ChannelOption.SO_KEEPALIVE, true);
            // Bind and start to accept incoming connections.
            ChannelFuture f=b.bind(port).sync();
            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            //sync()會同步等待連線操作結果,使用者執行緒將在此wait(),直到連線操作完成之後,執行緒被notify(),使用者程式碼繼續執行
            //closeFuture()當Channel關閉時返回一個ChannelFuture,用於鏈路檢測
            f.channel().closeFuture().sync();
        }finally{
            //資源優雅釋放
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
    
    public static void main(String[] args) {
        int port=8088;
        try {
            new DiscardServer(port).run();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
裡面的具體的類在註釋當中進行了說明這裡就不在一個一個羅列了,目前也是在初步學習階段,所以如果有不對的也希望大神指正。
三.測試
開啟Window的命令列,輸入telnet命令:telnet localhost 8088,如果能夠正確連線就代表成功,在新開啟的命令視窗隨意輸入字元,如果在myeclipse當中能夠正確輸出在console當中,就代表程式正常。

四.ECHO協議的DEMO
ECHO協議,定義了客戶端請求啥就返回啥

第一步編寫EchoServerHandler:

package io.netty.example.echo;
 
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
 
/**
 * @作者:CJY
 * @說明:這個是用來實現ECHO協議,這個協議的作用就是將客戶端輸入的資訊全部返回
 * @時間:2017-4-8下午12:07:50
 */
public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    /* 
     * @作者:CJY
     * @說明:該方法用於接收從客戶端接收的資訊
     * @時間:2017-4-8下午12:08:51
     * @see io.netty.channel.ChannelInboundHandlerAdapter#channelRead(io.netty.channel.ChannelHandlerContext, java.lang.Object)
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        //ChannelHandlerContext提供各種不同的操作用於觸發不同的I/O時間和操作
        //呼叫write方法來逐字返回接收到的資訊
        //這裡我們不需要在DISCARD例子當中那樣呼叫釋放,因為Netty會在寫的時候自動釋放
        //只調用write是不會釋放的,它會快取,直到呼叫flush
        ctx.write(msg);
        ctx.flush();
        //你可以直接使用writeAndFlush(msg)
        //ctx.writeAndFlush(msg);
    }
    
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

第二步編寫EchoServer:
package io.netty.example.echo;
 
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;
 
public class EchoServer {
    private int port;
    public EchoServer(int port){
        this.port = port;
    }
    
    public void run() throws Exception{
        //NioEventLoopGroup是一個處理I/O操作的多執行緒事件迴圈
        //bossGroup作為boss,接收傳入連線
        //bossGroup只負責接收客戶端的連線,不做複雜操作,為了減少資源佔用,取值越小越好
        //Group:群組,Loop:迴圈,Event:事件,這幾個東西聯在一起,相比大家也大概明白它的用途了。
        //Netty內部都是通過執行緒在處理各種資料,EventLoopGroup就是用來管理排程他們的,註冊Channel,管理他們的生命週期。
        EventLoopGroup bossGroup=new NioEventLoopGroup(1);
        //workerGroup作為worker,處理boss接收的連線的流量和將接收的連線註冊進入這個worker
        EventLoopGroup workerGroup=new NioEventLoopGroup();
        try {
            //ServerBootstrap負責建立服務端
            //你可以直接使用Channel去建立服務端,但是大多數情況下你無需做這種乏味的事情
            ServerBootstrap b=new ServerBootstrap();
            b.group(bossGroup, workerGroup)
            //指定使用NioServerSocketChannel產生一個Channel用來接收連線
            .channel(NioServerSocketChannel.class)
            //ChannelInitializer用於配置一個新的Channel
            //用於向你的Channel當中新增ChannelInboundHandler的實現
            .childHandler(new ChannelInitializer<SocketChannel>() {
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new EchoServerHandler());
                };
            })
            //對Channel進行一些配置
            //注意以下是socket的標準引數
            //BACKLOG用於構造服務端套接字ServerSocket物件,標識當伺服器請求處理執行緒全滿時,用於臨時存放已完成三次握手的請求的佇列的最大長度。如果未設定或所設定的值小於1,Java將使用預設值50。
            //Option是為了NioServerSocketChannel設定的,用來接收傳入連線的
            .option(ChannelOption.SO_BACKLOG, 128)
            //是否啟用心跳保活機制。在雙方TCP套接字建立連線後(即都進入ESTABLISHED狀態)並且在兩個小時左右上層沒有任何資料傳輸的情況下,這套機制才會被啟用。
            //childOption是用來給父級ServerChannel之下的Channels設定引數的
            .childOption(ChannelOption.SO_KEEPALIVE, true);
            // Bind and start to accept incoming connections.
            ChannelFuture f=b.bind(port).sync();
            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            //sync()會同步等待連線操作結果,使用者執行緒將在此wait(),直到連線操作完成之後,執行緒被notify(),使用者程式碼繼續執行
            //closeFuture()當Channel關閉時返回一個ChannelFuture,用於鏈路檢測
            f.channel().closeFuture().sync();
        }finally{
            //資源優雅釋放
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
    
    public static void main(String[] args) {
        int port=8088;
        try {
            new EchoServer(port).run();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}


測試如上
--------------------- 
作者:wocjy 
來源:CSDN 
原文:https://blog.csdn.net/wocjy/article/details/78661464 
版權宣告:本文為博主原創文章,轉載請附上博文連結!