【Android開發—智慧家居系列】(四):UDP通訊傳送指令
阿新 • • 發佈:2018-11-07
【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指令,就可以保持連線狀態。