Android 基于Netty实现socketClient 脱坑指上

因为公司项目需要,实现跟客户设备通信方式相同的自己设备(没有备用方案,临时拉我上来做的吗?啥都不清楚,跟客户设备对接的人都也是一脸懵逼,我要只能靠自己了。-----小声哔哔)

在网上找到了相关的demo跟jar包,开启自己的使用Netty填坑之路。

Jar 包下载路径:
下载地址
当然这个分数有点高,这个是我网上找的这个。要是没有分数那就留言或者发邮件给我(fflijinyi@foxmail.com)

关于使用以及遇到的问题:
1、注意编解码问题。注意编解码问题,一定要注意编解码问题。

注意点,关于数据传输编解码问题,一定要根据服务器来实现。不然很容出现能收到服务器的数据,但是发出去的数据服务器收到不到,或者是服务器能收到客户端发出去的数据,但是客户端收不到任何消息。

2、下面的代码我是参考网上一个小哥的代码上去做的。因为没有保存网址,不能附上链接,万分抱歉。

实现部分

NettyListener 用于实现抽象接口已经常量的定义:

package com.example.lijinyi.*.*.api.socketApi;

/**
 * author : lijinyi
 * e-mail : fflijinyi@foxmail.com
 * date   : 2019/9/2516:11
 * desc   :
 * version: 1.0
 */
public interface NettyListener {
    byte STATUS_CONNECT_SUCCESS = 1;//连接成功

    byte STATUS_CONNECT_CLOSED = 0;//关闭连接

    byte STATUS_CONNECT_ERROR = 0;//连接失败

    int STATUS_SUCCESS = 0;

    int STATUS_ERROR = 1;

    //人像库操作
    int ADD_LIB = 1;
    int CHG_LIB = 2;
    int DEL_LIB = 3;

    //人像操作
    int ADD_FACE = 4;
    int CHG_FACE = 5;
    int DEL_FACE = 6;

    /**
     * 当接收到系统消息
     */
    void onMessageResponse(int statusCode,Object msg);

    /**
     * 当连接状态发生变化时调用
     */
    void onServiceStatusConnectChanged(int statusCode);

}

NettyClient 这个部分代码实现了断线重连跟心跳

package com.example.lijinyi.*.*.manager;
import com.example.lijinyi.*.*.api.socketApi.NettyListener;



/**
 * author : lijinyi
 * e-mail : fflijinyi@foxmail.com
 * date   : 2019/9/2516:12
 * desc   :
 * version: 1.0
 */
public class NettyClient {

    private static final String TAG = "NettyClient";

    private EventLoopGroup group;//Bootstrap参数

    private NettyListener listener;//写的接口用来接收服务端返回的值

    private Channel channel;//通过对象发送数据到服务端

    private boolean isConnect = false;//判断是否连接了

    private static int reconnectNum = Integer.MAX_VALUE;//定义的重连到时候用
    private boolean isNeedReconnect = true;//是否需要重连
    private boolean isConnecting = false;//是否正在连接
    private long reconnectIntervalTime = 5000;//重连的时间

    public String host;//ip
    public int tcp_port;//端口

    /*
   构造 传入 ip和端口
    */
    public NettyClient(String host, int tcp_port) {
        this.host = host;
        this.tcp_port = tcp_port;
    }

    /*
   连接方法
    */
    public void connect() {

        if (isConnecting) {
            return;
        }
        //起个线程
        Thread clientThread = new Thread("client-Netty") {
            @Override
            public void run() {
                super.run();
                isNeedReconnect = true;
                reconnectNum = Integer.MAX_VALUE;
                connectServer();
            }
        };
        clientThread.start();
    }

    //连接时的具体参数设置
    private void connectServer() {
        synchronized (NettyClient.this) {
            ChannelFuture channelFuture = null;//连接管理对象
            if (!isConnect) {
                isConnecting = true;
                group = new NioEventLoopGroup();//设置的连接group
                Bootstrap bootstrap = new Bootstrap().group(group)//设置的一系列连接参数操作等
                        .option(ChannelOption.TCP_NODELAY, true)//屏蔽Nagle算法试图
                        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
                        .channel(NioSocketChannel.class)
                        .handler(new ChannelInitializer<SocketChannel>() { // 5
                            @Override
                            public void initChannel(SocketChannel ch) throws Exception {
									
								//这部分编解码根据自己的实际需要使用。
			
                                ch.pipeline().addLast(new StringDecoder());//进行字符串的编解码设置
                                ch.pipeline().addLast(new StringEncoder());
                                ch.pipeline().addLast(new ReadTimeoutHandler(60));//设置超时时间
                                ch.pipeline().addLast(new HttpRequestEncoder());// 客户端对发送的httpRequest进行编码
                                ch.pipeline().addLast(new HttpResponseDecoder());// 客户端需要对服务端返回的httpresopnse解码
                                ch.pipeline().addLast(new HttpRequestDecoder());
                                ch.pipeline().addLast(new HttpResponseEncoder());

                                ch.pipeline().addLast(new NettyClientHandler(listener));//需要的handlerAdapter
                            }
                        });

                try {
                    //连接监听
                    channelFuture = bootstrap.connect(host, tcp_port).addListener(new ChannelFutureListener() {
                        @Override
                        public void operationComplete(ChannelFuture channelFuture) throws Exception {
                            if (channelFuture.isSuccess()) {
                                Log.e(TAG, "连接成功");
                                isConnect = true;
                                channel = channelFuture.channel();
                            } else {
                                Log.e(TAG, "连接失败");
                                isConnect = false;
                            }
                            isConnecting = false;
                        }
                    }).sync();

                    // 等待连接关闭
                    channelFuture.channel().closeFuture().sync();
                    Log.e(TAG, " 断开连接");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    isConnect = false;
                    listener.onServiceStatusConnectChanged(NettyListener.STATUS_CONNECT_CLOSED);//STATUS_CONNECT_CLOSED 这我自己定义的 接口标识
                    if (null != channelFuture) {
                        if (channelFuture.channel() != null && channelFuture.channel().isOpen()) {
                            channelFuture.channel().close();
                        }
                    }
                    group.shutdownGracefully();
                    reconnect();//重新连接
                }
            }
        }
    }

    //断开连接
    public void disconnect() {
        Log.e(TAG, "disconnect");
        isNeedReconnect = false;
        group.shutdownGracefully();
    }

    //重新连接
    public void reconnect() {
        Log.e(TAG, "reconnect");
        if (isNeedReconnect && reconnectNum > 0 && !isConnect) {
            reconnectNum--;
            SystemClock.sleep(reconnectIntervalTime);
            if (isNeedReconnect && reconnectNum > 0 && !isConnect) {
                Log.e(TAG, "重新连接");
                connectServer();
            }
        }
    }

    //发送消息到服务端。
    public boolean sendMsgHeartToServer(String data, String sUri, ChannelFutureListener listener) {
        boolean flag = channel != null && isConnect;
        try {
            if (flag) {

                URI uri = new URI(sUri);
                ByteBuf byteBuf = Unpooled.wrappedBuffer(data.getBytes(StandardCharsets.UTF_8));

                FullHttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, uri.toASCIIString(), byteBuf);
                request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
                request.headers().set(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes());
                request.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON);

                channel.writeAndFlush(request).addListener(listener).sync();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return flag;
    }

  

    //重连时间
    public void setReconnectNum(int reconnectNum) {
        NettyClient.reconnectNum = reconnectNum;
    }

    public void setReconnectIntervalTime(long reconnectIntervalTime) {
        this.reconnectIntervalTime = reconnectIntervalTime;
    }

    //现在连接的状态
    public boolean getConnectStatus() {
        return isConnect;
    }

    public boolean isConnecting() {
        return isConnecting;
    }

    public void setConnectStatus(boolean status) {
        this.isConnect = status;
    }

    public void setListener(NettyListener listener) {
        this.listener = listener;
    }

}

这个部分代码实际是通过启动一个线程来实现socket运行,通过心跳发送的来判断是否发送成功来设置标志位,从而实现断线重连的效果。

NettyClientHandler 相关代码

package com.example.lijinyi.*.*.manager;

/**
 * author : lijinyi
 * e-mail : fflijinyi@foxmail.com
 * date   : 2019/9/2516:23
 * desc   :
 * version: 1.0
 */
public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    private static final String TAG = "NettyClientHandler";
    private NettyListener listener;

    //人像库处理
    private LibInfoDao libManagerDao = null;
    private FaceInfoDao faceManagerDao = null;
    private FaceEnvironment environment = null;
    private JSONObject object;
    private int statuCode = -1;
    private String strStatu = "";

    public NettyClientHandler(NettyListener listener) {
        this.listener = listener;
    }

    //每次给服务器发送的东西, 让服务器知道我们在连接 ---心跳部分
	//这个可以定义成我们想要发送心跳包的格式
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.WRITER_IDLE) {
                ctx.channel().writeAndFlush("Heartbeat" + System.getProperty("line.separator"));
            }
        }
    }

    /**
     * 连接成功
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Log.e(TAG, "channelActive");
        super.channelActive(ctx);
        listener.onServiceStatusConnectChanged(NettyListener.STATUS_CONNECT_SUCCESS);

    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        Log.e(TAG, "channelInactive");
    }

    //接收消息的地方, 接口调用返回到activity了
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //这里是接收从服务器发送过来的数据。
        System.out.println("客户端开始读取服务端过来的信息" + " " + msg.toString());
        
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        // 当引发异常时关闭连接。
        Log.e(TAG, "exceptionCaught");
        listener.onServiceStatusConnectChanged(NettyListener.STATUS_CONNECT_ERROR);
        cause.printStackTrace();
        ctx.close();
    }

}

整个Netty 实现客户端主要注意点就是编解码问题,channelRead() 接收数据部分,writeAndFlush()发送数据部分。其他也没有什么需要注意的地方了。

那么就这个样了,加油~

ヾ(◍°∇°◍)ノ゙

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

榗榏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值