socket框架netty-socket android客户端

作者: 阴天吃鱼 | 来源:发表于2018-06-20 17:05 被阅读45次

    之前自己写了socket建立的连接,但是涉及到一些需要完善的地方,表示自己有点懒。然后就在网上找了一个socket框架, 拿来直接用。


    hh 你好懒

    阅读测试后,个人认为NettyClient 主要分为几个部分:
    1,异步端口连接 Bootstrap
    2,NettyClient的解码方式等 pipeline
    3,配套的HandlerAdapter : ChannelInboundHandlerAdapter等。
    4,计算断开后重新调用连接。

    提供一下jar包下载,不知道怎么设置免费下载,没分的可以网上自行找资源,资源一堆的说! 我就直接上代码了,上的代码都是简单的封装了一下的。使用的时候直接调用指定的方法就可以。 (这字一放大,变色的字就没了,真是不知道怎么用!)

    • 我先把定义的接口写了,下面的代码里面有用到。
    public interface NettyListener {
    
        public final static byte STATUS_CONNECT_SUCCESS = 1;//连接成功
    
        public final static byte STATUS_CONNECT_CLOSED = 0;//关闭连接
    
        public final static byte STATUS_CONNECT_ERROR = 0;//连接失败
    
    
        /**
         * 当接收到系统消息
         */
        void onMessageResponse(Object msg);
    
        /**
         * 当连接状态发生变化时调用 
         */
        public void onServiceStatusConnectChanged(int statusCode);
    }
    
    • NettyClient 类的书写
    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("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));//服务端最后以"\n"作为结束标识
    //                                ch.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8));//解码
    //                                ch.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8));//解码
                                    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();
                }
            }
        }
    
        //发送消息到服务端。 Bootstrap设置的时候我没有设置解码,这边才转的
        public boolean sendMsgToServer(String data, ChannelFutureListener listener) {
            boolean flag = channel != null && isConnect;
            if (flag) {
                ByteBuf byteBuf = Unpooled.copiedBuffer(data + System.getProperty("line.separator"), //2
                        CharsetUtil.UTF_8);
                channel.writeAndFlush(byteBuf).addListener(listener);
            }
            return flag;
        }
    
        //重连时间
        public void setReconnectNum(int reconnectNum) {
            this.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;
        }
    
    }
    
    
    累成狗!.png

    好累,感觉我要睡着了。
    我要原谅我太菜,没有时间休息,公司开展的新项目的技术点不会,哈哈,没办法。。。太菜,抓把劲攻克!

    • netty socket 配套的handlerAdapter,直接上代码直接上
    public class NettyClientHandler extends ChannelInboundHandlerAdapter {
    
        private static final String TAG = "NettyClientHandler";
        private NettyListener listener;
    
        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("客户端开始读取服务端过来的信息");
            listener.onMessageResponse(msg);
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            // 当引发异常时关闭连接。
            Log.e(TAG, "exceptionCaught");
            listener.onServiceStatusConnectChanged(NettyListener.STATUS_CONNECT_ERROR);
            cause.printStackTrace();
            ctx.close();
        }
    
    }
    

    我觉得我注释写的还挺多的,哈哈,
    对了,还有个定义的host和port类。虽然就几行,但我还是决定写上

    public class Const { //百度找的markdown语法框框,表示有点丑
    public static final String HOST = "192.168.0.46";
    public static final int TCP_PORT = 5212;
    public static final int LIVE_PORT = 5500;
    }

                • 最重要的来袭了, 使用!

    布局没什么东西, 就是个Button发送消息的,连接我写成进入app后默认连接了,失败和成功会有提示的昂, 失败了会一直重连。

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <Button
            android:id="@+id/btn_send"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="发送消息" />
    </LinearLayout>
    
    

    其他核心的使用代码:!!!!

    public class MainActivity extends BaseActivitiy implements NettyListener {
    
        private Button btn_send;//发送
        private NettyClient nettyClient;//socket操作连接对象
        private final String TAG = "MainActivity.class";
    
        @Override
        protected int getContentView() {//这我自己的基类抽象的
            return R.layout.activity_main;
        }
    
        @Override
        protected void initView() {//这我自己的基类抽象的
            initSocketTcp();//默认自动连接socket
    
            btn_send = findViewById(R.id.btn_send);//发送
            btn_send.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (!nettyClient.getConnectStatus()) {//获取连接状态,必须连接才能点。
                        Toast.makeText(MainActivity.this, "先连接", Toast.LENGTH_SHORT).show();
                    } else {
                        JSONObject jsonObject = new JSONObject();//传个jsonobject给服务器
                        long l = System.currentTimeMillis();
                        try {
                            jsonObject.put("msgType", "infomation");
                            jsonObject.put("msgValue", "status");
                            jsonObject.put("msgTime", l + "");
    
                        } catch (JSONException e) {
                            e.printStackTrace();
                        }
                        /*
                        调用的发送。
                         */
                        nettyClient.sendMsgToServer(jsonObject.toString(), new ChannelFutureListener() {
                            @Override
                            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                                if (channelFuture.isSuccess()) {                //4
                                    Log.d(TAG, "Write auth successful");
                                } else {
                                    Log.d(TAG, "Write auth error");
                                }
                            }
                        });
                    }
                }
            });
        }
    
        /*
         socket 端口号以及开始连接,配置接口监听
         */
        private void initSocketTcp() {
            nettyClient = new NettyClient(Const.HOST, Const.TCP_PORT);
            if (!nettyClient.getConnectStatus()) {
                nettyClient.setListener(MainActivity.this);
                nettyClient.connect();
            } else {
                nettyClient.disconnect();
            }
        }
    
    
        /*
           回调客户端接收的信息  解析 数据流, 转换为string
           */
        @Override
        public void onMessageResponse(final Object msg) {
            ByteBuf result = (ByteBuf) msg;
            byte[] result1 = new byte[result.readableBytes()];
            result.readBytes(result1);
            result.release();
            final String ss = new String(result1);
    
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
    
                    Toast.makeText(MainActivity.this, "接收成功" + ss, Toast.LENGTH_SHORT).show();
                }
            });
        }
    
        //连接状态变化的时候 会走这
        @Override
        public void onServiceStatusConnectChanged(final int statusCode) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    if (statusCode == NettyListener.STATUS_CONNECT_SUCCESS) {
                        Log.e(TAG, "STATUS_CONNECT_SUCCESS:");
                        if (nettyClient.getConnectStatus()) {
                            Toast.makeText(MainActivity.this, "连接成功", Toast.LENGTH_SHORT).show();
                        }
                    } else {
                        Log.e(TAG, "onServiceStatusConnectChanged:" + statusCode);
                        if (!nettyClient.getConnectStatus()) {
                            Toast.makeText(MainActivity.this, "网路不好,正在重连", Toast.LENGTH_SHORT).show();
                        }
                    }
                }
            });
        }
    }
    

    基本就这些了,我觉得我写非常清楚, 按照步骤的话直接就可以实现,
    如果想知道到底能不能使用,能不能行。
    可以使用工具..等等,肯定是能用的,好吗???!

    哈哈,模拟服务端的话,可以下载个 ,windows还是mac都有,网上找就有,我没有提供链接。

    sokit
    sokit.png

    好了好了,可算写完了,技术太菜望见谅,我是一名又懒又菜的猿。

    是我.png

    相关文章

      网友评论

      • d3b602e70cc9:app后台,加了白名单,socket也会断开,只有插着数据线,锁屏后才一直不断开
        阴天吃鱼:推送唤醒
      • Sun_丶:大神 感谢分享 让我好好学习了一把

      本文标题:socket框架netty-socket android客户端

      本文链接:https://www.haomeiwen.com/subject/insxyftx.html