1. Netty之EchoHandler如何编写

导读

(1) 书写一个Netty网络服务器,一般需要4个文件

  • EchoServer : 服务端启动类
  • EchoServerHandler:服务端处理类
  • EchoClient: 客户端启动类
  • EchoClientHandler:客户端处理类

(2) 内容概括:

  • 本章节侧重于将Handler的用法,至于启动类,下一章节会详细的将每一行语句

(3) 开发工具

  • 若导入到eclipse,直接右键-》main运行即可

(4) 代码下载
git->echo为本节

(5) 实现代码:

  • 客户端建立连接之后发送“hello wenguang”到服务端
  • 服务端打印到终端并且将代码返回
  • 客户端将收到的代码打印出来
项目代码
  1. EchoServer
    (1) EchoSevre
package com.qinwenguang;

import java.net.InetSocketAddress;
import io.netty.bootstrap.ServerBootstrap;
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.NioServerSocketChannel;

public class EchoServer {

  private int port;

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

  //程序主要启动流程
  public void start() throws InterruptedException {

    final EchoServerHandler handler = new EchoServerHandler();
    EventLoopGroup group = new NioEventLoopGroup();

    try {
      ServerBootstrap b = new ServerBootstrap();
      b.group(group).channel(NioServerSocketChannel.class)
        .localAddress(new InetSocketAddress(port))
        .childHandler(new ChannelInitializer() {
          @Override
          public void initChannel(SocketChannel ch) throws Exception {
            //绑定入站事件
            ch.pipeline().addLast(handler);
          }
        });
      ChannelFuture future = b.bind().sync();
      System.out.println("start on port: " + port);
      future.channel().closeFuture().sync();

    } finally {
      group.shutdownGracefully().sync();
    }
  }

  public static void main(String[] args) {
    try {
      new EchoServer(17746).start();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
}

(2) EchoServerHandler

package com.qinwenguang;

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 io.netty.util.CharsetUtil;

class EchoServerHandler extends ChannelInboundHandlerAdapter {

  //有数据读取的时候调用
  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) {
    ByteBuf in = (ByteBuf) msg;
    System.out.println(
        "Server received: " + in.toString(CharsetUtil.UTF_8));
    ctx.write(in);
  }

  //本次读取完成调用
  @Override
  public void channelReadComplete(ChannelHandlerContext ctx) {
    ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
      .addListener(ChannelFutureListener.CLOSE);
  }

  @Override
  public void exceptionCaught(ChannelHandlerContext ctx,
      Throwable cause) {
    cause.printStackTrace();
    ctx.close();
  }
}
  1. EchoClient
    1)
package com.qinwenguang;

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;

public class EchoClient {

  private String host;
  private int port;

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

  /**程序绑定流程*/
  public void start()
    throws Exception {
    EventLoopGroup group = new NioEventLoopGroup();
    try {
      Bootstrap b = new Bootstrap();
      b.group(group)
        .channel(NioSocketChannel.class)
        .remoteAddress(new InetSocketAddress(host, port))
        .handler(new ChannelInitializer() {
          @Override
          public void initChannel(SocketChannel ch) 
          throws Exception {
          ch.pipeline().addLast( new EchoClientHandler());
          }
        });
      ChannelFuture f = b.connect().sync();
      f.channel().closeFuture().sync();
    } finally {
      group.shutdownGracefully().sync();
    }
  }

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

(2) EchoClientHandler

package com.qinwenguang;

import java.io.UnsupportedEncodingException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

@Sharable
public class EchoClientHandler
  extends SimpleChannelInboundHandler {
  @Override
  public void channelActive(ChannelHandlerContext ctx) throws UnsupportedEncodingException {
    ByteBuf buff = Unpooled.buffer();
    buff.writeBytes("Netty rocks!".getBytes("UTF-8"));
    ctx.writeAndFlush(buff);
  }

  @Override
  public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) {
    System.out.println( "Client received: " + in.toString(CharsetUtil.UTF_8));
  }

  @Override
  public void exceptionCaught(ChannelHandlerContext ctx,
      Throwable cause) {
    cause.printStackTrace();
    ctx.close();
  }
}
  1. 运行结果
    (1) server
mvn compile
mvn exec:java -Dexec.mainClass="com.qinwenguang.EchoServer"
1. Netty之EchoHandler如何编写_第1张图片
a79d18dd-df54-4915-8bdb-1cc85c439085.png

(2) client

mvn compile
mvn exec:java -Dexec.mainClass="com.qinwenguang.EchoClient"
1. Netty之EchoHandler如何编写_第2张图片
a79d18dd-df54-4915-8bdb-1cc85c439085.png
Handler解析
  1. handler部分类关系

(1) 涉及的类
- ChannelHandlerContext: Channel之间交换数据使用
- ChannelInboundHandlerAdapter:提供了很多方法对入站数据进行操作
- ByteBuf: 缓冲区:包含要读取的内容(读取和写入可以使用一个缓冲区)

(2) 动作
- 对于入栈事件:直接读取ByteBuf操作即可(netty已经封装好一切)
- 对于每一次客户端向服务端发送请求,服务端都会调用channelRead() channelReadComplete()两个方法

(3) 服务端输出

ctx.write(buff): 将数据刷新到channel上下文中 
ctx. flush() 刷新后才将数据发出到SocketChannel
ctx.close() 显式调用关闭之后, 连接才会断开,否则一直连接,而不管是否有数据发送
  1. serverHandler方法解析
    (1) channelRead: 从当前Channel的对端读取消息
/*服务端从客户端中读取消息*/
public void channelRead(ChannelHandlerContext ctx,  java.lang.Object msg)   
    throws java.lang.Exception;
- msg为读取缓冲区, 需要强制转为ByteBuf

(2) channelReadComplete:本次读取完毕有执行时执行

public void channelReadComplete(ChannelHandlerContext ctx) throws Exception
  • 本方法常常用于本次刷新缓冲区和关闭连接使用
//显式关闭,否则两者连接仍然保持
ctx.close()

(3) exceptionCaught: 重写父类ChannelHandler的方法,处理异常.

public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
    throws Exception {
    System.out.println("server exceptionCaught..");
    ctx.close();
}
  • 此方法服务端和客户端都必须调用
  1. ClientHandler
    (1) channelActive: 连接可用的时候执行
public void channelActive(ChannelHandlerContext ctx)
  • 一般在此方法中客户端连接上服务端后向服务端发送请求
  • 也可以在此方法中client连接上server后, Server像客户端发送数据

4.ByteBuf
(1) API

io.netty.buffer
    Class ByteBuf
/*Returns the number of readable bytes which is equal to (this.writerIndex - this.readerIndex).*/
public abstract int readableBytes()
 
/*Decodes this buffer's readable bytes into a string with the specified character set name+*/
public abstract java.lang.String toString(java.nio.charset.Charset charset);

(2) 新建ByteBuf

    ByteBuf directBuffer = Unpooled.buffer();  
    System.out.println(heapBuffer);  
              
    ByteBuf directBuffer = Unpooled.directBuffer();  
    System.out.println(directBuffer);  
              
    ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(new byte[•128]);  
    System.out.println(wrappedBuffer);  
              
    ByteBuf copiedBuffer = Unpooled.copiedBuffer(new byte[128]);  
    System.out.println(copiedBuffer);

(3) String -> ByteBuf

//新建ByteBuf
ByteBuf buff = Unpooled.buffer();
//写入数据
buff.writeBytes("Netty rocks!".getBytes("UTF-8"));
//写入和刷新
ctx.writeAndFlush(buff);
简单写法
ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!",CharsetUtil.UTF_8));

(4) ByetBuf -> String

buff.toString(CharsetUtil.UTF_8))

(5) ByteBuffer->byte

byte[] req = new byte[buf.readableBytes()];  /*创建字节数组的大小*/
buf.readBytes(req); /*读取内容到数组中*/
String body = new String(req, "UTF-8");

(6) byte -> ByteBuf

/*将byte数组写入Bytebuf中*/
resp.writeBytes("测试测试测试".getBytes());

(7) 什么时候需要新建ByteBuf

  • 需要新建ByteBuf:channelActive()中
  • 如果方法有传入ByteBuf的话,一般不需要新建

你可能感兴趣的:(1. Netty之EchoHandler如何编写)