Netty實現Socket


Netty實現Socket

        從Java1.4開始, Java引入了non-blocking IO,簡稱NIO。NIO與傳統socket最大的不同就是引入了Channel和多路復用selector的概念。傳統的socket是基於stream的,它是單向的,有InputStream表示read和OutputStream表示寫。而Channel是雙工的,既支持讀也支持寫,channel的讀/寫都是面向Buffer。 NIO中引入的多路復用Selector機制(如果是linux系統,則應用的epoll事件通知機制)可使一個線程同時監聽多個Channel上發生的事件。 雖然Java NIO相比於以往確實是一個大的突破,但是如果要真正上手進行開發,且想要開發出好的一個服務端網絡程序,那么你得要花費一點功夫了,畢竟Java NIO只是提供了一大堆的API而已,對於一般的軟件開發人員來說只能呵呵了。因此,社區中就涌現了很多基於Java NIO的網絡應用框架,其中以Apache的Mina,以及Netty最為出名。

一、Netty實現Socket
1、Netty服務端示例:

EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
    ServerBootstrap b = new ServerBootstrap(); // (2)
    b.group(bossGroup, workerGroup)  // (3)
     .channel(NioServerSocketChannel.class) // (4)
     .handler(new LoggingHandler())    // (5)
     .childHandler(new ChannelInitializer<SocketChannel>() { // (6)
         @Override
         public void initChannel(SocketChannel ch) throws Exception {
             ch.pipeline().addLast(new DiscardServerHandler());
         }
     })
     .option(ChannelOption.SO_BACKLOG, 128)          // (7)
     .childOption(ChannelOption.SO_KEEPALIVE, true); // (8)
    
     // Bind and start to accept incoming connections.
     ChannelFuture f = b.bind(port).sync(); // (9)
    
     // 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.
     f.channel().closeFuture().sync();
} finally {
    workerGroup.shutdownGracefully();
    bossGroup.shutdownGracefully();
}

上面這段代碼展示了服務端的一個基本步驟:
(1)、初始化用於Acceptor的主"線程池"以及用於I/O工作的從"線程池";
(2)、初始化ServerBootstrap實例, 此實例是netty服務端應用開發的入口;
(3)、通過ServerBootstrap的group方法,設置(1)中初始化的主從"線程池";
(4)、指定通道channel的類型,由於是服務端,故而是NioServerSocketChannel;
(5)、設置ServerSocketChannel的處理器(此處不詳述,后面的系列會進行深入分析)
(6)、設置子通道也就是SocketChannel的處理器, 其內部是實際業務開發的"主戰場"
(7)、配置ServerSocketChannel的選項
(8)、配置子通道也就是SocketChannel的選項
(9)、綁定並偵聽某個端口

2、Netty客戶示例:

public class TimeClient {
    public static void main(String[] args) throws Exception {
        String host = args[0];
        int port = Integer.parseInt(args[1]);
        EventLoopGroup workerGroup = new NioEventLoopGroup(); // (1)
        
        try {
            Bootstrap b = new Bootstrap(); // (2)
            b.group(workerGroup); // (3)
            b.channel(NioSocketChannel.class); // (4)
            b.option(ChannelOption.SO_KEEPALIVE, true); // (5)
            b.handler(new ChannelInitializer<SocketChannel>() { // (6)
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast(new TimeClientHandler());
                }
            });
            
            // Start the client.
            ChannelFuture f = b.connect(host, port).sync(); // (7)

            // Wait until the connection is closed.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }
}

客戶端的開發步驟和服務端都差不多:
(1)、初始化用於連接及I/O工作的"線程池";
(2)、初始化Bootstrap實例, 此實例是netty客戶端應用開發的入口;
(3)、通過Bootstrap的group方法,設置(1)中初始化的"線程池";
(4)、指定通道channel的類型,由於是客戶端,故而是NioSocketChannel;
(5)、設置SocketChannel的選項(此處不詳述,后面的系列會進行深入分析);
(6)、設置SocketChannel的處理器, 其內部是實際業務開發的"主戰場";
(7)、連接指定的服務地址;

二、Netty實現SSLSocket
netty創建服務端時,在初始化channl時,把handler加入ChannelPipeline時,在最開始那個handler設為SslHandler:

SSLContext sslContext = SslUtil.createSSLContext(type ,path ,password); ///SslUtil自定義類
SSLEngine sslEngine = sslContext.createSSLEngine(); sslEngine.setUseClientMode(false); /// 是否使用客戶端模式 sslEngine.setNeedClientAuth(false); ////是否需要驗證客戶端
pipeline.addLast("ssl", new SslHandler(sslEngine));

SslUtil類:

private static volatile SSLContext sslContext = null;

public static SSLContext createSSLContext(String type ,String path ,String password) throws Exception {
  if(null == sslContext){
     synchronized (SslUtil.class) {
      if(null == sslContext){

        KeyStore ks = KeyStore.getInstance(type); /// "JKS"          
        InputStream ksInputStream = new FileInputStream(path); /// 證書存放地址
          ks.load(ksInputStream, password.toCharArray());
          KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
          kmf.init(ks, password.toCharArray());
          SSLContext sslContext = SSLContext.getInstance("TLS");
          sslContext.init(kmf.getKeyManagers(), null, null);
      }
    }
  }
    return sslContext;
}




參考博客:
[1]Netty4.x 源碼實戰系列(一): 深入理解ServerBootstrap 與 Bootstrap (1)
[2]Netty 實現SSL安全連接(wss://)
[3]Netty筆記之三:Netty實現Socket編程
[4]Netty框架學習之(一):Netty框架簡介
[5]深入理解Netty框架


免責聲明!

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



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