5 changed files with 1435 additions and 0 deletions
-
227DeviceUDPManager使用说明.md
-
4app/build.gradle
-
539app/src/main/java/com/ouxuan/oxface/device/DeviceUDPManager.java
-
314app/src/main/java/com/ouxuan/oxface/device/DeviceUDPManagerUsageExample.java
-
351app/src/main/java/com/ouxuan/oxface/device/UDPTestActivity.java
@ -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 |
||||
|
<uses-permission android:name="android.permission.INTERNET" /> |
||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> |
||||
|
``` |
||||
|
|
||||
|
### 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); |
||||
|
``` |
@ -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)); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -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(); |
||||
|
} |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue