Netty | 示例代码

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fragrant_no1/article/details/85266410

服务端:

  • 启动类
package com.example.demo.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;

import java.net.InetSocketAddress;

/**
 * netty服务端 它包含业务逻辑,决定当有一个请求连接或接收数据时该做什么
 */
public class EchoServer {
    /**
     * 线程组
     */
    private EventLoopGroup eventLoopGroup;

    /**
     * 绑定端口
     */
    private int port;

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

    /**
     * 执行方法
     */
    public void start() throws InterruptedException {

        try {


        //创建引导类:端口绑定,启动NETTY服务
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        //创建NioEventLoopGroup 线程组来处理事件,如接受新连接、接收数据、写数据等等 包括boss,worker
        eventLoopGroup  = new NioEventLoopGroup();
        //指定通道类型为NioServerSocketChannel,设置InetSocketAddress让服务器监听某个端口已等待客户端连接,并配置线程组
        serverBootstrap.group(eventLoopGroup).channel(NioServerSocketChannel.class).localAddress(new InetSocketAddress("localhost",port)).childHandler(new ChannelInitializer<Channel>() {
            //设置childHandler执行所有的连接请求
            @Override
            protected void initChannel(Channel ch) throws Exception {
                ch.pipeline().addLast(new EchoInServerHandler());
            }
        });
        // 线程同步阻塞等待服务器绑定到指定端口
         ChannelFuture channelFuture  = serverBootstrap.bind().sync();
        //成功绑定到端口之后,给channel增加一个 管道关闭的监听器并同步阻塞,直到channel关闭,线程才会往下执行,结束进程。
         channelFuture.channel().closeFuture().sync();

        }finally {
            //优雅的关机
            eventLoopGroup.shutdownGracefully().sync();
        }

    }

    /**
     * 启动方法
     */
    public static void main(String[] args) throws InterruptedException {
        EchoServer echoServer = new EchoServer(8899);
        echoServer.start();
    }

    /**
     *     public Promise<V> sync() throws InterruptedException {
     *         await();
     *         rethrowIfFailed();  // 异步操作失败抛出异常
     *         return this;
     *     }
     *
     *    Future类
     *         // 异步操作完成且正常终止
     *     boolean isSuccess();
     *     // 异步操作是否可以取消
     *     boolean isCancellable();
     *     // 异步操作失败的原因
     *     Throwable cause();
     *     // 添加一个监听者,异步操作完成时回调,类比javascript的回调函数
     *     Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
     *     Future<V> removeListener(GenericFutureListener<? extends Future<? super V>> listener);
     *     // 阻塞直到异步操作完成
     *     Future<V> await() throws InterruptedException;
     *     // 同上,但异步操作失败时抛出异常
     *     Future<V> sync() throws InterruptedException;
     *     // 非阻塞地返回异步结果,如果尚未完成返回null
     *     V getNow();
     */

}

  • 服务端handle执行类
package com.example.demo.netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;

import java.util.Date;

public class EchoInServerHandler extends ChannelInboundHandlerAdapter {
    /**
     *ChannelHandlerContext pipeline 中handle共享的
     * 客户端请求读取方法
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        System.out.println("server 读取数据……");
        //读取数据
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        //序列化
        String body = new String(req, "UTF-8");
        System.out.println("接收客户端数据:" + body);
        //向客户端写数据
        System.out.println("server向client发送数据" + body);
        String currentTime = new Date(System.currentTimeMillis()).toString();
        //bytes转byteBuff
        ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
        //然后从byteBuff中写入通道,但是这个write动作也是异步,不是及时执行得。
        ChannelFuture channelFuture = ctx.writeAndFlush(resp); //刷新后才将数据发出到SocketChannel
        channelFuture.addListener(ChannelFutureListener.CLOSE); //关闭跟客户端的连接通道
        //所以可能下面得代码会先执行
       // System.out.println("通道写入");
    }

    /**
     * 读取数据完毕走的方法
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("server 读取数据完毕..");
    }

    /**
     * 异常捕获处理
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        //停止往下进行handle
        ctx.close();
    }


}

客户端

  • 客户端启动类
package com.example.demo.netty;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
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;

/**
 * NETTY客户端
 * 连接服务器 • 写数据到服务器 • 等待接受服务器返回相同的数据 • 关闭连接
 */
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 {
            //创建Bootstrap对象用来引导启动客户端
            Bootstrap bootstrap = new Bootstrap();
            //创建EventLoopGroup对象并设置到Bootstrap中,EventLoopGroup可以理解为是一个线程池,这个线程池用来处理连接、接受数据、发送数据
            nioEventLoopGroup = new NioEventLoopGroup();
            //创建InetSocketAddress并设置到Bootstrap中,InetSocketAddress是指定连接的服务器地址
            bootstrap.group(nioEventLoopGroup).channel(NioSocketChannel.class).remoteAddress(new InetSocketAddress(host, port))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        //添加一个ChannelHandler,客户端成功连接服务器后就会被执行
                        @Override
                        protected void initChannel(SocketChannel ch)
                                throws Exception {
                            ch.pipeline().addLast(new EchoInClientHandler());
                        }
                    });
            // • 调用Bootstrap.connect()来连接服务器,阻塞式连接
            ChannelFuture f = bootstrap.connect().sync();
           //阻塞关闭,直到通道关闭
           f.channel().closeFuture().sync();

        } finally {
            // • 最后关闭EventLoopGroup来释放资源
            nioEventLoopGroup.shutdownGracefully().sync();
        }
    }

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

  • 客户端handle执行类
package com.example.demo.netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class EchoInClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
    private ChannelFuture  channelFuture;
    //客户端连接服务器成功后被调用
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("客户端连接服务器,开始发送数据……");
        byte[] req = "你好啊,请给我发送现在的时间(客户端)".getBytes();
        ByteBuf  firstMessage = Unpooled.buffer(req.length);
        firstMessage.writeBytes(req);
        channelFuture = ctx.writeAndFlush(firstMessage);
    }
    //•	从服务器接收到数据后调用
    @Override
    protected void messageReceived(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception {
        System.out.println("client 读取server数据..");
        //服务端返回消息后
        ByteBuf buf = byteBuf;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req, "UTF-8");
        System.out.println("服务端数据为 :" + body);
        channelFuture.addListener(ChannelFutureListener.CLOSE);//关闭和服务端的连接通道,接着会出发通道的关闭,然后回继续执行sync后面的代码,因为对应的通道为连接通道
    }

    //•	发生异常时被调用
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("client exceptionCaught..");
        // 释放资源
        ctx.close();
    }

    /**
     * 这三个Listener对象定义了对Channel处理时常用的操作,如果符合需求,可以直接使用。
     *     ChannelFutureListener CLOSE = (future) --> {
     *         future.channel().close();   //操作完成时关闭Channel
     *     };
     *
     *     ChannelFutureListener CLOSE_ON_FAILURE = (future) --> {
     *         if (!future.isSuccess()) {
     *             future.channel().close();   // 操作失败时关闭Channel
     *         }
     *     };
     *
     *     ChannelFutureListener FIRE_EXCEPTION_ON_FAILURE = (future) --> {
     *         if (!future.isSuccess()) {
     *             // 操作失败时触发一个ExceptionCaught事件
     *             future.channel().pipeline().fireExceptionCaught(future.cause());
     *         }
     *     };
     */
}

猜你喜欢

转载自blog.csdn.net/fragrant_no1/article/details/85266410