Netty自學-Netty學習(一)

什麼Netty?

Netty是由JBOSS提供的一個java開源框架。Netty提供異步的、事件驅動的網絡應用程序框架和工具,用以快速開發高性能、高可靠性的網絡服務器和客戶端程序。java

也就是說,Netty 是一個基於NIO的客戶、服務器端編程框架,使用Netty 能夠確保你快速和簡單的開發出一個網絡應用,例如實現了某種協議的客戶,服務端應用。Netty至關簡化和流線化了網絡應用的編程開發過程,例如,TCP和UDP的socket服務開發。編程

咱們下面編寫四個類bootstrap

1.用於接收數據的服務器端Socket服務器

2.用於接收客戶端的消息,用於接收和反饋客戶端發出的消息類ServertHandler網絡

3.用於發送數據的服務器端Client框架

4.用於發送數據和接收服務器端發出的數據處理類ClientHandler異步

Socket.javasocket

import io.netty.bootstrap.ServerBootstrap;  
import io.netty.buffer.ByteBuf;  
import io.netty.buffer.Unpooled;  
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;  
import io.netty.handler.codec.DelimiterBasedFrameDecoder;  
import io.netty.handler.codec.string.StringDecoder;  
  
public class Server {  
      
    public static void main(String[] args) throws InterruptedException {  
        //1.第一個線程組是用於接收Client端鏈接的  
        EventLoopGroup bossGroup = new NioEventLoopGroup();   
        //2.第二個線程組是用於實際的業務處理的  
        EventLoopGroup workerGroup = new NioEventLoopGroup();  
        ServerBootstrap b = new ServerBootstrap();  
        b.group(bossGroup, workerGroup);//綁定兩個線程池  
        b.channel(NioServerSocketChannel.class);//指定NIO的模式,若是是客戶端就是NioSocketChannel  
        b.option(ChannelOption.SO_BACKLOG, 1024);//TCP的緩衝區設置  
        b.option(ChannelOption.SO_SNDBUF, 32*1024);//設置發送緩衝的大小  
        b.option(ChannelOption.SO_RCVBUF, 32*1024);//設置接收緩衝區大小  
        b.option(ChannelOption.SO_KEEPALIVE, true);//保持連續  
        b.childHandler(new ChannelInitializer<SocketChannel>() {  
            @Override  
            protected void initChannel(SocketChannel sc) throws Exception {  
                ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());//拆包粘包定義結束字符串(第一種解決方案)  
                sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));//在管道中加入結束字符串  
            //  sc.pipeline().addLast(new FixedLengthFrameDecoder(200));第二種定長  
                sc.pipeline().addLast(new StringDecoder());//定義接收類型爲字符串把ByteBuf轉成String  
                sc.pipeline().addLast(new ServertHandler());//在這裏配置具體數據接收方法的處理  
            }  
        });  
        ChannelFuture future = b.bind(8765).sync();//綁定端口  
        future.channel().closeFuture().sync();//等待關閉(程序阻塞在這裏等待客戶端請求)  
        bossGroup.shutdownGracefully();//關閉線程  
        workerGroup.shutdownGracefully();//關閉線程  
    }  
}  

1.在上面這個Server.java中,咱們都要定義兩個線程池,boss和worker,boss是用於管理鏈接到server端的client的鏈接數的線程池,而woeker是用於管理實際操做的線程池。ide

2.ServerBootstrap用一個ServerSocketChannelFactory 來實例化。ServerSocketChannelFactory 有兩種選擇,一種是NioServerSocketChannelFactory,一種是OioServerSocketChannelFactory。 前者使用NIO,後則使用普通的阻塞式IO。它們都須要兩個線程池實例做爲參數來初始化,一個是boss線程池,一個是worker線程池。工具

3.而後使ServerBookstrap管理boss和worker線程池。而且設置各個緩衝區的大小。

4.這裏的事件處理類常常會被用來處理一個最近的已經接收的Channel。ChannelInitializer是一個特殊的處理類,他的目的是幫助使用者配置一個新的Channel。也許你想經過增長一些處理類好比NettyServerHandler來配置一個新的Channel 或者其對應的ChannelPipeline來實現你的網絡程序。 當你的程序變的複雜時,可能你會增長更多的處理類到pipline上,而後提取這些匿名類到最頂層的類上。

5.在使用原始的encoder、decoder的狀況下,Netty發送接收數據都是按照ByteBuf的形式,其它形式都是不合法的。 而在上面這個Socket中,我使用sc.pipeline().addLast()這個方法設置了接收爲字符串類型,注意:只能設置接收爲字符串類型,發送仍是須要發送ByteBuf類型的數據。並且在這裏我還設置了以$_爲結尾的字符串就表明了本次請求字符串的結束。

6.經過b.bind綁定端口,用於監聽的端口號。

ServerHandler.java

public class ServertHandler extends ChannelHandlerAdapter {  
  
    @Override  
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {  
        String body = (String) msg;  
        System.out.println("server"+body);//前面已經定義了接收爲字符串,這裏直接接收字符串就能夠  
        //服務端給客戶端的響應  
        String response= " hi client!$_";//發送的數據以定義結束的字符串結尾  
        ctx.writeAndFlush(Unpooled.copiedBuffer(response.getBytes()));//發送必須仍是ByteBuf類型  
    }  
  
    @Override  
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {  
         cause.printStackTrace();  
          ctx.close();  
    }  
      
}  

ServertHandler繼承自 ChannelHandlerAdapter,這個類實現了ChannelHandler接口,ChannelHandler提供了許多事件處理的接口方法,而後你能夠覆蓋這些方法。如今僅僅只須要繼承ChannelHandlerAdapter類而不是你本身去實現接口方法。

1.因爲咱們再server端開始的時候已經定義了接收類型爲String,因此在這裏咱們接收到的msg直接強轉成String就能夠了。同時也要定義以什麼爲一次請求的結尾。

Client.java

public class Client {  
  
    public static void main(String[] args) throws InterruptedException {  
        EventLoopGroup worker = new NioEventLoopGroup();  
        Bootstrap b = new Bootstrap();  
        b.group(worker)  
        .channel(NioSocketChannel.class)  
        .handler(new ChannelInitializer<SocketChannel>() {  
            @Override  
            protected void initChannel(SocketChannel sc) throws Exception {  
                ByteBuf buf = Unpooled.copiedBuffer("$_".getBytes());   
                sc.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,buf));  
                sc.pipeline().addLast(new StringDecoder());  
                sc.pipeline().addLast(new ClientHandler());  
            }  
        });  
        ChannelFuture f=b.connect("127.0.0.1",8765).sync();  
        f.channel().writeAndFlush(Unpooled.copiedBuffer(" hi server2$_".getBytes()));  
        f.channel().writeAndFlush(Unpooled.copiedBuffer(" hi server3$_".getBytes()));  
        f.channel().writeAndFlush(Unpooled.copiedBuffer(" hi server4$_".getBytes()));  
        f.channel().closeFuture().sync();  
        worker.shutdownGracefully();  
    }  
}  

client端和Socket端幾乎代碼相同,只是client端用的不是ServerBootstrap而是Bootstrap來管理鏈接。這裏沒什麼好說的。

ClientHandler.java

public class ClientHandler extends ChannelHandlerAdapter{  
    @Override  
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {  
        try {  
            System.out.println("client"+msg.toString());  
        } finally {  
            ReferenceCountUtil.release(msg);//釋放緩衝區  
        }  
    }  
  
    @Override  
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {  
         cause.printStackTrace();  
          ctx.close();  
    }  
}  

ClientHandler和ServertHandler代碼和原理也是同樣,只是在client端咱們要釋放緩衝區。爲何在ServerHandler咱們不須要釋放呢 ?由於在ServertHandler咱們調用ctx.writeAndFlush方法的時候,這個方法默認已經幫咱們釋放了緩衝區。

相關文章
相關標籤/搜索