迅速上手android UDP控制網路開關量,以及常見坑
[目標]
透過android裝置,實現對某一開關量的控制,達到給電子門鎖通電開啟的目的。
網路開關量控制器如下:
本質上就是一個把乙太網協議轉換成對輸出開關量的動作的硬體模組。
[裝置]
- NR01 網路開關量控制器。含乙太網口一個
- 普通家用路由器,型號TP- LINK WR541G+(10年前的型號了)
- 商用android裝置一臺
[設計思路]
雖然開關量控制器提供ModBus協議,但是給android來控制,有點大材小用了。
直接用它提供的UDP模式,發個包控制繼電器輸出開一下,給門鎖訊號就行了。
根據網路開關量控制器報文,android裝置傳送一個UDP包on1:01 代表1號繼電器開發閉合1秒。
如果網路開發控制器收到了,就回應一個UDP包,內容是on1
分為一個UDP傳送執行緒和一個UDP接收執行緒。 傳送一次,就等待接收一會兒。收到on1就不再傳送UDP開鎖包了。超時收不到就再傳送一次。即使UDP經常丟包,在區域網裡,試3次到5次也夠了。
[實做]
UDP傳送很簡單,網上文章很多。接收也描述的很多,不過好多是描述對UDP支援多麼的不好。
這次用的是Android一個商用裝置,對系統篡改少一些。
[踩坑]
最主要的是DatagramSocket對傳送和接收來講一定只用一個例項
否則程式碼中UDP一個位元組都接收不到。
至於傳送和接收是1個執行緒還是2個執行緒,並不重要。
[程式碼]
ShopDoor.java
/*******************************************************************************
* Copyright 2018 Stephen Zhu
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.xxx.peripheral;
import com.xxx.UniCallBack;
import com.xxx.DebugUtil;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.channels.DatagramChannel;
import java.util.Date;
/**
* 門鎖開關
* <p>
* 開啟門鎖功能
* @author zhuguangsheng
*/
public class ShopDoor {
/*UDP方式向控制器傳送訊號*/
//傳送IP
private static String SEND_IP = "";
//傳送埠號
private static int SEND_PORT = 5000;
//傳送重試次數
private final static int SEND_RETRY_COUNT = 5;
//每次重試延時
private final static int SEND_RETRY_INTERVAL_MS = 2000;
//接收超時
private final static int RECEIVE_SO_TIMEROUT = 800;
/**
* 傳送執行緒的迴圈標識
*/
private static boolean sendFlag = true;
/**
* 接收執行緒的迴圈標識
*/
private static boolean listenStatus = true;
private static byte[] receiveInfo;
private static byte[] SENDBUF = "on1:01".getBytes();
private static byte[] RECEIVEOK = "on1".getBytes();
static UniCallBack sCallBack;
/**
* 傳送和接收共用一個DatagramSocket,實際試過,如果用2個,那接收不到UDP
*/
static DatagramSocket mUdpSocket;
/**
* 開門
* 非同步非阻塞呼叫,靠callback返回撥用成功或失敗
*
* @param sendIp
* @param sendPort
* @param callBack 回撥,成功或失敗
*/
public static void OpenDoor(String sendIp, int sendPort, final UniCallBack callBack) {
SEND_IP = sendIp;
SEND_PORT = sendPort;
sCallBack = callBack;
//讓傳送能迴圈
sendFlag = true;
//讓接收能迴圈
listenStatus = true;
//啟動接收執行緒
new UdpReceiveThread().start();
//稍等一會,讓接收執行緒穩定
try {
Thread.sleep(200);
} catch (Exception e) {
e.printStackTrace();
}
new UdpSendThread().start();
}
/**
* UDP資料傳送執行緒
*/
public static class UdpSendThread extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < SEND_RETRY_COUNT; i++) {
if(!sendFlag){
break;
}
InetAddress serverAddr;
//mUdpSocket = new DatagramSocket();
if(mUdpSocket==null){
mUdpSocket = new DatagramSocket(null);
mUdpSocket.setReuseAddress(true);
mUdpSocket.bind(new InetSocketAddress(SEND_PORT));
}
serverAddr = InetAddress.getByName(SEND_IP);
DatagramPacket outPacket = new DatagramPacket(SENDBUF, SENDBUF.length, serverAddr, SEND_PORT);
mUdpSocket.send(outPacket);
//在傳送這裡不close了,要不然接收的也被close了。統一到接收結束的地方close
//mUdpSocket.close();
Thread.sleep(SEND_RETRY_INTERVAL_MS);
}
//n次之後,還沒結果也別收了,算失敗。接收執行緒也停了算了
listenStatus = false;
} catch (Exception e) {
e.printStackTrace();
try {
sCallBack.onError("open door signal send error 開門傳送過程錯誤");
}catch (Exception e1){
DebugUtil.log("sCallBack exception");
}
}
}
}
/**
* UDP資料接收執行緒
*/
public static class UdpReceiveThread extends Thread {
@Override
public void run() {
while (listenStatus) {
try {
DatagramChannel channel = DatagramChannel.open();
if (mUdpSocket == null) {
mUdpSocket = channel.socket();
mUdpSocket.setReuseAddress(true);
}
//serverAddr = InetAddress.getByName(SEND_IP);
byte[] inBuf = new byte[1024];
DatagramPacket inPacket = new DatagramPacket(inBuf, inBuf.length);
mUdpSocket.setSoTimeout((int) (RECEIVE_SO_TIMEROUT));
DebugUtil.log("before receive start time " + System.currentTimeMillis());
mUdpSocket.receive(inPacket);
/**
if (!inPacket.getAddress().equals(serverAddr)) {
//throw new IOException("未知名的報文");
DebugUtil.log("未知地址的報文");
}
*/
receiveInfo = inPacket.getData();
String receiveInfoStr = new String(receiveInfo);
String receiveOk = new String(RECEIVEOK);
DebugUtil.log("receiveInfo=" + receiveInfoStr);
if (receiveInfoStr.contains(new String(receiveOk))) {
sendFlag = false;
listenStatus = false;
try {
sCallBack.onSuccess(receiveOk);
}catch (Exception e1){
DebugUtil.log("sCallBack exception");
}
//不再接收了
} else {
//可能是別的指令的回覆,忽略它行了,繼續迴圈
continue;
}
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
DebugUtil.log("after receive exception time " + System.currentTimeMillis());
try {
sCallBack.onError("接收過程錯誤" + e.toString());
}catch (Exception e1){
DebugUtil.log("sCallBack exception");
}
}
}
//嘗試清除工作
try{
mUdpSocket.close();
mUdpSocket = null;
}catch (Exception e){
e.printStackTrace();
}
//接收執行緒結束
}
}
}
另:用到的UniCallBack只有以下幾行,是自定義的介面
UniCallBack.java
public interface UniCallBack {
public void onSuccess(String msg);
public void onError(String err);
}
而DebugUtil.java也可以簡寫為以下內容:
public class DebugUtil {
/**
* 輸出一行log
* @param level
* @param msg
*/
private static void log(Level level, String msg){
Log.i("debuglog", msg);
}
}
結束
歡迎指出問題,歡迎各種合作聯絡。
相關文章
- JRebel熱部署迅速上手熱部署
- 25個常見網路
- Android技能樹 — 網路小結(2)之TCP/UDPAndroidTCPUDP
- 常見的網路協議協議
- LINUX 網路管理常見命令Linux
- Android Studio中的一些常見控制元件Android控制元件
- 常見網際網路分析指標指標
- 小程式開發常見踩坑系列(下)
- Linux 網路開發常見面試題回顧Linux面試題
- UDP網路測試UDP
- udp網路通訊UDP
- 常見網路協議彙總協議
- 常見網路測試命令使用
- 常見網路攻擊:XSS 篇
- keepalived 1.3.5常見配置以及常見問題解決
- 頁面的修改、新增,以及驗證控制元件的常見應用控制元件
- 淺談TCP、UDP、ICMP三種常見協議TCPUDP協議
- 常見的網路協議彙總協議
- 常見的網路攻擊型別型別
- 網路通訊1:UDPUDP
- 快速上手Linux核心命令(八):網路相關命令Linux
- 網路安全(一):常見的網路威脅及防範
- 網路安全常見問題有哪些?網路安全學習
- 網路爬蟲之關於爬蟲 http 代理的常見使用方式爬蟲HTTP
- Android Studio常見問題(+)Android
- 常見的網路安全威脅詳解!
- 網路安全常見面試題-Web方向面試題Web
- 網路爬蟲編寫常見問題爬蟲
- 常見迴圈神經網路結構神經網路
- 聲網 Token 鑑權機制,以及常見的問題
- 瞭解常見網路攻擊方式,做好網路安全防範!
- Linux中如何排查網路?常見的網路排查指令都有哪些?Linux
- 變數命名以及常見錯誤變數
- Android Oreo 常見問題 2.0 | Android 開發者 FAQ Vol.9Android
- Android Oreo 常見問題 3.0 | Android 開發者 FAQ Vol.11Android
- Android小知識-剖析Retrofit中的網路請求流程以及相關引數Android
- 虛擬機器常見的網路型別有哪些?linux網路虛擬機型別Linux
- UDP和TCP以及HTTPUDPTCPHTTP