可靠udp初步实现

This commit is contained in:
2025-07-28 19:48:41 +08:00
parent b810e6c2fb
commit 6cdb19d8a0
3 changed files with 316 additions and 162 deletions

View File

@@ -1,38 +1,59 @@
package com.pinappletech.android.main;
import android.app.Service;
import android.os.IBinder;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.pm.ServiceInfo;
import android.os.Handler;
import android.os.Looper;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import android.os.Build;
import android.app.NotificationChannel;
import android.app.Notification;
import android.content.Context;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.unity3d.player.UnityPlayer;
public class Main extends Service {
private static final String TAG = "PineappleService";
private Handler handler = new Handler(Looper.getMainLooper());
private Runnable logRunnable = new Runnable() {
@Override
public void run() {
Log.i("PineappleService", "服务正在运行...");
handler.postDelayed(this, 5000);
}
};
// ================= 可靠UDP参数 =================
private static final int MAX_RETRIES = 3;
private static final int RETRY_INTERVAL = 200; // 重传间隔(ms)
private static final int ACK_TIMEOUT = 300; // ACK超时(ms)
private static final int BUFFER_SIZE = 1024;
// 序列号计数器(线程安全)
private int sequenceCounter = 0;
private final Object sequenceLock = new Object();
// 待确认消息 <序列号, 消息数据>
private final Map<Integer, PendingMessage> pendingMessages = new ConcurrentHashMap<>();
// 接收缓冲区 <序列号, 消息内容>
private final Map<Integer, String> receiveBuffer = new ConcurrentHashMap<>();
private int expectedSequence = 0; // 下一个期望序列号
// 线程池
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private final ExecutorService networkExecutor = Executors.newFixedThreadPool(2);
// ================= 服务生命周期 =================
@Override
public IBinder onBind(Intent intent) {
return null;
@@ -40,8 +61,9 @@ public class Main extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("PineappleService", "菠萝服务启动");
Log.i(TAG, "菠萝服务启动");
// 创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
"pineapple_channel",
@@ -63,18 +85,252 @@ public class Main extends Service {
startForeground(1, notification);
}
handler.post(logRunnable);
// 启动UDP接收器
startUdpReceiver();
// 启动重传检查任务
scheduler.scheduleAtFixedRate(this::checkPendingMessages,
RETRY_INTERVAL, RETRY_INTERVAL, TimeUnit.MILLISECONDS);
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i("PineappleService", "菠萝服务停止");
Log.i(TAG, "菠萝服务停止");
// 清理资源
isRunning = false;
scheduler.shutdownNow();
networkExecutor.shutdownNow();
if (udpSocket != null && !udpSocket.isClosed()) {
udpSocket.close();
}
}
// ================= 可靠UDP核心实现 =================
private static class PendingMessage {
byte[] data;
InetAddress targetAddress;
int targetPort;
int retryCount;
long lastSentTime;
PendingMessage(byte[] data, InetAddress targetAddress, int targetPort) {
this.data = data;
this.targetAddress = targetAddress;
this.targetPort = targetPort;
this.retryCount = 0;
this.lastSentTime = System.currentTimeMillis();
}
}
// 重传检查
private void checkPendingMessages() {
long now = System.currentTimeMillis();
pendingMessages.forEach((seq, msg) -> {
if (now - msg.lastSentTime > RETRY_INTERVAL && msg.retryCount < MAX_RETRIES) {
networkExecutor.execute(() -> {
try {
DatagramPacket packet = new DatagramPacket(
msg.data, msg.data.length, msg.targetAddress, msg.targetPort);
udpSocket.send(packet);
msg.retryCount++;
msg.lastSentTime = now;
Log.d(TAG, "重发消息 seq=" + seq + ", 重试次数=" + msg.retryCount);
} catch (Exception e) {
Log.e(TAG, "重发失败: " + e.getMessage());
}
});
} else if (msg.retryCount >= MAX_RETRIES) {
pendingMessages.remove(seq);
Log.w(TAG, "消息 seq=" + seq + " 达到最大重试次数");
}
});
}
// 处理接收到的消息
private void handleReceivedMessage(String message, InetAddress senderAddress, int senderPort) {
// 解析消息头
if (message.startsWith("MSG:") && message.length() > 5) {
int colonIndex = message.indexOf(':', 4);
if (colonIndex != -1) {
try {
int seq = Integer.parseInt(message.substring(4, colonIndex));
String content = message.substring(colonIndex + 1);
// 处理ACK消息
if (content.startsWith("ACK/")) {
handleAck(seq);
return;
}
// 处理普通消息
handleDataMessage(seq, content, senderAddress, senderPort);
return;
} catch (NumberFormatException e) {
Log.e(TAG, "无效序列号: " + e.getMessage());
}
}
}
// 旧格式消息处理(兼容)
Log.i(TAG, "收到旧格式消息: " + message);
handleLegacyMessage(message, senderAddress, senderPort);
}
private void handleAck(int ackSeq) {
// 从待确认字典中移除
pendingMessages.remove(ackSeq);
Log.d(TAG, "收到ACK确认 seq=" + ackSeq);
}
private void handleDataMessage(int seq, String content, InetAddress senderAddress, int senderPort) {
// 发送ACK确认
sendAck(seq, senderAddress, senderPort);
// 处理乱序消息
receiveBuffer.put(seq, content);
// 按顺序处理缓冲的消息
while (receiveBuffer.containsKey(expectedSequence)) {
String bufferedMsg = receiveBuffer.remove(expectedSequence);
processValidMessage(bufferedMsg, senderAddress);
expectedSequence++;
}
}
private void processValidMessage(String message, InetAddress senderAddress) {
// 实际业务处理
Log.i(TAG, "收到有效消息: " + message);
// 这里添加你的业务逻辑...
if ("PLAY".equals(message)) {
Log.i(TAG, "执行播放指令");
callUnity("AppManager", "UnityMethod", "play");
} else if ("STOP".equals(message)) {
Log.i(TAG, "执行停止指令");
callUnity("AppManager", "UnityMethod", "stop");
}
// 其他指令...
}
private void sendAck(int seq, InetAddress targetAddress, int targetPort) {
String ackMsg = "MSG:" + seq + ":ACK/" + seq;
byte[] ackData = ackMsg.getBytes();
networkExecutor.execute(() -> {
try {
DatagramPacket packet = new DatagramPacket(
ackData, ackData.length, targetAddress, targetPort);
udpSocket.send(packet);
Log.d(TAG, "发送ACK seq=" + seq);
} catch (Exception e) {
Log.e(TAG, "发送ACK失败: " + e.getMessage());
}
});
}
// 可靠发送方法
private void sendReliable(InetAddress targetAddress, int targetPort, String message) {
int seq;
synchronized (sequenceLock) {
seq = sequenceCounter++;
}
// 构建可靠消息
String formattedMsg = "MSG:" + seq + ":" + message;
byte[] data = formattedMsg.getBytes();
// 添加到待确认字典
PendingMessage pendingMsg = new PendingMessage(data, targetAddress, targetPort);
pendingMessages.put(seq, pendingMsg);
// 初始发送
networkExecutor.execute(() -> {
try {
DatagramPacket packet = new DatagramPacket(
data, data.length, targetAddress, targetPort);
udpSocket.send(packet);
Log.d(TAG, "发送可靠消息 seq=" + seq + ", 内容=" + message);
} catch (Exception e) {
Log.e(TAG, "发送失败: " + e.getMessage());
}
});
}
// ================= 旧版兼容处理 =================
private void handleLegacyMessage(String message, InetAddress senderAddress, int senderPort) {
if ("DISCOVER".equals(message)) {
String sn = Main.sn;
String power = Main.power;
String jsonResponse = String.format("{\"SN\":\"%s\",\"Power\":\"%s\"}", sn, power);
networkExecutor.execute(() -> {
try {
byte[] response = jsonResponse.getBytes();
DatagramPacket responsePacket = new DatagramPacket(
response, response.length, senderAddress, senderPort);
udpSocket.send(responsePacket);
Log.i(TAG, "发送DISCOVER响应");
} catch (Exception e) {
Log.e(TAG, "发送DISCOVER响应失败: " + e.getMessage());
}
});
}
}
// ================= UDP接收线程 =================
private DatagramSocket udpSocket;
private boolean isRunning = false;
private void startUdpReceiver() {
Log.i(TAG, "启动UDP接收器");
isRunning = true;
networkExecutor.execute(() -> {
try {
udpSocket = new DatagramSocket(8888);
byte[] buffer = new byte[BUFFER_SIZE];
while (isRunning) {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
udpSocket.receive(packet);
String receivedData = new String(
packet.getData(), 0, packet.getLength()).trim();
Log.i(TAG, "原始UDP数据: " + receivedData);
handleReceivedMessage(
receivedData,
packet.getAddress(),
packet.getPort());
}
} catch (Exception e) {
if (isRunning) {
Log.e(TAG, "UDP接收错误: " + e.getMessage());
}
} finally {
if (udpSocket != null && !udpSocket.isClosed()) {
udpSocket.close();
}
}
});
}
// ================= Unity通信方法 =================
public void callUnity(String gameObjectName, String methodName, String message) {
try {
UnityPlayer.currentActivity.runOnUiThread(() -> {
UnityPlayer.UnitySendMessage(gameObjectName, methodName, message);
});
} catch (Exception e) {
Log.e(TAG, "调用Unity方法失败: " + e.getMessage());
}
}
// ================= 静态方法Unity调用=================
public static void startMyForegroundService() {
Log.i("PineappleService", "开始启动服务...");
try {
@@ -92,120 +348,14 @@ public class Main extends Service {
}
}
private DatagramSocket udpSocket;
private boolean isRunning = false;
private Thread udpReceiveThread;
private void startUdpReceiver() {
Log.i("PineappleService", "启动UDP接收");
isRunning = true;
udpReceiveThread = new Thread(new Runnable() {
@Override
public void run() {
try {
udpSocket = new DatagramSocket(8888);
byte[] buffer = new byte[1024];
while (isRunning) {
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
udpSocket.receive(packet);
String receivedData = new String(packet.getData(), 0, packet.getLength()).trim();
Log.i("PineappleService", "收到UDP数据: " + receivedData);
if ("DISCOVER".equals(receivedData)) {
String sn = Main.sn;
String power = Main.power;
String jsonResponse = String.format("{\"SN\":\"%s\",\"Power\":\"%s\"}", sn, power);
byte[] response = jsonResponse.getBytes();
DatagramPacket responsePacket = new DatagramPacket(
response,
response.length,
packet.getAddress(),
packet.getPort());
udpSocket.send(responsePacket);
} else if ("PLAY".equals(receivedData)) {
Log.i("PineappleService", "收到播放指令");
// callUnityMethod("AppManager", "StartApp", null);
// UnityPlayer.UnitySendMessage("AppManager", "UnityMethod", "test");
callUnity("AppManager", "UnityMethod", "test");
}
}
} catch (Exception e) {
Log.e("PineappleService", "UDP接收错误: " + e.getMessage());
}
}
});
udpReceiveThread.start();
}
// 用于接收 Unity 返回的值
private static String sn = "未知";
private static String power = "0%";
// Unity 调用此方法设置 SN
public static void setSnFromUnity(String sn) {
Main.sn = sn;
}
// Unity 调用此方法设置电量
public static void setPowerFromUnity(String power) {
Main.power = power;
}
// /**
// * 调用Unity的方法
// *
// * @param gameObjectName 调用的GameObject的名称
// * @param functionName 方法名
// * @param args 参数
// * @return 调用是否成功
// */
// boolean callUnity(String gameObjectName, String functionName, String args) {
// final String TAG = "PineappleService"; // 日志标签
// try {
// // 1. 获取UnityPlayer类
// Class<?> classtype = Class.forName("com.unity3d.player.UnityPlayer");
// // 2. 获取UnitySendMessage静态方法
// Method method = classtype.getMethod("UnitySendMessage", String.class,
// String.class, String.class);
// // 3. 关键修复静态方法第一个参数必须为null
// method.invoke(null, gameObjectName, functionName, args); // ✅ 修复静态调用
// return true;
// } catch (ClassNotFoundException e) {
// Log.i(TAG, " ERROR: UnityPlayer class not found! Check Unity integration.");
// } catch (NoSuchMethodException e) {
// Log.i(TAG, " ERROR: UnitySendMessage method not found! Update Unity
// library.");
// } catch (IllegalAccessException e) {
// Log.i(TAG, " ERROR: Access denied to UnitySendMessage! Check method
// visibility.");
// } catch (InvocationTargetException e) {
// Log.i(TAG, " ERROR: UnitySendMessage crashed: " + e.getTargetException());
// } catch (Exception e) {
// Log.i(TAG, " UNEXPECTED ERROR: " + e.getMessage());
// }
// return false;
// }
public void callUnity(String gameObjectName, String methodName, String message) {
try {
// 使用UnityPlayer.currentActivity获取当前Activity上下文
UnityPlayer.currentActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
// 直接使用UnityPlayer的静态方法UnitySendMessage
UnityPlayer.UnitySendMessage(gameObjectName, methodName, message);
}
});
} catch (Exception e) {
Log.e("PineappleService", "调用Unity方法失败: " + e.getMessage());
}
}
}