From 4cb51b96682d8a0d881973fa47fad6c65e230e54 Mon Sep 17 00:00:00 2001 From: "3075067877@qq.com" <3075067877@qq.com> Date: Thu, 11 Sep 2025 17:48:05 +0800 Subject: [PATCH] add udp --- DeviceUDPManager使用说明.md | 227 +++++++++ app/build.gradle | 4 + .../com/ouxuan/oxface/device/DeviceUDPManager.java | 539 +++++++++++++++++++++ .../device/DeviceUDPManagerUsageExample.java | 314 ++++++++++++ .../com/ouxuan/oxface/device/UDPTestActivity.java | 351 ++++++++++++++ 5 files changed, 1435 insertions(+) create mode 100644 DeviceUDPManager使用说明.md create mode 100644 app/src/main/java/com/ouxuan/oxface/device/DeviceUDPManager.java create mode 100644 app/src/main/java/com/ouxuan/oxface/device/DeviceUDPManagerUsageExample.java create mode 100644 app/src/main/java/com/ouxuan/oxface/device/UDPTestActivity.java diff --git a/DeviceUDPManager使用说明.md b/DeviceUDPManager使用说明.md new file mode 100644 index 0000000..4d5a557 --- /dev/null +++ b/DeviceUDPManager使用说明.md @@ -0,0 +1,227 @@ +# DeviceUDPManager 使用说明 + +## 概述 +`DeviceUDPManager` 是基于 KryoNet 库实现的 UDP 通信管理器,支持客户端和服务端两种模式,提供了完整的 UDP 数据收发功能。 + +## 特性 +- ✅ 支持客户端和服务端模式 +- ✅ 自动重连机制 +- ✅ 线程安全的数据收发 +- ✅ 支持多种数据类型(String、Integer、byte[]等) +- ✅ 完整的事件回调机制 +- ✅ 单例模式,全局统一管理 + +## 快速开始 + +### 1. 依赖已添加 +在 `app/build.gradle` 中已添加必要依赖: +```gradle +// KryoNet UDP通信库 +implementation 'com.esotericsoftware:kryonet:2.22.0-RC1' +implementation 'com.esotericsoftware:kryo:2.24.0' +``` + +### 2. 权限配置 +在 `AndroidManifest.xml` 中已包含必要权限: +```xml + + +``` + +### 3. 基本使用 + +#### 客户端模式 +```java +// 获取管理器实例 +DeviceUDPManager udpManager = DeviceUDPManager.getInstance(); + +// 设置监听器 +udpManager.setMessageListener(new DeviceUDPManager.UDPMessageListener() { + @Override + public void onMessageReceived(Object message, Connection connection) { + Log.i("UDP", "收到消息: " + message); + } + + @Override + public void onMessageSent(Object message) { + Log.i("UDP", "发送成功: " + message); + } + + @Override + public void onMessageSendFailed(Object message, String error) { + Log.e("UDP", "发送失败: " + error); + } +}); + +udpManager.setConnectionStateListener(new DeviceUDPManager.ConnectionStateListener() { + @Override + public void onConnected() { + Log.i("UDP", "连接成功"); + // 可以开始发送消息 + udpManager.sendUDPMessage("Hello Server!"); + } + + @Override + public void onDisconnected() { + Log.i("UDP", "连接断开"); + } + + @Override + public void onConnectionFailed(String error) { + Log.e("UDP", "连接失败: " + error); + } + + @Override + public void onReconnecting(int attempt) { + Log.i("UDP", "重连中,第 " + attempt + " 次"); + } +}); + +// 初始化为客户端 +udpManager.initializeAsClient(context, "192.168.1.100", 54555, 54777); +``` + +#### 服务端模式 +```java +// 获取管理器实例 +DeviceUDPManager udpManager = DeviceUDPManager.getInstance(); + +// 设置监听器(同客户端) +// ... + +// 初始化为服务端 +udpManager.initializeAsServer(context, 54555, 54777); +``` + +### 4. 发送消息 +```java +// 发送字符串消息 +udpManager.sendUDPMessage("Hello UDP!"); + +// 发送数字消息 +udpManager.sendUDPMessage(12345); + +// 发送JSON格式消息 +String jsonMsg = "{\"type\":\"status\",\"value\":\"online\"}"; +udpManager.sendUDPMessage(jsonMsg); + +// 服务端模式:发送消息给特定客户端 +udpManager.sendUDPMessageToConnection(connection, "Hello Client!"); +``` + +## API 参考 + +### 主要方法 + +#### 初始化方法 +- `initializeAsClient(Context, String, int, int)` - 初始化为客户端 +- `initializeAsServer(Context, int, int)` - 初始化为服务端 + +#### 消息发送 +- `sendUDPMessage(Object)` - 发送UDP消息 +- `sendUDPMessageToConnection(Connection, Object)` - 发送消息给特定连接(服务端模式) + +#### 状态管理 +- `isConnected()` - 获取连接状态 +- `isInitialized()` - 获取初始化状态 +- `isClientMode()` - 获取当前模式 +- `reconnect()` - 手动重连(客户端模式) +- `stop()` - 停止UDP管理器 +- `release()` - 释放所有资源 + +#### 配置方法 +- `setMessageListener(UDPMessageListener)` - 设置消息监听器 +- `setConnectionStateListener(ConnectionStateListener)` - 设置连接状态监听器 +- `setAutoReconnect(boolean)` - 设置自动重连 + +### 回调接口 + +#### UDPMessageListener +- `onMessageReceived(Object, Connection)` - 接收到消息 +- `onMessageSent(Object)` - 消息发送成功 +- `onMessageSendFailed(Object, String)` - 消息发送失败 + +#### ConnectionStateListener +- `onConnected()` - 连接成功 +- `onDisconnected()` - 连接断开 +- `onConnectionFailed(String)` - 连接失败 +- `onReconnecting(int)` - 重连中 + +## 使用场景 + +### 1. 设备间通信 +```java +// 设备A作为服务端 +udpManager.initializeAsServer(context, 54555, 54777); + +// 设备B作为客户端 +udpManager.initializeAsClient(context, "设备A的IP", 54555, 54777); +``` + +### 2. 命令控制 +```java +// 发送控制命令 +udpManager.sendUDPMessage("command:restart"); +udpManager.sendUDPMessage("command:get_status"); + +// 接收端处理 +@Override +public void onMessageReceived(Object message, Connection connection) { + if (message.toString().startsWith("command:")) { + String command = message.toString().substring(8); + handleCommand(command, connection); + } +} +``` + +### 3. 状态同步 +```java +// 定期发送状态信息 +String status = "{\"device_id\":\"" + Build.MODEL + + "\",\"status\":\"online\"," + + "\"timestamp\":" + System.currentTimeMillis() + "}"; +udpManager.sendUDPMessage(status); +``` + +## 注意事项 + +1. **线程安全**: 所有方法都是线程安全的,可以在任意线程调用 +2. **网络权限**: 确保应用具有网络访问权限 +3. **端口占用**: 避免端口冲突,确保指定端口未被其他应用使用 +4. **数据类型**: 发送的对象需要是Kryo支持的类型或已注册的自定义类型 +5. **资源释放**: 在不需要时调用 `stop()` 或 `release()` 释放资源 +6. **异常处理**: 网络操作可能失败,注意处理回调中的错误信息 + +## 测试 + +可以使用提供的 `UDPTestActivity` 进行功能测试: + +1. 在 `AndroidManifest.xml` 中注册Activity +2. 启动Activity进行测试 +3. 可以在同一设备上启动服务端和客户端进行本地测试 + +## 扩展 + +### 自定义数据类型 +```java +// 注册自定义类型 +public class CustomMessage { + public String type; + public String content; + public long timestamp; +} + +// 在初始化时注册 +udpManager.getKryo().register(CustomMessage.class); +``` + +### 加密通信 +可以在发送前加密消息,接收后解密: +```java +// 发送加密消息 +String encrypted = encrypt(originalMessage); +udpManager.sendUDPMessage(encrypted); + +// 接收解密消息 +String decrypted = decrypt(encryptedMessage); +``` \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index d4225fa..b243669 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -91,6 +91,10 @@ dependencies { // 华为扫码库 implementation "com.huawei.hms:scanplus:2.12.0.301" + + // KryoNet UDP通信库 + implementation 'com.esotericsoftware:kryonet:2.22.0-RC1' + implementation 'com.esotericsoftware:kryo:2.24.0' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' diff --git a/app/src/main/java/com/ouxuan/oxface/device/DeviceUDPManager.java b/app/src/main/java/com/ouxuan/oxface/device/DeviceUDPManager.java new file mode 100644 index 0000000..855ef45 --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/device/DeviceUDPManager.java @@ -0,0 +1,539 @@ +package com.ouxuan.oxface.device; + +import android.content.Context; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; + +import com.esotericsoftware.kryonet.Client; +import com.esotericsoftware.kryonet.Connection; +import com.esotericsoftware.kryonet.Listener; +import com.esotericsoftware.kryonet.Server; + +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * 设备UDP通信管理器 + * 基于KryoNet库实现UDP数据收发功能 + * + * 功能特性: + * - 支持UDP服务端和客户端模式 + * - 自动重连机制 + * - 线程安全的数据收发 + * - 支持自定义消息类型 + * + * 使用示例: + * DeviceUDPManager manager = DeviceUDPManager.getInstance(); + * manager.initializeAsClient(context, "192.168.1.100", 54555, 54777); + * manager.sendUDPMessage("Hello World"); + * + * @author AI Assistant + * @version 1.0 + */ +public class DeviceUDPManager { + + private static final String TAG = "DeviceUDPManager"; + private static final int DEFAULT_TCP_PORT = 54555; + private static final int DEFAULT_UDP_PORT = 54777; + private static final int RECONNECT_DELAY = 5000; // 5秒重连延迟 + private static final int CONNECTION_TIMEOUT = 5000; // 5秒连接超时 + + private static DeviceUDPManager instance; + private Context context; + + // 网络组件 + private Client client; + private Server server; + + // 配置参数 + private String serverIP; + private int tcpPort = DEFAULT_TCP_PORT; + private int udpPort = DEFAULT_UDP_PORT; + private boolean isClientMode = true; + private boolean isInitialized = false; + private boolean isConnected = false; + private boolean autoReconnect = true; + + // 线程管理 + private ExecutorService executorService; + private Handler mainHandler; + + // 回调接口 + private UDPMessageListener messageListener; + private ConnectionStateListener connectionListener; + + /** + * UDP消息监听接口 + */ + public interface UDPMessageListener { + /** + * 接收到UDP消息 + * @param message 消息内容 + * @param connection 连接对象 + */ + void onMessageReceived(Object message, Connection connection); + + /** + * UDP消息发送成功 + * @param message 发送的消息 + */ + void onMessageSent(Object message); + + /** + * UDP消息发送失败 + * @param message 发送的消息 + * @param error 错误信息 + */ + void onMessageSendFailed(Object message, String error); + } + + /** + * 连接状态监听接口 + */ + public interface ConnectionStateListener { + /** + * 连接成功 + */ + void onConnected(); + + /** + * 连接断开 + */ + void onDisconnected(); + + /** + * 连接失败 + * @param error 错误信息 + */ + void onConnectionFailed(String error); + + /** + * 重连中 + * @param attempt 重连次数 + */ + void onReconnecting(int attempt); + } + + private DeviceUDPManager() { + executorService = Executors.newCachedThreadPool(); + mainHandler = new Handler(Looper.getMainLooper()); + } + + /** + * 获取单例实例 + */ + public static synchronized DeviceUDPManager getInstance() { + if (instance == null) { + instance = new DeviceUDPManager(); + } + return instance; + } + + /** + * 初始化为客户端模式 + * @param context 应用上下文 + * @param serverIP 服务器IP地址 + * @param tcpPort TCP端口 + * @param udpPort UDP端口 + */ + public void initializeAsClient(Context context, String serverIP, int tcpPort, int udpPort) { + this.context = context.getApplicationContext(); + this.serverIP = serverIP; + this.tcpPort = tcpPort; + this.udpPort = udpPort; + this.isClientMode = true; + + Log.i(TAG, "初始化为客户端模式 - 服务器: " + serverIP + ", TCP: " + tcpPort + ", UDP: " + udpPort); + initializeClient(); + } + + /** + * 初始化为服务端模式 + * @param context 应用上下文 + * @param tcpPort TCP端口 + * @param udpPort UDP端口 + */ + public void initializeAsServer(Context context, int tcpPort, int udpPort) { + this.context = context.getApplicationContext(); + this.tcpPort = tcpPort; + this.udpPort = udpPort; + this.isClientMode = false; + + Log.i(TAG, "初始化为服务端模式 - TCP: " + tcpPort + ", UDP: " + udpPort); + initializeServer(); + } + + /** + * 初始化客户端 + */ + private void initializeClient() { + executorService.execute(() -> { + try { + // 清理现有连接 + cleanup(); + + // 创建客户端 + client = new Client(); + + // 注册常用数据类型 + registerCommonClasses(client.getKryo()); + + // 添加监听器 + client.addListener(new Listener() { + @Override + public void connected(Connection connection) { + Log.i(TAG, "客户端已连接到服务器"); + isConnected = true; + notifyConnectionState(true); + } + + @Override + public void disconnected(Connection connection) { + Log.i(TAG, "客户端已断开连接"); + isConnected = false; + notifyConnectionState(false); + + // 自动重连 + if (autoReconnect && isInitialized) { + scheduleReconnect(); + } + } + + @Override + public void received(Connection connection, Object object) { + Log.d(TAG, "收到UDP消息: " + object); + notifyMessageReceived(object, connection); + } + }); + + // 启动客户端 + client.start(); + isInitialized = true; + + // 连接到服务器 + connectToServer(); + + } catch (Exception e) { + Log.e(TAG, "初始化客户端失败", e); + notifyConnectionError("初始化客户端失败: " + e.getMessage()); + } + }); + } + + /** + * 初始化服务端 + */ + private void initializeServer() { + executorService.execute(() -> { + try { + // 清理现有连接 + cleanup(); + + // 创建服务端 + server = new Server(); + + // 注册常用数据类型 + registerCommonClasses(server.getKryo()); + + // 添加监听器 + server.addListener(new Listener() { + @Override + public void connected(Connection connection) { + Log.i(TAG, "客户端已连接到服务器: " + connection.getRemoteAddressTCP()); + isConnected = true; + notifyConnectionState(true); + } + + @Override + public void disconnected(Connection connection) { + Log.i(TAG, "客户端已断开连接: " + connection.getRemoteAddressTCP()); + // 服务端模式下不需要自动重连 + } + + @Override + public void received(Connection connection, Object object) { + Log.d(TAG, "收到UDP消息: " + object + " 来自: " + connection.getRemoteAddressTCP()); + notifyMessageReceived(object, connection); + } + }); + + // 绑定端口并启动 + server.bind(tcpPort, udpPort); + server.start(); + isInitialized = true; + isConnected = true; + + Log.i(TAG, "UDP服务器已启动 - TCP: " + tcpPort + ", UDP: " + udpPort); + notifyConnectionState(true); + + } catch (Exception e) { + Log.e(TAG, "初始化服务端失败", e); + notifyConnectionError("初始化服务端失败: " + e.getMessage()); + } + }); + } + + /** + * 连接到服务器(客户端模式) + */ + private void connectToServer() { + if (!isClientMode || client == null) return; + + executorService.execute(() -> { + try { + Log.i(TAG, "正在连接到服务器: " + serverIP); + client.connect(CONNECTION_TIMEOUT, serverIP, tcpPort, udpPort); + } catch (IOException e) { + Log.e(TAG, "连接服务器失败", e); + notifyConnectionError("连接服务器失败: " + e.getMessage()); + + // 自动重连 + if (autoReconnect && isInitialized) { + scheduleReconnect(); + } + } + }); + } + + /** + * 安排重连 + */ + private int reconnectAttempt = 0; + private void scheduleReconnect() { + if (!autoReconnect || !isInitialized) return; + + reconnectAttempt++; + Log.i(TAG, "计划重连,第 " + reconnectAttempt + " 次尝试"); + + mainHandler.postDelayed(() -> { + if (isInitialized && !isConnected && autoReconnect) { + notifyReconnecting(reconnectAttempt); + connectToServer(); + } + }, RECONNECT_DELAY); + } + + /** + * 注册常用数据类型 + */ + private void registerCommonClasses(com.esotericsoftware.kryo.Kryo kryo) { + kryo.register(String.class); + kryo.register(Integer.class); + kryo.register(Long.class); + kryo.register(Boolean.class); + kryo.register(Double.class); + kryo.register(Float.class); + kryo.register(byte[].class); + // 可以根据需要注册更多自定义类型 + } + + /** + * 发送UDP消息 + * @param message 要发送的消息 + * @return 是否发送成功 + */ + public boolean sendUDPMessage(Object message) { + if (!isInitialized || !isConnected) { + Log.w(TAG, "UDP管理器未初始化或未连接,无法发送消息"); + notifyMessageSendFailed(message, "UDP管理器未初始化或未连接"); + return false; + } + + executorService.execute(() -> { + try { + if (isClientMode && client != null) { + // 客户端模式发送 + client.sendUDP(message); + Log.d(TAG, "客户端发送UDP消息: " + message); + notifyMessageSent(message); + } else if (!isClientMode && server != null) { + // 服务端模式广播发送 + server.sendToAllUDP(message); + Log.d(TAG, "服务端广播UDP消息: " + message); + notifyMessageSent(message); + } + } catch (Exception e) { + Log.e(TAG, "发送UDP消息失败", e); + notifyMessageSendFailed(message, "发送失败: " + e.getMessage()); + } + }); + + return true; + } + + /** + * 发送UDP消息到指定连接(服务端模式) + * @param connection 目标连接 + * @param message 要发送的消息 + * @return 是否发送成功 + */ + public boolean sendUDPMessageToConnection(Connection connection, Object message) { + if (!isInitialized || server == null || isClientMode) { + Log.w(TAG, "服务端模式未初始化,无法发送消息"); + notifyMessageSendFailed(message, "服务端模式未初始化"); + return false; + } + + executorService.execute(() -> { + try { + connection.sendUDP(message); + Log.d(TAG, "服务端发送UDP消息到连接: " + message); + notifyMessageSent(message); + } catch (Exception e) { + Log.e(TAG, "发送UDP消息失败", e); + notifyMessageSendFailed(message, "发送失败: " + e.getMessage()); + } + }); + + return true; + } + + /** + * 设置消息监听器 + */ + public void setMessageListener(UDPMessageListener listener) { + this.messageListener = listener; + } + + /** + * 设置连接状态监听器 + */ + public void setConnectionStateListener(ConnectionStateListener listener) { + this.connectionListener = listener; + } + + /** + * 设置自动重连 + */ + public void setAutoReconnect(boolean autoReconnect) { + this.autoReconnect = autoReconnect; + } + + /** + * 获取连接状态 + */ + public boolean isConnected() { + return isConnected; + } + + /** + * 获取初始化状态 + */ + public boolean isInitialized() { + return isInitialized; + } + + /** + * 获取当前模式 + */ + public boolean isClientMode() { + return isClientMode; + } + + /** + * 手动重连(客户端模式) + */ + public void reconnect() { + if (isClientMode && isInitialized) { + Log.i(TAG, "手动重连"); + reconnectAttempt = 0; + connectToServer(); + } + } + + /** + * 停止UDP管理器 + */ + public void stop() { + Log.i(TAG, "停止UDP管理器"); + isInitialized = false; + autoReconnect = false; + cleanup(); + } + + /** + * 清理资源 + */ + private void cleanup() { + isConnected = false; + + if (client != null) { + try { + client.stop(); + } catch (Exception e) { + Log.e(TAG, "停止客户端失败", e); + } + client = null; + } + + if (server != null) { + try { + server.stop(); + } catch (Exception e) { + Log.e(TAG, "停止服务端失败", e); + } + server = null; + } + } + + /** + * 释放所有资源 + */ + public void release() { + Log.i(TAG, "释放UDP管理器资源"); + stop(); + + if (executorService != null && !executorService.isShutdown()) { + executorService.shutdown(); + } + + messageListener = null; + connectionListener = null; + } + + // 通知方法 + private void notifyMessageReceived(Object message, Connection connection) { + if (messageListener != null) { + mainHandler.post(() -> messageListener.onMessageReceived(message, connection)); + } + } + + private void notifyMessageSent(Object message) { + if (messageListener != null) { + mainHandler.post(() -> messageListener.onMessageSent(message)); + } + } + + private void notifyMessageSendFailed(Object message, String error) { + if (messageListener != null) { + mainHandler.post(() -> messageListener.onMessageSendFailed(message, error)); + } + } + + private void notifyConnectionState(boolean connected) { + if (connectionListener != null) { + mainHandler.post(() -> { + if (connected) { + reconnectAttempt = 0; // 连接成功后重置重连计数 + connectionListener.onConnected(); + } else { + connectionListener.onDisconnected(); + } + }); + } + } + + private void notifyConnectionError(String error) { + if (connectionListener != null) { + mainHandler.post(() -> connectionListener.onConnectionFailed(error)); + } + } + + private void notifyReconnecting(int attempt) { + if (connectionListener != null) { + mainHandler.post(() -> connectionListener.onReconnecting(attempt)); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/device/DeviceUDPManagerUsageExample.java b/app/src/main/java/com/ouxuan/oxface/device/DeviceUDPManagerUsageExample.java new file mode 100644 index 0000000..559a796 --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/device/DeviceUDPManagerUsageExample.java @@ -0,0 +1,314 @@ +package com.ouxuan.oxface.device; + +import android.content.Context; +import android.util.Log; + +import com.esotericsoftware.kryonet.Connection; + +/** + * DeviceUDPManager 使用示例 + * 展示如何在项目中初始化和使用UDP通信功能 + * + * @author AI Assistant + * @version 1.0 + */ +public class DeviceUDPManagerUsageExample { + + private static final String TAG = "UDPManagerExample"; + private DeviceUDPManager udpManager; + private Context context; + + public DeviceUDPManagerUsageExample(Context context) { + this.context = context; + this.udpManager = DeviceUDPManager.getInstance(); + } + + /** + * 初始化为客户端模式示例 + */ + public void initializeAsClientExample() { + // 设置消息监听器 + udpManager.setMessageListener(new DeviceUDPManager.UDPMessageListener() { + @Override + public void onMessageReceived(Object message, Connection connection) { + Log.i(TAG, "收到消息: " + message + " 来自: " + connection.getRemoteAddressTCP()); + + // 处理接收到的消息 + if (message instanceof String) { + String strMessage = (String) message; + handleStringMessage(strMessage, connection); + } + // 可以根据需要处理其他类型的消息 + } + + @Override + public void onMessageSent(Object message) { + Log.i(TAG, "消息发送成功: " + message); + } + + @Override + public void onMessageSendFailed(Object message, String error) { + Log.e(TAG, "消息发送失败: " + message + ", 错误: " + error); + } + }); + + // 设置连接状态监听器 + udpManager.setConnectionStateListener(new DeviceUDPManager.ConnectionStateListener() { + @Override + public void onConnected() { + Log.i(TAG, "UDP连接成功"); + // 连接成功后可以发送消息 + sendTestMessage(); + } + + @Override + public void onDisconnected() { + Log.i(TAG, "UDP连接断开"); + } + + @Override + public void onConnectionFailed(String error) { + Log.e(TAG, "UDP连接失败: " + error); + } + + @Override + public void onReconnecting(int attempt) { + Log.i(TAG, "UDP重连中,第 " + attempt + " 次尝试"); + } + }); + + // 设置自动重连 + udpManager.setAutoReconnect(true); + + // 初始化为客户端(连接到服务器) + String serverIP = "192.168.1.100"; // 替换为实际的服务器IP + int tcpPort = 54555; + int udpPort = 54777; + udpManager.initializeAsClient(context, serverIP, tcpPort, udpPort); + } + + /** + * 初始化为服务端模式示例 + */ + public void initializeAsServerExample() { + // 设置消息监听器 + udpManager.setMessageListener(new DeviceUDPManager.UDPMessageListener() { + @Override + public void onMessageReceived(Object message, Connection connection) { + Log.i(TAG, "服务端收到消息: " + message + " 来自: " + connection.getRemoteAddressTCP()); + + // 处理接收到的消息并回复 + if (message instanceof String) { + String strMessage = (String) message; + handleServerMessage(strMessage, connection); + } + } + + @Override + public void onMessageSent(Object message) { + Log.i(TAG, "服务端消息发送成功: " + message); + } + + @Override + public void onMessageSendFailed(Object message, String error) { + Log.e(TAG, "服务端消息发送失败: " + message + ", 错误: " + error); + } + }); + + // 设置连接状态监听器 + udpManager.setConnectionStateListener(new DeviceUDPManager.ConnectionStateListener() { + @Override + public void onConnected() { + Log.i(TAG, "服务端已准备就绪,等待客户端连接"); + } + + @Override + public void onDisconnected() { + Log.i(TAG, "客户端断开连接"); + } + + @Override + public void onConnectionFailed(String error) { + Log.e(TAG, "服务端启动失败: " + error); + } + + @Override + public void onReconnecting(int attempt) { + // 服务端模式下通常不需要重连 + } + }); + + // 初始化为服务端 + int tcpPort = 54555; + int udpPort = 54777; + udpManager.initializeAsServer(context, tcpPort, udpPort); + } + + /** + * 发送测试消息 + */ + public void sendTestMessage() { + if (udpManager.isConnected()) { + // 发送字符串消息 + udpManager.sendUDPMessage("Hello from Android device!"); + + // 发送JSON格式的消息 + String jsonMessage = "{\"type\":\"device_status\",\"status\":\"online\",\"timestamp\":" + System.currentTimeMillis() + "}"; + udpManager.sendUDPMessage(jsonMessage); + + // 发送数字消息 + udpManager.sendUDPMessage(12345); + } else { + Log.w(TAG, "UDP未连接,无法发送消息"); + } + } + + /** + * 处理接收到的字符串消息(客户端) + */ + private void handleStringMessage(String message, Connection connection) { + Log.d(TAG, "处理字符串消息: " + message); + + // 根据消息内容进行不同处理 + if (message.startsWith("{")) { + // JSON格式消息 + handleJsonMessage(message, connection); + } else if (message.equals("ping")) { + // 心跳消息 + udpManager.sendUDPMessage("pong"); + } else if (message.startsWith("command:")) { + // 命令消息 + String command = message.substring(8); + handleCommand(command, connection); + } else { + // 普通文本消息 + Log.i(TAG, "收到普通文本消息: " + message); + } + } + + /** + * 处理服务端接收到的消息 + */ + private void handleServerMessage(String message, Connection connection) { + Log.d(TAG, "服务端处理消息: " + message); + + // 服务端可以回复消息给特定客户端 + if (message.equals("ping")) { + udpManager.sendUDPMessageToConnection(connection, "pong"); + } else if (message.equals("get_status")) { + String status = "{\"server_status\":\"running\",\"timestamp\":" + System.currentTimeMillis() + "}"; + udpManager.sendUDPMessageToConnection(connection, status); + } else { + // 回复确认消息 + udpManager.sendUDPMessageToConnection(connection, "message_received: " + message); + } + } + + /** + * 处理JSON格式消息 + */ + private void handleJsonMessage(String jsonMessage, Connection connection) { + try { + Log.d(TAG, "处理JSON消息: " + jsonMessage); + // 这里可以使用Gson或其他JSON库解析消息 + // 示例:设备状态更新、配置同步等 + } catch (Exception e) { + Log.e(TAG, "解析JSON消息失败", e); + } + } + + /** + * 处理命令消息 + */ + private void handleCommand(String command, Connection connection) { + Log.d(TAG, "处理命令: " + command); + + switch (command) { + case "restart": + Log.i(TAG, "收到重启命令"); + // 执行重启逻辑 + break; + case "get_config": + Log.i(TAG, "收到获取配置命令"); + // 返回当前配置 + udpManager.sendUDPMessage("{\"config\":{\"version\":\"1.0\",\"mode\":\"production\"}}"); + break; + case "shutdown": + Log.i(TAG, "收到关闭命令"); + // 执行关闭逻辑 + break; + default: + Log.w(TAG, "未知命令: " + command); + break; + } + } + + /** + * 手动重连 + */ + public void reconnect() { + if (udpManager.isClientMode()) { + udpManager.reconnect(); + } + } + + /** + * 停止UDP管理器 + */ + public void stop() { + udpManager.stop(); + } + + /** + * 获取连接状态 + */ + public boolean isConnected() { + return udpManager.isConnected(); + } + + /** + * 在Activity或Service中的使用示例 + */ + public static class IntegrationExample { + + /** + * 在Application中初始化(推荐) + */ + public static void initializeInApplication(Context context) { + DeviceUDPManagerUsageExample example = new DeviceUDPManagerUsageExample(context); + + // 根据设备角色选择模式 + boolean isServer = isCurrentDeviceServer(); // 自定义逻辑判断 + + if (isServer) { + example.initializeAsServerExample(); + } else { + example.initializeAsClientExample(); + } + } + + /** + * 在Activity中使用 + */ + public static void useInActivity(Context context) { + DeviceUDPManager manager = DeviceUDPManager.getInstance(); + + // 检查连接状态 + if (manager.isConnected()) { + // 发送设备状态消息 + String deviceInfo = "{\"device_id\":\"" + android.os.Build.MODEL + "\",\"timestamp\":" + System.currentTimeMillis() + "}"; + manager.sendUDPMessage(deviceInfo); + } + } + + /** + * 判断当前设备是否应该作为服务端 + * 这里可以根据具体业务逻辑实现 + */ + private static boolean isCurrentDeviceServer() { + // 示例:可以根据设备配置、IP地址范围、设备类型等判断 + // 这里简单返回false,实际使用时需要根据业务需求实现 + return false; + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/ouxuan/oxface/device/UDPTestActivity.java b/app/src/main/java/com/ouxuan/oxface/device/UDPTestActivity.java new file mode 100644 index 0000000..3c03123 --- /dev/null +++ b/app/src/main/java/com/ouxuan/oxface/device/UDPTestActivity.java @@ -0,0 +1,351 @@ +package com.ouxuan.oxface.device; + +import android.os.Bundle; +import android.util.Log; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ScrollView; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.appcompat.app.AppCompatActivity; + +import com.esotericsoftware.kryonet.Connection; +import com.ouxuan.oxface.R; + +/** + * UDP通信测试Activity + * 用于测试DeviceUDPManager的功能 + * + * @author AI Assistant + * @version 1.0 + */ +public class UDPTestActivity extends AppCompatActivity { + + private static final String TAG = "UDPTestActivity"; + + private DeviceUDPManager udpManager; + private EditText etServerIP; + private EditText etTcpPort; + private EditText etUdpPort; + private EditText etMessage; + private Button btnStartClient; + private Button btnStartServer; + private Button btnSendMessage; + private Button btnStop; + private TextView tvStatus; + private TextView tvLog; + private ScrollView scrollView; + + private StringBuilder logBuilder = new StringBuilder(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // 由于这是测试Activity,我们使用简单的布局 + initializeViews(); + + udpManager = DeviceUDPManager.getInstance(); + setupUDPManager(); + setupClickListeners(); + + // 设置默认值 + etServerIP.setText("192.168.1.100"); + etTcpPort.setText("54555"); + etUdpPort.setText("54777"); + etMessage.setText("Hello UDP!"); + + updateStatus("未连接"); + } + + /** + * 初始化视图(简化版,实际项目中应该使用XML布局) + */ + private void initializeViews() { + // 这里为了演示目的使用代码创建布局 + // 在实际项目中应该创建对应的XML布局文件 + + android.widget.LinearLayout mainLayout = new android.widget.LinearLayout(this); + mainLayout.setOrientation(android.widget.LinearLayout.VERTICAL); + mainLayout.setPadding(32, 32, 32, 32); + + // 服务器IP输入 + android.widget.TextView ipLabel = new android.widget.TextView(this); + ipLabel.setText("服务器IP:"); + mainLayout.addView(ipLabel); + + etServerIP = new EditText(this); + etServerIP.setHint("输入服务器IP地址"); + mainLayout.addView(etServerIP); + + // TCP端口输入 + android.widget.TextView tcpLabel = new android.widget.TextView(this); + tcpLabel.setText("TCP端口:"); + mainLayout.addView(tcpLabel); + + etTcpPort = new EditText(this); + etTcpPort.setHint("TCP端口"); + etTcpPort.setInputType(android.text.InputType.TYPE_CLASS_NUMBER); + mainLayout.addView(etTcpPort); + + // UDP端口输入 + android.widget.TextView udpLabel = new android.widget.TextView(this); + udpLabel.setText("UDP端口:"); + mainLayout.addView(udpLabel); + + etUdpPort = new EditText(this); + etUdpPort.setHint("UDP端口"); + etUdpPort.setInputType(android.text.InputType.TYPE_CLASS_NUMBER); + mainLayout.addView(etUdpPort); + + // 按钮布局 + android.widget.LinearLayout buttonLayout = new android.widget.LinearLayout(this); + buttonLayout.setOrientation(android.widget.LinearLayout.HORIZONTAL); + + btnStartClient = new Button(this); + btnStartClient.setText("启动客户端"); + buttonLayout.addView(btnStartClient); + + btnStartServer = new Button(this); + btnStartServer.setText("启动服务端"); + buttonLayout.addView(btnStartServer); + + btnStop = new Button(this); + btnStop.setText("停止"); + buttonLayout.addView(btnStop); + + mainLayout.addView(buttonLayout); + + // 消息输入 + android.widget.TextView msgLabel = new android.widget.TextView(this); + msgLabel.setText("发送消息:"); + mainLayout.addView(msgLabel); + + etMessage = new EditText(this); + etMessage.setHint("输入要发送的消息"); + mainLayout.addView(etMessage); + + btnSendMessage = new Button(this); + btnSendMessage.setText("发送消息"); + mainLayout.addView(btnSendMessage); + + // 状态显示 + tvStatus = new TextView(this); + tvStatus.setText("状态: 未连接"); + tvStatus.setTextSize(16); + tvStatus.setPadding(0, 20, 0, 10); + mainLayout.addView(tvStatus); + + // 日志显示 + android.widget.TextView logLabel = new android.widget.TextView(this); + logLabel.setText("日志:"); + mainLayout.addView(logLabel); + + tvLog = new TextView(this); + tvLog.setTextSize(12); + tvLog.setBackgroundColor(0xFFF0F0F0); + tvLog.setPadding(10, 10, 10, 10); + tvLog.setMaxLines(20); + + scrollView = new ScrollView(this); + scrollView.addView(tvLog); + android.widget.LinearLayout.LayoutParams scrollParams = new android.widget.LinearLayout.LayoutParams( + android.widget.LinearLayout.LayoutParams.MATCH_PARENT, 400); + scrollView.setLayoutParams(scrollParams); + mainLayout.addView(scrollView); + + setContentView(mainLayout); + } + + /** + * 设置UDP管理器监听器 + */ + private void setupUDPManager() { + udpManager.setMessageListener(new DeviceUDPManager.UDPMessageListener() { + @Override + public void onMessageReceived(Object message, Connection connection) { + String logMsg = "收到消息: " + message + " 来自: " + + (connection.getRemoteAddressTCP() != null ? connection.getRemoteAddressTCP().toString() : "未知"); + addLog(logMsg); + Log.i(TAG, logMsg); + } + + @Override + public void onMessageSent(Object message) { + String logMsg = "消息发送成功: " + message; + addLog(logMsg); + Log.i(TAG, logMsg); + } + + @Override + public void onMessageSendFailed(Object message, String error) { + String logMsg = "消息发送失败: " + message + ", 错误: " + error; + addLog(logMsg); + Log.e(TAG, logMsg); + showToast("发送失败: " + error); + } + }); + + udpManager.setConnectionStateListener(new DeviceUDPManager.ConnectionStateListener() { + @Override + public void onConnected() { + updateStatus("已连接"); + addLog("UDP连接成功"); + Log.i(TAG, "UDP连接成功"); + showToast("连接成功"); + } + + @Override + public void onDisconnected() { + updateStatus("已断开"); + addLog("UDP连接断开"); + Log.i(TAG, "UDP连接断开"); + } + + @Override + public void onConnectionFailed(String error) { + updateStatus("连接失败"); + addLog("UDP连接失败: " + error); + Log.e(TAG, "UDP连接失败: " + error); + showToast("连接失败: " + error); + } + + @Override + public void onReconnecting(int attempt) { + updateStatus("重连中 (第" + attempt + "次)"); + addLog("UDP重连中,第 " + attempt + " 次尝试"); + Log.i(TAG, "UDP重连中,第 " + attempt + " 次尝试"); + } + }); + + udpManager.setAutoReconnect(true); + } + + /** + * 设置按钮点击监听器 + */ + private void setupClickListeners() { + btnStartClient.setOnClickListener(v -> startClient()); + btnStartServer.setOnClickListener(v -> startServer()); + btnSendMessage.setOnClickListener(v -> sendMessage()); + btnStop.setOnClickListener(v -> stopUDP()); + } + + /** + * 启动客户端 + */ + private void startClient() { + String serverIP = etServerIP.getText().toString().trim(); + String tcpPortStr = etTcpPort.getText().toString().trim(); + String udpPortStr = etUdpPort.getText().toString().trim(); + + if (serverIP.isEmpty()) { + showToast("请输入服务器IP"); + return; + } + + try { + int tcpPort = Integer.parseInt(tcpPortStr); + int udpPort = Integer.parseInt(udpPortStr); + + updateStatus("连接中..."); + addLog("正在启动客户端连接到 " + serverIP + ":" + tcpPort + "/" + udpPort); + + udpManager.initializeAsClient(this, serverIP, tcpPort, udpPort); + + } catch (NumberFormatException e) { + showToast("端口号格式错误"); + } + } + + /** + * 启动服务端 + */ + private void startServer() { + String tcpPortStr = etTcpPort.getText().toString().trim(); + String udpPortStr = etUdpPort.getText().toString().trim(); + + try { + int tcpPort = Integer.parseInt(tcpPortStr); + int udpPort = Integer.parseInt(udpPortStr); + + updateStatus("启动中..."); + addLog("正在启动服务端,端口: " + tcpPort + "/" + udpPort); + + udpManager.initializeAsServer(this, tcpPort, udpPort); + + } catch (NumberFormatException e) { + showToast("端口号格式错误"); + } + } + + /** + * 发送消息 + */ + private void sendMessage() { + String message = etMessage.getText().toString().trim(); + + if (message.isEmpty()) { + showToast("请输入要发送的消息"); + return; + } + + if (!udpManager.isConnected()) { + showToast("UDP未连接,无法发送消息"); + return; + } + + addLog("发送消息: " + message); + udpManager.sendUDPMessage(message); + + // 清空输入框 + etMessage.setText(""); + } + + /** + * 停止UDP + */ + private void stopUDP() { + updateStatus("已停止"); + addLog("停止UDP服务"); + udpManager.stop(); + showToast("UDP服务已停止"); + } + + /** + * 更新状态显示 + */ + private void updateStatus(String status) { + runOnUiThread(() -> tvStatus.setText("状态: " + status)); + } + + /** + * 添加日志 + */ + private void addLog(String log) { + runOnUiThread(() -> { + String timestamp = java.text.DateFormat.getTimeInstance().format(new java.util.Date()); + logBuilder.append("[").append(timestamp).append("] ").append(log).append("\n"); + tvLog.setText(logBuilder.toString()); + + // 自动滚动到底部 + scrollView.post(() -> scrollView.fullScroll(ScrollView.FOCUS_DOWN)); + }); + } + + /** + * 显示Toast消息 + */ + private void showToast(String message) { + runOnUiThread(() -> Toast.makeText(this, message, Toast.LENGTH_SHORT).show()); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + if (udpManager != null) { + udpManager.stop(); + } + } +} \ No newline at end of file