java-netty


netty常用API學習

netty簡介

  • Netty是基于Java NIO的網(wǎng)絡應用框架.
  • Netty是一個NIO client-server(客戶端服務器)框架狸剃,使用Netty可以快速開發(fā)網(wǎng)絡應用塑娇,例如服務器和客戶端協(xié)議。Netty提供了一種新的方式來使開發(fā)網(wǎng)絡應用程序被丧,這種新的方式使得它很容易使用和有很強的擴展性。Netty的內部實現(xiàn)時很復雜的,但是Netty提供了簡單易用的api從網(wǎng)絡處理代碼中解耦業(yè)務邏輯鸽扁。Netty是完全基于NIO實現(xiàn)的谁撼,所以整個Netty都是異步的歧胁。
  • 網(wǎng)絡應用程序通常需要有較高的可擴展性,無論是Netty還是其他的基于Java NIO的框架,都會提供可擴展性的解決方案喊巍。Netty中一個關鍵組成部分是它的異步特性.

netty的helloworld

下載netty包

服務端啟動類

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * ? 配置服務器功能,如線程玄糟、端口 ? 實現(xiàn)服務器處理程序勿她,它包含業(yè)務邏輯,決定當有一個請求連接或接收數(shù)據(jù)時該做什么
 *
 */
public class EchoServer {

    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public void start() throws Exception {
        EventLoopGroup eventLoopGroup = null;
        try {
            //創(chuàng)建ServerBootstrap實例來引導綁定和啟動服務器
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            //創(chuàng)建NioEventLoopGroup對象來處理事件阵翎,如接受新連接逢并、接收數(shù)據(jù)、寫數(shù)據(jù)等等
            eventLoopGroup = new NioEventLoopGroup();
            //指定通道類型為NioServerSocketChannel郭卫,設置InetSocketAddress讓服務器監(jiān)聽某個端口已等待客戶端連接砍聊。
            serverBootstrap.group(eventLoopGroup).channel(NioServerSocketChannel.class).localAddress("localhost",port).childHandler(new ChannelInitializer<Channel>() {
                //設置childHandler執(zhí)行所有的連接請求
                @Override
                protected void initChannel(Channel ch) throws Exception {
                    ch.pipeline().addLast(new EchoServerHandler());
                }
                    });
            // 最后綁定服務器等待直到綁定完成,調用sync()方法會阻塞直到服務器完成綁定,然后服務器等待通道關閉贰军,因為使用sync()玻蝌,所以關閉操作也會被阻塞。
            ChannelFuture channelFuture = serverBootstrap.bind().sync();
            System.out.println("開始監(jiān)聽词疼,端口為:" + channelFuture.channel().localAddress());
            channelFuture.channel().closeFuture().sync();
        } finally {
            eventLoopGroup.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws Exception {
        new EchoServer(20000).start();
    }
}

服務端回調方法

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.util.Date;

public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        System.out.println("server 讀取數(shù)據(jù)……");
        //讀取數(shù)據(jù)
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req, "UTF-8");
        System.out.println("接收客戶端數(shù)據(jù):" + body);
        //向客戶端寫數(shù)據(jù)
        System.out.println("server向client發(fā)送數(shù)據(jù)");
        String currentTime = new Date(System.currentTimeMillis()).toString();
        ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
        ctx.write(resp);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("server 讀取數(shù)據(jù)完畢..");
        ctx.flush();//刷新后才將數(shù)據(jù)發(fā)出到SocketChannel
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

}

客戶端啟動類


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.net.InetSocketAddress;

/**
 * ? 連接服務器 ? 寫數(shù)據(jù)到服務器 ? 等待接受服務器返回相同的數(shù)據(jù) ? 關閉連接
 *
 */
public class EchoClient {

    private final String host;
    private final int port;

    public EchoClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void start() throws Exception {
        EventLoopGroup nioEventLoopGroup = null;
        try {
            //創(chuàng)建Bootstrap對象用來引導啟動客戶端
            Bootstrap bootstrap = new Bootstrap();
            //創(chuàng)建EventLoopGroup對象并設置到Bootstrap中俯树,EventLoopGroup可以理解為是一個線程池,這個線程池用來處理連接贰盗、接受數(shù)據(jù)许饿、發(fā)送數(shù)據(jù)
            nioEventLoopGroup = new NioEventLoopGroup();
            //創(chuàng)建InetSocketAddress并設置到Bootstrap中,InetSocketAddress是指定連接的服務器地址
            bootstrap.group(nioEventLoopGroup).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(host, port))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        //添加一個ChannelHandler舵盈,客戶端成功連接服務器后就會被執(zhí)行
                        @Override
                        protected void initChannel(SocketChannel ch)
                                throws Exception {
                            ch.pipeline().addLast(new EchoClientHandler());
                        }
                    });
            // ? 調用Bootstrap.connect()來連接服務器
            ChannelFuture f = bootstrap.connect().sync();
            // ? 最后關閉EventLoopGroup來釋放資源
            f.channel().closeFuture().sync();
        } finally {
            nioEventLoopGroup.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws Exception {
        new EchoClient("localhost", 20000).start();
    }
}

netty中handler的執(zhí)行順序

  • Handler在netty中陋率,無疑占據(jù)著非常重要的地位。Handler與Servlet中的filter很像秽晚,通過Handler可以完成通訊報文的解碼編碼瓦糟、攔截指定的報文、統(tǒng)一對日志錯誤進行處理赴蝇、統(tǒng)一對請求進行計數(shù)菩浙、控制Handler執(zhí)行與否。一句話句伶,沒有它做不到的只有你想不到的劲蜻。
  • Netty中的所有handler都實現(xiàn)自ChannelHandler接口。按照輸出輸出來分熄阻,分為ChannelInboundHandler斋竞、ChannelOutboundHandler兩大類。ChannelInboundHandler對從客戶端發(fā)往服務器的報文進行處理秃殉,一般用來執(zhí)行解碼坝初、讀取客戶端數(shù)據(jù)浸剩、進行業(yè)務處理等;ChannelOutboundHandler對從服務器發(fā)往客戶端的報文進行處理鳄袍,一般用來進行編碼绢要、發(fā)送報文到客戶端。
  • Netty中拗小,可以注冊多個handler重罪。ChannelInboundHandler按照注冊的先后順序執(zhí)行;ChannelOutboundHandler按照注冊的先后順序逆序執(zhí)行哀九,如下圖所示剿配,按照注冊的先后順序對Handler進行排序,request進入Netty后的執(zhí)行順序為
    • mark

代碼示例

  • server

    • import io.netty.bootstrap.ServerBootstrap;
      import io.netty.channel.Channel;
      import io.netty.channel.ChannelFuture;
      import io.netty.channel.ChannelInitializer;
      import io.netty.channel.EventLoopGroup;
      import io.netty.channel.nio.NioEventLoopGroup;
      import io.netty.channel.socket.nio.NioServerSocketChannel;
      
      /**
       * ? 配置服務器功能阅束,如線程呼胚、端口 ? 實現(xiàn)服務器處理程序,它包含業(yè)務邏輯息裸,決定當有一個請求連接或接收數(shù)據(jù)時該做什么
       */
      public class EchoServer {
      
          private final int port;
      
          public EchoServer(int port) {
              this.port = port;
          }
      
          public void start() throws Exception {
              EventLoopGroup eventLoopGroup = null;
              try {
                  //server端引導類
                  ServerBootstrap serverBootstrap = new ServerBootstrap();
                  //連接池處理數(shù)據(jù)
                  eventLoopGroup = new NioEventLoopGroup();
                  serverBootstrap.group(eventLoopGroup)
                  .channel(NioServerSocketChannel.class)//指定通道類型為NioServerSocketChannel蝇更,一種異步模式,OIO阻塞模式為OioServerSocketChannel
                  .localAddress("localhost",port)//設置InetSocketAddress讓服務器監(jiān)聽某個端口已等待客戶端連接呼盆。
                  .childHandler(new ChannelInitializer<Channel>() {//設置childHandler執(zhí)行所有的連接請求
                      @Override
                      protected void initChannel(Channel ch) throws Exception {
                          // 注冊兩個InboundHandler年扩,執(zhí)行順序為注冊順序,所以應該是InboundHandler1 InboundHandler2
                          // 注冊兩個OutboundHandler访圃,執(zhí)行順序為注冊順序的逆序厨幻,所以應該是OutboundHandler2 OutboundHandler1
                          ch.pipeline().addLast(new EchoInHandler1());
                          ch.pipeline().addLast(new EchoInHandler2());
                          ch.pipeline().addLast(new EchoOutHandler1());
                          ch.pipeline().addLast(new EchoOutHandler2()); 
                          
                      }
                          });
                  // 最后綁定服務器等待直到綁定完成,調用sync()方法會阻塞直到服務器完成綁定,然后服務器等待通道關閉挽荠,因為使用sync()克胳,所以關閉操作也會被阻塞平绩。
                  ChannelFuture channelFuture = serverBootstrap.bind().sync();
                  System.out.println("開始監(jiān)聽圈匆,端口為:" + channelFuture.channel().localAddress());
                  channelFuture.channel().closeFuture().sync();
              } finally {
                  eventLoopGroup.shutdownGracefully().sync();
              }
          }
      
          public static void main(String[] args) throws Exception {
              new EchoServer(20000).start();
          }
      }
      
  • EchoInHandler1

    • public class EchoInHandler1 extends ChannelInboundHandlerAdapter {
      
          @Override
          public void channelRead(ChannelHandlerContext ctx, Object msg)
                  throws Exception {
              System.out.println("in1");
               // 通知執(zhí)行下一個InboundHandler
              ctx.fireChannelRead(msg);
          }
      
          @Override
          public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
              ctx.flush();//刷新后才將數(shù)據(jù)發(fā)出到SocketChannel
          }
      
          @Override
          public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
                  throws Exception {
              cause.printStackTrace();
              ctx.close();
          }
      
      }
      

      ?

  • EchoInHandler2

    • import io.netty.buffer.ByteBuf;
      import io.netty.buffer.Unpooled;
      import io.netty.channel.ChannelHandlerContext;
      import io.netty.channel.ChannelInboundHandlerAdapter;
      
      import java.util.Date;
      
      import cn.itcast_03_netty.sendobject.bean.Person;
      
      public class EchoInHandler2 extends ChannelInboundHandlerAdapter {
      
          @Override
          public void channelRead(ChannelHandlerContext ctx, Object msg)
                  throws Exception {
              System.out.println("in2");
              ByteBuf buf = (ByteBuf) msg;
              byte[] req = new byte[buf.readableBytes()];
              buf.readBytes(req);
              String body = new String(req, "UTF-8");
              System.out.println("接收客戶端數(shù)據(jù):" + body);
              //向客戶端寫數(shù)據(jù)
              System.out.println("server向client發(fā)送數(shù)據(jù)");
              String currentTime = new Date(System.currentTimeMillis()).toString();
              ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
              ctx.write(resp);
              
          }
      
          @Override
          public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
              ctx.flush();//刷新后才將數(shù)據(jù)發(fā)出到SocketChannel
          }
      
          @Override
          public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
                  throws Exception {
              cause.printStackTrace();
              ctx.close();
          }
      
      }
      

      ?

  • EchoOutHandler1

    • import java.util.Date;
      
      import io.netty.buffer.ByteBuf;
      import io.netty.buffer.Unpooled;
      import io.netty.channel.ChannelHandlerContext;
      import io.netty.channel.ChannelOutboundHandlerAdapter;
      import io.netty.channel.ChannelPromise;
      
      public class EchoOutHandler1 extends ChannelOutboundHandlerAdapter {
          @Override
          // 向client發(fā)送消息
          public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
              System.out.println("out1");
              /*System.out.println(msg);*/
              
              String currentTime = new Date(System.currentTimeMillis()).toString();
              ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
              ctx.write(resp);
              ctx.flush();
             }
      }
      

      ?

  • EchoOutHandler2

    • import io.netty.channel.ChannelHandlerContext;
      import io.netty.channel.ChannelOutboundHandlerAdapter;
      import io.netty.channel.ChannelPromise;
      
      public class EchoOutHandler2 extends ChannelOutboundHandlerAdapter {
      
           @Override
              public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                  System.out.println("out2");
                  // 執(zhí)行下一個OutboundHandler
                  /*System.out.println("at first..msg = "+msg);
                  msg = "hi newed in out2";*/
                  super.write(ctx, msg, promise);
              }
      
      }
      

      ?

總結

  • 在使用Handler的過程中,需要注意:
    1. ChannelInboundHandler之間的傳遞捏雌,通過調用ctx.fireChannelRead(msg)實現(xiàn)跃赚;調用ctx.write(msg) 將傳遞到ChannelOutboundHandler。
    2. ctx.write()方法執(zhí)行后性湿,需要調用flush()方法才能令它立即執(zhí)行纬傲。
    3. 流水線pipeline中outhandler不能放在最后,否則不生效
    4. Handler的消費處理放在最后一個處理肤频。

netty發(fā)送對象

簡介

  • Netty中叹括,通訊的雙方建立連接后,會把數(shù)據(jù)按照ByteBuf的方式進行傳輸宵荒,例如http協(xié)議中汁雷,就是通過HttpRequestDecoder對ByteBuf數(shù)據(jù)流進行處理净嘀,轉換成http的對象∠姥叮基于這個思路挖藏,我自定義一種通訊協(xié)議:Server和客戶端直接傳輸java對象。
  • 實現(xiàn)的原理是通過Encoder把java對象轉換成ByteBuf流進行傳輸厢漩,通過Decoder把ByteBuf轉換成java對象進行處理膜眠,處理邏輯如下圖所示:
    • mark

代碼

  • bean

    • 
      import java.io.Serializable;
      
      public class Person implements Serializable {
          private static final long serialVersionUID = 1L;
          private String name;
          private String sex;
          private int age;
      
          public String toString() {
              return "name:" + name + " sex:" + sex + " age:" + age;
          }
      get/set...
      }
      
  • 序列化

    • import io.netty.buffer.ByteBuf;
      import io.netty.channel.ChannelHandlerContext;
      import io.netty.handler.codec.MessageToByteEncoder;
      import cn.itcast_03_netty.sendobject.bean.Person;
      import cn.itcast_03_netty.sendobject.utils.ByteObjConverter;
       /**
        * 序列化
        * 將object轉換成Byte[]
        *
        */
      public class PersonEncoder extends MessageToByteEncoder<Person> {
       
          @Override
          protected void encode(ChannelHandlerContext ctx, Person msg, ByteBuf out) throws Exception {
              //工具類:將object轉換為byte[]
              byte[] datas = ByteObjConverter.objectToByte(msg);
              out.writeBytes(datas);
              ctx.flush();
          }
      }
      
  • 反序列化

    • import io.netty.buffer.ByteBuf;
      import io.netty.channel.ChannelHandlerContext;
      import io.netty.handler.codec.ByteToMessageDecoder;
      
      import java.util.List;
      
      import cn.itcast_03_netty.sendobject.utils.ByteBufToBytes;
      import cn.itcast_03_netty.sendobject.utils.ByteObjConverter;
       
       /**
        * 反序列化
        * 將Byte[]轉換為Object
        *
        */
      public class PersonDecoder extends ByteToMessageDecoder {
          @Override
          protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
              //工具類:將ByteBuf轉換為byte[]
              ByteBufToBytes read = new ByteBufToBytes();
              byte[] bytes = read.read(in);
              //工具類:將byte[]轉換為object
              Object obj = ByteObjConverter.byteToObject(bytes);
              out.add(obj);
          }
       
      }
      
  • 轉換工具類

    • 
      import io.netty.buffer.ByteBuf;
      
      public class ByteBufToBytes {
          /**
           * 將ByteBuf轉換為byte[]
           * @param datas
           * @return
           */
          public byte[] read(ByteBuf datas) {
              byte[] bytes = new byte[datas.readableBytes()];// 創(chuàng)建byte[]
              datas.readBytes(bytes);// 將ByteBuf轉換為byte[]
              return bytes;
          }
      }
      
    • 
      import java.io.ByteArrayInputStream;
      import java.io.ByteArrayOutputStream;
      import java.io.IOException;
      import java.io.ObjectInputStream;
      import java.io.ObjectOutputStream;
      
      public class ByteObjConverter {
          /**
           * 使用IO的inputstream流將byte[]轉換為object
           * @param bytes
           * @return
           */
          public static Object byteToObject(byte[] bytes) {
              Object obj = null;
              ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
              ObjectInputStream oi = null;
              try {
                  oi = new ObjectInputStream(bi);
                  obj = oi.readObject();
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  try {
                      bi.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
                  try {
                      oi.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
              return obj;
          }
          /**
           * 使用IO的outputstream流將object轉換為byte[]
           * @param bytes
           * @return
           */
          public static byte[] objectToByte(Object obj) {
              byte[] bytes = null;
              ByteArrayOutputStream bo = new ByteArrayOutputStream();
              ObjectOutputStream oo = null;
              try {
                  oo = new ObjectOutputStream(bo);
                  oo.writeObject(obj);
                  bytes = bo.toByteArray();
              } catch (Exception e) {
                  e.printStackTrace();
              } finally {
                  try {
                      bo.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
                  try {
                      oo.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
              return bytes;
          }
      }
      
  • ServerHandler

    • 
      import io.netty.channel.ChannelHandlerContext;
      import io.netty.channel.ChannelInboundHandlerAdapter;
      import cn.itcast_03_netty.sendobject.bean.Person;
      
      public class EchoServerHandler extends ChannelInboundHandlerAdapter {
      
          @Override
          public void channelRead(ChannelHandlerContext ctx, Object msg)
                  throws Exception {
              Person person = (Person) msg;
              System.out.println(person.getName());
              System.out.println(person.getAge());
              System.out.println(person.getSex());
          }
      
          @Override
          public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
              System.out.println("server 讀取數(shù)據(jù)完畢..");
              ctx.flush();//刷新后才將數(shù)據(jù)發(fā)出到SocketChannel
          }
      
          @Override
          public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
                  throws Exception {
              cause.printStackTrace();
              ctx.close();
          }
      
      }
      
  • server

    • import io.netty.bootstrap.ServerBootstrap;
      import io.netty.channel.Channel;
      import io.netty.channel.ChannelFuture;
      import io.netty.channel.ChannelInitializer;
      import io.netty.channel.EventLoopGroup;
      import io.netty.channel.nio.NioEventLoopGroup;
      import io.netty.channel.socket.nio.NioServerSocketChannel;
      import cn.itcast_03_netty.sendobject.coder.PersonDecoder;
      
      /**
       * ? 配置服務器功能,如線程溜嗜、端口 ? 實現(xiàn)服務器處理程序宵膨,它包含業(yè)務邏輯,決定當有一個請求連接或接收數(shù)據(jù)時該做什么
       * 
       *
       */
      public class EchoServer {
      
          private final int port;
      
          public EchoServer(int port) {
              this.port = port;
          }
      
          public void start() throws Exception {
              EventLoopGroup eventLoopGroup = null;
              try {
                  //創(chuàng)建ServerBootstrap實例來引導綁定和啟動服務器
                  ServerBootstrap serverBootstrap = new ServerBootstrap();
                  //創(chuàng)建NioEventLoopGroup對象來處理事件炸宵,如接受新連接柄驻、接收數(shù)據(jù)、寫數(shù)據(jù)等等
                  eventLoopGroup = new NioEventLoopGroup();
                  //指定通道類型為NioServerSocketChannel焙压,一種異步模式鸿脓,OIO阻塞模式為OioServerSocketChannel
                  //設置InetSocketAddress讓服務器監(jiān)聽某個端口已等待客戶端連接。
                  serverBootstrap.group(eventLoopGroup).channel(NioServerSocketChannel.class).localAddress("localhost",port)
                  .childHandler(new ChannelInitializer<Channel>() {
                      //設置childHandler執(zhí)行所有的連接請求
                      @Override
                      protected void initChannel(Channel ch) throws Exception {
                          //注冊解碼的handler
                          ch.pipeline().addLast(new PersonDecoder());  //IN1  反序列化
                          //添加一個入站的handler到ChannelPipeline  
                          ch.pipeline().addLast(new EchoServerHandler());   //IN2
                      }
                          });
                  // 最后綁定服務器等待直到綁定完成涯曲,調用sync()方法會阻塞直到服務器完成綁定,然后服務器等待通道關閉野哭,因為使用sync(),所以關閉操作也會被阻塞幻件。
                  ChannelFuture channelFuture = serverBootstrap.bind().sync();
                  System.out.println("開始監(jiān)聽拨黔,端口為:" + channelFuture.channel().localAddress());
                  channelFuture.channel().closeFuture().sync();
              } finally {
                  eventLoopGroup.shutdownGracefully().sync();
              }
          }
      
          public static void main(String[] args) throws Exception {
              new EchoServer(20000).start();
          }
      }
      
  • clientHandler

    • 
      import io.netty.buffer.ByteBuf;
      import io.netty.channel.ChannelHandlerContext;
      import io.netty.channel.SimpleChannelInboundHandler;
      import cn.itcast_03_netty.sendobject.bean.Person;
      
      public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
          // 客戶端連接服務器后被調用
          @Override
          public void channelActive(ChannelHandlerContext ctx) throws Exception {
               Person person = new Person();
                  person.setName("angelababy");
                  person.setSex("girl");
                  person.setAge(18);
                  ctx.write(person);
                  ctx.flush();
          }
      
          // ? 從服務器接收到數(shù)據(jù)后調用
          @Override
          protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg)
                  throws Exception {
              System.out.println("client 讀取server數(shù)據(jù)..");
              // 服務端返回消息后
              ByteBuf buf = (ByteBuf) msg;
              byte[] req = new byte[buf.readableBytes()];
              buf.readBytes(req);
              String body = new String(req, "UTF-8");
              System.out.println("服務端數(shù)據(jù)為 :" + body);
          }
      
          // ? 發(fā)生異常時被調用
          @Override
          public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
                  throws Exception {
              System.out.println("client exceptionCaught..");
              // 釋放資源
              ctx.close();
          }
      }
      
  • client

    • 
      import io.netty.bootstrap.Bootstrap;
      import io.netty.channel.ChannelFuture;
      import io.netty.channel.ChannelInitializer;
      import io.netty.channel.EventLoopGroup;
      import io.netty.channel.nio.NioEventLoopGroup;
      import io.netty.channel.socket.SocketChannel;
      import io.netty.channel.socket.nio.NioSocketChannel;
      
      import java.net.InetSocketAddress;
      
      import cn.itcast_03_netty.sendobject.coder.PersonEncoder;
      
      /**
       * ? 連接服務器 ? 寫數(shù)據(jù)到服務器 ? 等待接受服務器返回相同的數(shù)據(jù) ? 關閉連接
       *
       */
      public class EchoClient {
      
          private final String host;
          private final int port;
      
          public EchoClient(String host, int port) {
              this.host = host;
              this.port = port;
          }
      
          public void start() throws Exception {
              EventLoopGroup nioEventLoopGroup = null;
              try {
                  // 創(chuàng)建Bootstrap對象用來引導啟動客戶端
                  Bootstrap bootstrap = new Bootstrap();
                  // 創(chuàng)建EventLoopGroup對象并設置到Bootstrap中,EventLoopGroup可以理解為是一個線程池绰沥,這個線程池用來處理連接篱蝇、接受數(shù)據(jù)、發(fā)送數(shù)據(jù)
                  nioEventLoopGroup = new NioEventLoopGroup();
                  // 創(chuàng)建InetSocketAddress并設置到Bootstrap中徽曲,InetSocketAddress是指定連接的服務器地址
                  bootstrap.group(nioEventLoopGroup)//
                          .channel(NioSocketChannel.class)//
                          .remoteAddress(new InetSocketAddress(host, port))//
                          .handler(new ChannelInitializer<SocketChannel>() {//
                                      // 添加一個ChannelHandler零截,客戶端成功連接服務器后就會被執(zhí)行
                                      @Override
                                      protected void initChannel(SocketChannel ch)
                                              throws Exception {
                                          // 注冊編碼的handler
                                          ch.pipeline().addLast(new PersonEncoder());  //out
                                          //注冊處理消息的handler
                                          ch.pipeline().addLast(new EchoClientHandler());   //in
                                      }
                                  });
                  // ? 調用Bootstrap.connect()來連接服務器
                  ChannelFuture f = bootstrap.connect().sync();
                  // ? 最后關閉EventLoopGroup來釋放資源
                  f.channel().closeFuture().sync();
              } finally {
                  nioEventLoopGroup.shutdownGracefully().sync();
              }
          }
      
          public static void main(String[] args) throws Exception {
              new EchoClient("localhost", 20000).start();
          }
      }
      
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市秃臣,隨后出現(xiàn)的幾起案子涧衙,更是在濱河造成了極大的恐慌,老刑警劉巖奥此,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弧哎,死亡現(xiàn)場離奇詭異,居然都是意外死亡稚虎,警方通過查閱死者的電腦和手機撤嫩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蠢终,“玉大人序攘,你說我怎么就攤上這事鸭限。” “怎么了两踏?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵败京,是天一觀的道長。 經常有香客問我梦染,道長赡麦,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任帕识,我火速辦了婚禮泛粹,結果婚禮上,老公的妹妹穿的比我還像新娘肮疗。我一直安慰自己晶姊,他們只是感情好,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布伪货。 她就那樣靜靜地躺著们衙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪碱呼。 梳的紋絲不亂的頭發(fā)上蒙挑,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天,我揣著相機與錄音愚臀,去河邊找鬼忆蚀。 笑死,一個胖子當著我的面吹牛姑裂,可吹牛的內容都是我干的馋袜。 我是一名探鬼主播,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼舶斧,長吁一口氣:“原來是場噩夢啊……” “哼欣鳖!你這毒婦竟也來了?” 一聲冷哼從身側響起捧毛,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤观堂,失蹤者是張志新(化名)和其女友劉穎让网,沒想到半個月后呀忧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡溃睹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年而账,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片因篇。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡泞辐,死狀恐怖笔横,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情咐吼,我是刑警寧澤吹缔,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站锯茄,受9級特大地震影響厢塘,放射性物質發(fā)生泄漏。R本人自食惡果不足惜肌幽,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一晚碾、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧喂急,春花似錦格嘁、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至狡孔,卻和暖如春冶伞,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背步氏。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工响禽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人荚醒。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓芋类,卻偏偏與公主長得像,于是被迫代替她去往敵國和親界阁。 傳聞我的和親對象是個殘疾皇子侯繁,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務發(fā)現(xiàn)泡躯,斷路器贮竟,智...
    卡卡羅2017閱讀 134,669評論 18 139
  • 6.2 Channel實現(xiàn) ![Netty_Channel類圖][2] Channel的類圖比較清晰。我們主要分析...
    Hypercube閱讀 8,527評論 6 19
  • RPC框架遠程調用的實現(xiàn)方式在原理上是比較簡單的较剃,即將調用的方法(接口名咕别、方法名、參數(shù)類型写穴、參數(shù))序列化之后發(fā)送到...
    謎碌小孩閱讀 3,105評論 0 13
  • 重新學習笑來老師《通往財富自由之路》筆記專題(2) ——你擁有最寶貴的財富是什么 一惰拱、講了什么 這篇文章打磨的概念...
    從零再次起步閱讀 238評論 0 0
  • 2014年畢業(yè),至今為止已工作3年多差不多4年了啊送,期間一直不知道自己到底想做什么偿短,一直摸爬滾打的換了3份工作欣孤,一份...
    欣籽閱讀 676評論 0 0