socket实现TCP通信_TCP连接android与单片机(2016_03_21)

2019-04-15 18:12发布

最近做了一个TCP连接android与单片机的项目。记录一下。 TCP连接对象
首先是TCP连接,以前没有做过,摘抄的同事项目中TCP连接对象。
public class TCPChannel {
    private SocketChannel myChannel = null;
    private boolean isConnecting = true;
    private boolean isConnected = false;
    // 发送心跳包的线程是否已经关闭
    private boolean isSendHeartBreak = false;
    private Selector selector = null;
    private Iterator iterator;
    private SelectionKey key;
    private String mac = "";
    private SocketAddress address;
    private int timeOut;
    private Thread heartbeat;

    private int heartCunt = 0;
    private boolean isWaitHeart = false;

    public TCPChannel(SocketChannel socketChannel, SocketAddress address,
            int timeOut) {
        // TODO Auto-generated constructor stub
        myChannel = socketChannel;
        this.address = address;
        this.timeOut = timeOut;
        try {
            LogUtil.e("selector open is will run open!!");
            selector = Selector.open();
        } catch (IOException e) {
            LogUtil.e("Selector.open() is wrong XXXX");
            e.printStackTrace();
        }
    }

    public void connect() throws IOException, NullPointerException {
        LogUtil.e("connect is be runned!!");

        isConnecting = true;
        isConnected = false;
        System.out.println(myChannel.toString());
        myChannel.configureBlocking(false);
        if (selector == null)
            throw new NullPointerException(mac);
        myChannel.register(selector, SelectionKey.OP_CONNECT
                | SelectionKey.OP_READ | SelectionKey.OP_WRITE);
        myChannel.connect(address);

        // System.out.println("检查TCP连接:" + checkConnect());
        if (checkConnect()) {
            LogUtil.e("checkConnect return true");
        } else {
            this.close();
            LogUtil.e("checkConnect return false");

        }
    }

    public void reConnect() {
        Log.w("重练空调", "准备开始");
        if (!isConnected) {
            return;
        }
        isConnecting = true;
        isConnected = false;
        if (heartbeat != null) {
            heartbeat.interrupt();
        } else {
            System.out.println("重连接时候心跳为空");
            // sendHeartbeat();
        }
        try {
            Log.w("重练空调", "关闭连接");
            myChannel.socket().close();
            myChannel.close();
            selector.close();
            myChannel = SocketChannel.open();
            try {
                selector = Selector.open();
            } catch (IOException e) {
                Log.e("----->", "Selector.open()错误2");
                e.printStackTrace();
            }
            Log.w("重练空调", "正在重连");
            try {
                connect();
            } catch (NullPointerException e) {
                isConnected = true;
                Log.e("----->", e + "抛出空指针异常33");
            }

            if (!isConnected) {
                Log.w("重练空调", "没有连上");

                close();
            } else {
                Log.w("重练空调", "重连成功");
                Log.i("----->12.4", "重连成功?" + heartCunt + " "
                        + isSendHeartBreak);

                // 在断开wifi的情况下,心跳线程异常会从而终止,此判断是如果心跳因为异常而终止则重新开启心跳
                if (heartCunt < 3) {
                    if (isSendHeartBreak) {
                        // sendHeartbeat();
                    }
                } else {
                    close();
                }
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ConcurrentModificationException e) {
            e.printStackTrace();
            close();
        }
    }

    public boolean checkConnect() {
        isConnected = false;
        try {
            Thread.sleep(timeOut * 1000);

        } catch (InterruptedException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        try {
            LogUtil.e("check TCP connection is:::-->" + myChannel
                    + "                   " + myChannel.finishConnect()
                    + "            " + myChannel.isConnected());
            if (myChannel != null && myChannel.finishConnect()
                    && myChannel.isConnected()) {

                LogUtil.e(" not null 重连的时候?");
                isConnected = true;
            }
        } catch (Exception e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }
        isConnecting = false;
        return isConnected;
    }

    public void send(byte[] bytes) {
        // TODO Auto-generated method stub
        if (!isConnected || isConnecting) {
            return;
        }
        System.out.println("bytes  " + bytes + "myChannel  " + myChannel);
        try {
            ByteBuffer buf = ByteBuffer.wrap(bytes);
            myChannel.write(buf);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public int sendUpdateDate(byte[] bytes) {
        // TODO Auto-generated method stub
        if (!isConnected || isConnecting) {
            return -1;
        }
        System.out.println("bytes  " + bytes + "myChannel  " + myChannel);
        try {
            ByteBuffer buf = ByteBuffer.wrap(bytes);
            myChannel.write(buf);
            return 0;
        } catch (IOException e) {

            e.printStackTrace();
            return -2;
        }
    }

    public void receive(Handler mainHandler) throws ParseException {

        // LogUtil.e("in receive ...");
        if (!isConnected || isConnecting) {
            return;
        }
        // LogUtil.e("in receive 校验之后 ");
        try {
            selector.select(100);
            if (selector != null && selector.isOpen()) {
                // LogUtil.e("selector校验  selector != null && selector.isOpen()");
                iterator = selector.selectedKeys().iterator();
            } else {
                return;
            }
            while (iterator.hasNext()) {
                // LogUtil.e("进入while循环内, iterator.hasNext()");
                key = iterator.next();
                // LogUtil.e("key == "+key.toString());
                // 删除正在处理的SelectionKey
                iterator.remove();
                // 如果该SelectionKey对应的Channel中有可读的数据
                // 测试此键的通道是否已准备好接受新的套接字连接。
                ServerSocketChannel server = null;
                SocketChannel client = null;
                if (key.isReadable()) {
                    LogUtil.e("key.isReadable() 为true了  有可读数据");
                    // 使用NIO读取Channel中的数据
                    SocketChannel sc = (SocketChannel) key.channel();
                    // ByteArrayOutputStream buff = new
                    // ByteArrayOutputStream(1024 );//缓冲
                    // ByteArrayOutputStream有toByteArray()方法 直接得到数据
                    ByteBuffer buff = ByteBuffer.allocate(256);
                    String content = "";
                    int i = 0;
                    // //161 23.97
                    while ((i = sc.read(buff)) > 0) {
                        sc.read(buff);
                        buff.flip();
                        byte[] bytes = new byte[i];
                        buff.get(bytes);
                        switch (i) {
                        case 97:// 1-46
                            BatteryInfo batteryInfo = SaveUtil
                                    .saveBatteryInfo(bytes);
                            if(batteryInfo!=null){
                                Message msg = new Message();
                                Bundle b = new Bundle();// 存放数据
                                b.putSerializable("BatteryInfo",
                                        (Serializable) batteryInfo);
                                msg.setData(b);
                                msg.what= 2;
                                LogUtil.e("发送数据97的、、、");
                                mainHandler.sendMessage(msg);
                            }
                            // 1.bytes,先截取前三个,后二个。留中间数据.返回三个数组
                            // 2。数据分开保存至bean中
                            // 2.1前三个直接把无符号byte转为int存储。
                            // 2.2中间数组两个合并一个,合并后,获取到short的无符号int值存储bean
                            // 2.2.1 数据格38-43四个合成一个转成long存储bean中。
                            // 2.3后两个变成16进制存储到CRC高低字节中。
//                            LogUtil.e(batteryInfo.toString());
                            break;
                        case 161:// 80-158

                            break;
                        case 25:// 60-69
                            BatteryWarmInfo saveBatteryWarmInfo = SaveUtil
                                    .saveBatteryWarmInfo(bytes);
//                            LogUtil.e(saveBatteryWarmInfo.toString());
                            // 判断saveBatteryWarmInfo.警告信息集合长度,如果有数据,更新界面
                            if (saveBatteryWarmInfo.WarmInfos.size() > 0) {
                                // TODO发送消息到 MainUIThread 线程更新界面??
                                // mainHandler.
                                Message msg = new Message();
                                Bundle b = new Bundle();// 存放数据
                                b.putSerializable("warmInfos",
                                        (Serializable) saveBatteryWarmInfo);
                                msg.setData(b);
                                msg.what= 1;
                                LogUtil.e("发送数据 25的、、、");
                                mainHandler.sendMessage(msg);
                                
                            }
                            break;

                        default:
                            LogUtil.e("获取到未知长度数据!请复查代码");
                            break;
                        }

                        // for (int x = 0; x < i; x++) {
                        // LogUtil.e(Byte.toString(bytes[x]));
                        // }
                        buff.clear();
                    }

                    key.interestOps(SelectionKey.OP_READ);
                    if (i == -1) {
                        reConnect();
                    }
                }

            }
            // LogUtil.e("跳出while循环, iterator.hasNext()");
        } catch (IOException e1) {
            e1.printStackTrace();
        }

    }

    public void close() {
        isConnecting = true;
        isConnected = false;
        selector = null;
        mac = null;
        try {
            myChannel.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (NullPointerException e) {
            Log.w("TCP关闭", "空指针");
        }
        myChannel = null;
    }

    public String getMac() {
        return mac;
    }

    public boolean isConnected() {
        return isConnected;
    }

    // private void ProcessReply(String id, List strs) {
    // JsonParser parser = new JsonParser();
    // for (String str : strs) {
    //
    // System.out.println("str====:" + str);
    // // 改用正则匹配
    // if (str.matches("\{.*\}")) {
    //
    // System.out.println("没有问题啊!!!!!!!!!!!!!!!!");
    //
    // JsonElement jsonEl = parser.parse(str);
    // JsonObject jsonObj = null;
    // jsonObj = jsonEl.getAsJsonObject();
    // JsonElement je;
    // if ((je = jsonObj.get("response_type")) != null
    // && je.getAsInt() == 2) {
    // heartCunt = 0;
    // isWaitHeart = false;
    // } else {
    // Control.receivedPackets(gApp, jsonObj, TCPChannel.this);
    // }
    // } else {
    // System.out.println(id + "回码有问题啊!!!!!!!!!!!!!!!!" + str);
    // GreeApplication.baseActivity.showToast(id + "局域网回码错误");
    // }
    // }
    // }

    // public void sendHeartbeat() {
    // isSendHeartBreak = false;
    // heartbeat = new Thread(mac + "心跳") {
    // public void run() {
    // while (isConnected && !isConnecting) {
    // try {
    // Log.i("心跳包",
    // "心跳包发送"
    // + mac
    // + "       "
    // + JsonUtil.toJsonString(new Heartbeat(
    // GreeTime.getTimestamp())));
    //
    // String heartPacket = JsonUtil
    // .toJsonString(new Heartbeat(GreeTime
    // .getTimestamp())); // 加密
    //
    // String key = GreeApplication.LANKeymap.get(mac);
    // if (key == null) {
    //
    // key = "fbaef480";
    // }
    //
    // try {
    // heartPacket = Des.encryptDES(heartPacket, key,
    // false);
    // } catch (Exception e1) {
    // // TODO Auto-generated catch block
    // e1.printStackTrace();
    // }
    //
    // System.out.println("=================心跳des加密:" + key);
    // send(HeadUtil.addHead(heartPacket));
    // isWaitHeart = true;
    // sleep(10 * 1000);
    // if (isWaitHeart && heartCunt++ > 1) {
    // // 网络断开操作
    // Log.i("心跳包", "心跳包检测到网络断开" + mac);
    // reConnect();
    // }
    // } catch (UnsupportedEncodingException e) {
    // Log.i("----->12.4", "1:heart  error " + e.toString());
    // isSendHeartBreak = true;
    // e.printStackTrace();
    // } catch (InterruptedException e) {
    // Log.i("----->12.4", "2:heart  error " + "num is "
    // + heartCunt + "   " + e.toString());
    // isSendHeartBreak = true;
    // e.printStackTrace();
    // }
    // }
    // }
    // };
    // // ScheduledThreadPoolExecutor scheduler = new
    // // ScheduledThreadPoolExecutor(1);
    // // scheduler.schedule(heartbeat, 1,TimeUnit.SECONDS);
    // heartbeat.start();
    // }

    // private void checkTiming() throws ParseException {
    // // TODO Auto-generated method stub
    // DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",
    // Locale.getDefault());
    // Date _date = new Date();
    //
    // String time_str = df.format(_date);
    //
    // System.out.println("定时现在时间:" + time_str);
    // if (AirCtrlDB.mPowerOnTimer != null) {
    // if (AirCtrlDB.mPowerOnTimer.bTimerOn) {
    // // System.out.println("//ON ");
    // time_str = time_str.substring(0, 11)
    // + AirCtrlDB.mPowerOnTimer.mHours + ":"
    // + AirCtrlDB.mPowerOnTimer.mMin + ":00";
    // long timeInterval = GreeTime.getInstance().timeIntervalNow(
    // time_str);
    // //
    // Log.i("----->12.4","在定时开机的循环中:"+AirCtrlDB.mPowerOnTimer.mfireMode+"   "+Math.abs(timeInterval));
    // if (Math.abs(timeInterval) < 4000) {
    //
    // System.out.println("//on < 36 ");
    // boolean _Execution = true;
    // switch (AirCtrlDB.mPowerOnTimer.mfireMode) {
    // case Timer_Mode_Once:
    // AirCtrlDB.mPowerOnTimer.bTimerOn = false;
    // break;
    // case Timer_Mode_WorkDay:
    // if (ByteOrder.getDay(_date) < 6) {
    //
    // } else {
    // _Execution = false;
    // }
    // break;
    // }
    // if (_Execution) {
    // // 开机
    // Log.i("----->12.4", "开机");
    // System.out.println("//on //开机 ");
    // ByteOrder.setpoweronView();
    // }
    //
    // }
    //
    // }
    // }
    //
    // if (AirCtrlDB.mPowerOffTimer != null) {
    // if (AirCtrlDB.mPowerOffTimer.bTimerOn) {
    // // System.out.println("//Off ");
    // /*
    // * DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    // * Date _date = new Date(); String time_str = df.format(_date);
    // */
    // time_str = time_str.substring(0, 11)
    // + AirCtrlDB.mPowerOffTimer.mHours + ":"
    // + AirCtrlDB.mPowerOffTimer.mMin + ":00";
    // long timeInterval = GreeTime.getInstance().timeIntervalNow(
    // time_str);
    // //
    // Log.i("----->12.4","在定时关机的循环中:"+AirCtrlDB.mPowerOffTimer.mfireMode+"   "+Math.abs(timeInterval));
    // if (Math.abs(timeInterval) < 4000) {
    // System.out.println("//off < 36 ");
    // boolean _Execution = true;
    // switch (AirCtrlDB.mPowerOffTimer.mfireMode) {
    // case Timer_Mode_Once:
    // AirCtrlDB.mPowerOffTimer.bTimerOn = false;
    // break;
    // case Timer_Mode_WorkDay:
    // if (ByteOrder.getDay(_date) < 6) {
    //
    // } else {
    // _Execution = false;
    // }
    // break;
    // }
    //
    // if (_Execution) {
    // // 关机
    // System.out.println("//off //关机 ");
    // ByteOrder.setpowerOffView();
    // }
    // }
    // }
    // }
    //
    // }

}
此类的几个主要方法简单说明下: 1.TCPChannel(SocketChannel socketChannel, SocketAddress address,int timeOut) 初始化对象,SocketChannel,SocketAddress  java api对象。 2.connect() 连接方法.内部调用了SocketChannel的register(selector, SelectionKey.OP_CONNECT| SelectionKey.OP_READ | SelectionKey.OP_WRITE);和connect(address); 3.reConnect() 重连socket, 4.send(byte[] bytes) 发送byte数组数据。 5.receiver(); 数据接收。 调用方式: 先执行,接收数据的循环,接收到数据,就发送给主线程的handler jieshou = new jieshou("TCP循环检测接收");
        jieshou.start();
private class jieshou extends Thread {
        public jieshou(String string) {
            this.setName(string);
        }

        @Override
        public void run() {
            super.run();
            LogUtil.e("接收线程run方法中 ");

            while (true) {
                try {
                    if (tcpChannel != null) {                         tcpChannel.receive(mainHandler);

                    }
                    sleep(100);
                } catch (Exception exception) {
                    exception.printStackTrace();
                }
            }
        }
    }
然后,连接通道,发送数据。
address = new InetSocketAddress(ipAddress, 8088);
            flag = true;
            // 建立TCP连接通道

            thread = new Thread() {

                public void run() {
                    try {
                        tcpChannel = new TCPChannel(SocketChannel.open(),
                                address, 2);
                        tcpChannel.connect();
                        while (flag) {
                            LogUtil.e("while true循环着  去请求数据。。。。");
                            try {
                                tcpChannel.send(getSendData(1, 46));
                                sleep(500);
                                tcpChannel.send(getSendData(60, 10));

                                sleep(2500);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                                LogUtil.e("while 里抛出异常。");
                            }
                        }

                        LogUtil.e("tcp 请求数据,循环结束了。。。。。");
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            };
            thread.start(); 断网后,重新连接: if (tcpChannel != null)
     tcpChannel.reConnect(); TCP连接就完毕了。之后是CRC16校验和byte数据处理。