1. 程式人生 > >【Android開發—智慧家居系列】(四):UDP通訊傳送指令

【Android開發—智慧家居系列】(四):UDP通訊傳送指令

【Android開發—智慧家居系列】(四):UDP通訊傳送指令

 

思路回顧

【1】手機連線WIFI模組 
【2】UDP通訊對WIFI模組傳送指令,以和WIFI模組保持連線狀態 
【3】UDP通訊對WIFI模組傳送指令,讓其搜尋可用的無線網,返回WIFI列表 
【4】傳送指令,讓WIFI模組接入指定路由 
【5】手機連線路由 
【6】傳送指令,獲得WIFI模組的動態IP地址

UDP通訊執行緒類

package com.jczb.smartlife.common;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

import android.os.Handler;
import android.os.Message;

import com.jczb.smartlife.common.Tool;

public class GetInfoThread extends Thread {
    private Handler handler;
    private DatagramSocket socket;
    private int msgType;

    private final String IP = "255.255.255.255";//廣播地址
    private int PORT = 26000;

    /**
     * 48899埠:C32x系列的埠,使用者可以用AT指令更改
     * 49000埠:除C32x系列,其他WIFI模組的埠
     * 1902埠:有人掌控寶系列產品的埠
     */
    private int targetPort = 49000 ;

    private boolean receive = true;


    /**
     * 
     * @param handler 傳入監聽此執行緒的Handler
     * @param intMsg 傳入監聽的訊息型別
     */
    public GetInfoThread(Handler handler,int msgType) {
        this.handler = handler;
        this.msgType=msgType;
        init();
    }

    public void init(){
        try {

            socket = new DatagramSocket(null);
            socket.setBroadcast(true);
            socket.setReuseAddress(true);
            socket.bind(new InetSocketAddress(PORT));
        } catch (SocketException e) {
            e.printStackTrace();
            sendErrorMsg("Search Thread init fail");
            return;
        } 
    }


    public void run() {
        if (socket == null) {
            return;
        }

        try {

            byte[] data = new byte[1024];
            //建立一個空的DatagramPacket物件
            DatagramPacket revPacket = new DatagramPacket(data, data.length);
            while (receive) {
                //服務端接收資料
                socket.receive(revPacket);
                if(null!=handler){
                    byte[] realData = new byte[revPacket.getLength()];
                    System.arraycopy(data, 0, realData,0, realData.length);
                    Message msg =handler.obtainMessage(msgType,realData);
                    handler.sendMessage(msg);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            socket.close();
        }
    }

    public void close() {
        if (socket == null)
            return;
        socket.close();
    }


    private void sendErrorMsg(String info){

    }

    /**
     * 傳送資料
     * @param msg
     */
    public void sendMsg(byte[] msg) {
        if (socket != null) {
            try {
                System.out.println("targetPort------------------->"+targetPort);
                DatagramPacket sendPacket = new DatagramPacket(msg, msg.length,
                        InetAddress.getByName(IP), targetPort);
                socket.send(sendPacket);
            } catch (UnknownHostException e) {
                e.printStackTrace();
                System.out.println("傳送失敗");
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("傳送失敗");
            }

        }
    }

    public void setReceive(boolean receive) {
        this.receive = receive;
    }

    public void setTargetPort(int targetPort) {
        this.targetPort = targetPort;
    }
    public void setMsgType(int msgType){
        this.msgType=msgType;
    }
}

傳送訊息的執行緒類

/**
     * 傳送訊息的佇列,每次傳送資料時,只需要呼叫putMsg(byte[] data)方法
     * 
     * @author usr_liujinqi
     * 
     */
    private class SendMsgThread extends Thread {
        // 傳送訊息的佇列
        private Queue<byte[]> sendMsgQuene = new LinkedList<byte[]>();
        // 是否傳送訊息
        private boolean send = true;

        private GetInfoThread ss;

        public SendMsgThread(GetInfoThread ss) {
            this.ss = ss;
        }

        public synchronized void putMsg(byte[] msg) {
            // 喚醒執行緒
            if (sendMsgQuene.size() == 0)
                notify();
            sendMsgQuene.offer(msg);
        }

        public void run() {
            synchronized (this) {
                while (send) {
                    // 當佇列裡的訊息傳送完畢後,執行緒等待
                    while (sendMsgQuene.size() > 0) {
                        byte[] msg = sendMsgQuene.poll();
                        if (ss != null)
                            ss.sendMsg(msg);
                    }
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        public void setSend(boolean send) {
            this.send = send;
        }
    }

應用

【舉例】傳送搜尋WIFI模組的指令

//1.使用者獲得溫控器中WIFI模組的WIFI模組的IP地址,MAC地址,模組名稱的指令    
    private final byte[] getInfoCode=new byte[]{(byte)0x48,0x46,0x2D,0x41,0x31,0x31,0x41,0x53,0x53,0x49,0x53,0x54,0x48,0x52,0x45,0x41,0x44};
    public static final int REC_Module=0x04;//搜尋WIFI模組的資訊(包括IP、Mac、名稱)
//例項化一個執行緒,使用者獲得模組資訊(IP,Mac,名稱)
        //引數為監聽為此執行緒的Handler,以及接收成功後,給Handler傳送的訊息型別
        getInfoThread = new GetInfoThread(handler,Tool.REC_Module);
        getInfoThread.start();
        //傳送訊息的執行緒
        smt = new SendMsgThread(getInfoThread);
        smt.start();
        //設定傳送的目的埠號
        getInfoThread.setTargetPort(Integer.parseInt("48899"));     
        smt.putMsg(getInfoCode);

WIFI模組在接收到指令後,就會回覆資訊,以下的Handler就是針對回覆過來的資訊進行解析處理等操作。

//處理訊息的Handler
    private Handler handler= new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case Tool.REC_Module:// 解析接收到的資料
                //設定傳送的目的埠號
                getInfoThread.setTargetPort(Integer.parseInt("48899")); 
                getInfoThread.setMsgType(Tool.REC_OK);
                smt.putMsg(okCode);
                SetServer();
                SetDHCP();
                byte[] data = (byte[]) msg.obj;
                //將十進位制的資料轉化成十六進位制資料
                String strTemp= Tool.bytesToHexString(data);
                //將十六進位制的字串去掉空格之後進行解析
                String strdecode=Tool.DecodeHex(strTemp.replace(" ", ""));
                //取出IP,Mac地址,模組名稱
                decodeIP(strdecode);
                Toast.makeText(ConnectWifiActivity.this, "獲得WIFI模組名稱成功!"+ModuleName, Toast.LENGTH_SHORT).show();

                break;
            case Tool.REC_Server:
                byte[] dataServer = (byte[])msg.obj;
                //將十進位制的資料轉化成十六進位制資料
                String strServer= Tool.bytesToHexString(dataServer);
                if("2b 6f 6b 0d 0a 0d 0a ".equals(strServer)){
                    Toast.makeText(ConnectWifiActivity.this, "設定伺服器成功!", Toast.LENGTH_SHORT).show();

                }
                break;
            case Tool.REC_SSID:
                byte[] dataSSID=(byte[])msg.obj;
                Tool.bytesToHexString(dataSSID);
                decodeData(dataSSID);
                break;
            case Tool.REC_AT:
                byte[] dataID = (byte[]) msg.obj;
                //將十進位制的資料轉化成十六進位制資料
                String strTempID= Tool.bytesToHexString(dataID);
                //將十六進位制的字串去掉空格之後進行解析
                String strdecodeID=Tool.DecodeHex(strTempID.replace(" ", ""));
                break;
            case Tool.REC_DHCP:
                byte[] dataDHCP = (byte[])msg.obj;
                //將十進位制的資料轉化成十六進位制資料
                String strDHCP= Tool.bytesToHexString(dataDHCP);
                if("2b 6f 6b 0d 0a 0d 0a ".equals(strDHCP)){
                    Toast.makeText(ConnectWifiActivity.this, "設定DHCP網路引數成功!", Toast.LENGTH_SHORT).show();

                }
                break;
            default:
                byte[] data1 = (byte[]) msg.obj;
                //將十進位制的資料轉化成十六進位制資料
                String strTemp1= Tool.bytesToHexString(data1);
                Toast.makeText(ConnectWifiActivity.this, strTemp1, Toast.LENGTH_SHORT).show();
                break;
            }
        }
    };

總結

  凡是需要對WIFI模組傳送指令的,就需要用到上述的兩個執行緒類,還有一個對返回資訊進行處理的Handler。只是傳送的指令的code不一樣,如上述表示的是搜尋WIFI模組的十六進位制的指令。不管WIFI模組在AP模式下還是STA模式,通訊的最開始步驟都是先搜尋模組,然後獲得它的IP和Mac之後,立即回覆+ok指令,就可以保持連線狀態。