1. 程式人生 > >socket實現TCP通訊_TCP連線android與微控制器(2016_03_21)

socket實現TCP通訊_TCP連線android與微控制器(2016_03_21)

最近做了一個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<SelectionKey> 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<String> 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資料處理。