Netty在Android开发中的应用实战系列(一)——— 搭建服务端与客户端

227 阅读4分钟
原文链接: azhon.blog.csdn.net

阅读本文建议从第一篇开始往后看

本系列文章

一、简单的介绍一下Netty

  • 官网地址:https://netty.io
  • 官网Jar包下载:https://netty.io/downloads.html
  • Jar包也可以去mvnrepository下载:https://mvnrepository.com/artifact/io.netty/netty-all
  • 直接上官网的介绍再好不过了
    在这里插入图片描述
  • 谷歌翻译过来就是
    Netty是一个异步事件驱动的网络应用程序框架用于快速开发可维护的高性能协议服务器和客户端。
  • Netty是一个NIO异步非阻塞应用程序框架,关于IONIOAIO的理解大家可以找文章看看

二、创建Tcp服务端

  • 引入netty-all-4.1.39.Final.jar

  • 添加网络权限<uses-permission android:name="android.permission.INTERNET" />

  • 创建NettyServer.java开启tcp服务

public class NettyServer {
    private static final String TAG = "NettyServer";

    //端口
    private static final int PORT = 7010;

    /**
     * 启动tcp服务端
     */
    public void startServer() {
        try {
            EventLoopGroup bossGroup = new NioEventLoopGroup();
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            //添加发送数据编码器
                            pipeline.addLast(new ServerEncoder());
                            //添加解码器,对收到的数据进行解码
                            pipeline.addLast(new ServerDecoder());
                            //添加数据处理
                            pipeline.addLast(new ServerHandler());
                        }
                    });
            //服务器启动辅助类配置完成后,调用 bind 方法绑定监听端口,调用 sync 方法同步等待绑定操作完成
            b.bind(PORT).sync();
            Log.d(TAG, "TCP 服务启动成功 PORT = " + PORT);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • ServerEncoder.java服务端发送数据编码器
public class ServerEncoder extends MessageToByteEncoder<Object> {

    private static final String TAG = "ServerEncoder";

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Object data, ByteBuf byteBuf) throws Exception {
        //自己发送过来的东西进行编码
        byteBuf.writeBytes(data.toString().getBytes());
    }
}
  • ServerDecoder.java解码器,对客户端的数据进行解析
public class ServerDecoder extends ByteToMessageDecoder {
    private static final String TAG = "ServerDecoder";

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        //收到的数据长度
        int length = byteBuf.readableBytes();
        //创建个byteBuf存储数据,进行编辑
        ByteBuf dataBuf = Unpooled.buffer(length);
        //写入收到的数据
        dataBuf.writeBytes(byteBuf);
        //将byteBuf转为数组
        String data = new String(dataBuf.array());
        Log.d(TAG, "收到了客户端发送的数据:" + data);
        //将数据传递给下一个Handler,也就是在NettyServer给ChannelPipeline添加的处理器
        list.add(data);
    }
}
  • ServerHandler.java数据处理器
public class ServerHandler extends SimpleChannelInboundHandler<Object> {

    private static final String TAG = "ServerHandler";

    /**
     * 当收到数据的回调
     *
     * @param channelHandlerContext 封装的连接对像
     * @param o
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {
        Log.d(TAG, "收到了解码器处理过的数据:" + o.toString());
    }

    /**
     * 有客户端连接过来的回调
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        Log.d(TAG, "有客户端连接过来:" + ctx.toString());
    }

    /**
     * 有客户端断开了连接的回调
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        Log.d(TAG, "有客户端断开了连接:" + ctx.toString());
    }
}
  • 重点:设置childHandler(ChannelHandler childHandler),对连接通道设置数据处理器(ChannelHandler)
  • 上面添加了一个数据解码处理器,一个数据处理器,对收到的客户端数据进行处理,如下图所示:

三、创建客户端,与创建服务端差不多一至

  • 创建NettyClient.java
public class NettyClient {
    private static final String TAG = "NettyClient";
    private final int PORT = 7010;
    //连接的服务端ip地址
    private final String IP = "192.168.3.111";
    private static NettyClient nettyClient;
    //与服务端的连接通道
    private Channel channel;
    
    public static NettyClient getInstance() {
        if (nettyClient == null) {
            nettyClient = new NettyClient();
        }
        return nettyClient;
    }
    /**
     *需要在子线程中发起连接
     */
    private NettyClient() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                connect();
            }
        }).start();
    }
    /**
     * 获取与服务端的连接
     */
    public static Channel getChannel() {
        if (nettyClient == null) {
            return null;
        }
        return nettyClient.channel;
    }
    /**
     * 连接服务端
     */
    public void connect() {
        try {
            NioEventLoopGroup group = new NioEventLoopGroup();
            Bootstrap bootstrap = new Bootstrap()
                    // 指定channel类型
                    .channel(NioSocketChannel.class)
                    // 指定EventLoopGroup
                    .group(group)
                    // 指定Handler
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            //添加发送数据编码器
                            pipeline.addLast(new ClientEncoder());
                            //添加数据处理器
                            pipeline.addLast(new ClientHandler());
                        }
                    });
            // 连接到服务端
            ChannelFuture channelFuture = bootstrap.connect(new InetSocketAddress(IP, PORT));
            //获取连接通道
            channel = channelFuture.sync().channel();
        } catch (Exception e) {
            Log.e(TAG, "连接失败:" + e.getMessage());
            e.printStackTrace();
        }
    }
}
  • ClientEncoder.java对发送的数据进行编码
public class ClientEncoder extends MessageToByteEncoder<Object> {

    private static final String TAG = "ClientEncoder";

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Object data, ByteBuf byteBuf) throws Exception {
        //自己发送过来的东西进行编码
        byteBuf.writeBytes(data.toString().getBytes());
    }
}
  • ClientHandler.java数据处理器
public class ClientHandler extends SimpleChannelInboundHandler<Object> {

    private static final String TAG = "ClientHandler";

    /**
     * 当收到数据的回调
     *
     * @param channelHandlerContext 封装的连接对像
     * @param o
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {

    }

    /**
     * 与服务端连接成功的回调
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        Log.d(TAG, "与服务端连接成功:" + ctx.toString());
    }

    /**
     * 与服务端断开的回调
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        Log.d(TAG, "与服务端断开连接:" + ctx.toString());
    }
}

四、先启动服务端 在启动客户端,这样就可以建立起连接了

  • 启动服务端
 NettyServer server = new NettyServer();
 server.startServer();
  • 启动客户端
NettyClient client = NettyClient.getInstance();
  • 运行效果
    在这里插入图片描述
    在这里插入图片描述

五、下一篇文章将着重对EncoderDecoderHandler进行讲解说明

Demo将会在本系列文章中最后一篇给出