营养数据计算优化;人脸增加数据处理;其它优化;

This commit is contained in:
2025-12-25 18:57:26 +08:00
parent f080a8b73c
commit dfb734e25d
29 changed files with 1350 additions and 64 deletions
@@ -0,0 +1,300 @@
package com.sw.dualscreen.socket;
import android.annotation.SuppressLint;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
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;
/**
* 局域网多客户端通信管理器:支持并发、心跳、认证、广播、点对点发送
* 可用于 Android 和 JVM 程序。
*/
public class LanCommunicationManager {
// ============ 监听配置 ============
private final int port;
private final long HEARTBEAT_TIMEOUT_MS;
private final int MAX_CLIENT_THREADS;
// ============ 状态 ============
private volatile boolean running = false;
private ServerSocket serverSocket;
// ============ 线程池 ============
private final ExecutorService acceptExecutor = Executors.newSingleThreadExecutor();
private final ExecutorService clientExecutor;
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
// ============ 客户端会话 ============
private final ConcurrentHashMap<String, ClientSession> clients = new ConcurrentHashMap<>();
private final ConcurrentHashMap<Socket, ClientSession> unAuthSessions = new ConcurrentHashMap<>();
// ============ 回调接口 ============
public interface Listener {
// 新客户端完成 AUTH / 认证
void onClientConnected(String clientId);
// 客户端断开
void onClientDisconnected(String clientId);
// 收到业务消息(type != heartbeat/auth)
void onMessageReceived(String clientId, JSONObject message);
}
private Listener listener;
// ============ 构造 ============
public LanCommunicationManager(int port, int maxClientThreads, long heartbeatTimeoutMs) {
this.port = port;
this.MAX_CLIENT_THREADS = maxClientThreads;
this.HEARTBEAT_TIMEOUT_MS = heartbeatTimeoutMs;
this.clientExecutor = Executors.newFixedThreadPool(Math.max(2, maxClientThreads));
}
public void setListener(Listener listener) {
this.listener = listener;
}
// ============ 启动服务端 ============
@SuppressLint("DiscouragedApi")
public void start() throws IOException {
if (running) return;
running = true;
serverSocket = new ServerSocket(port);
serverSocket.setSoTimeout(2000);
acceptExecutor.execute(this::acceptLoop);
scheduler.scheduleAtFixedRate(this::heartbeatCheck,
HEARTBEAT_TIMEOUT_MS,
HEARTBEAT_TIMEOUT_MS,
TimeUnit.MILLISECONDS);
System.out.println("LanCommunicationManager started on port " + port);
}
private void acceptLoop() {
while (running) {
try {
Socket socket = serverSocket.accept();
socket.setSoTimeout((int) HEARTBEAT_TIMEOUT_MS * 2);
ClientSession session = new ClientSession(socket);
unAuthSessions.put(socket, session);
clientExecutor.execute(() -> clientReadLoop(session));
} catch (SocketTimeoutException ignore) {
} catch (Exception e) {
if (running) e.printStackTrace();
}
}
}
// ============ 处理客户端数据读取 ============
private void clientReadLoop(ClientSession session) {
Socket socket = session.socket;
try (DataInputStream in = new DataInputStream(socket.getInputStream())) {
while (running && !socket.isClosed()) {
int len;
try {
len = in.readInt();
} catch (SocketTimeoutException ste) {
continue;
}
if (len <= 0 || len > 10 * 1024 * 1024) break;
byte[] buf = new byte[len];
in.readFully(buf);
session.updateLastSeen();
JSONObject msg = new JSONObject(new String(buf));
handleMessage(session, msg);
}
} catch (Exception ignored) {
} finally {
closeSession(session);
}
}
private void handleMessage(ClientSession session, JSONObject msg) {
String type = msg.optString("type", "");
switch (type) {
case "auth":
handleAuth(session, msg);
break;
case "heartbeat":
session.updateLastSeen();
break;
default:
if (listener != null && session.clientId != null) {
listener.onMessageReceived(session.clientId, msg);
}
break;
}
}
private void handleAuth(ClientSession session, JSONObject msg) {
String clientId = msg.optString("clientId", null);
if (clientId == null) return;
session.clientId = clientId;
// 移动到已认证 map
unAuthSessions.remove(session.socket);
clients.put(clientId, session);
if (listener != null) listener.onClientConnected(clientId);
sendToSession(session, ack("auth_ok"));
}
// ============ 心跳超时 ============
private void heartbeatCheck() {
long now = System.currentTimeMillis();
for (Map.Entry<String, ClientSession> e : clients.entrySet()) {
ClientSession s = e.getValue();
if (now - s.lastSeen > HEARTBEAT_TIMEOUT_MS) {
closeSession(s);
}
}
for (ClientSession s : unAuthSessions.values()) {
if (now - s.lastSeen > HEARTBEAT_TIMEOUT_MS * 2) {
closeSession(s);
}
}
}
// ============ 发送 ============
public boolean sendToClient(String clientId, JSONObject json) {
ClientSession s = clients.get(clientId);
return s != null && sendToSession(s, json);
}
public void broadcast(JSONObject json) {
for (ClientSession s : clients.values()) {
sendToSession(s, json);
}
}
private boolean sendToSession(ClientSession s, JSONObject json) {
try {
DataOutputStream out = s.out;
synchronized (out) {
byte[] data = json.toString().getBytes();
out.writeInt(data.length);
out.write(data);
out.flush();
}
return true;
} catch (Exception e) {
closeSession(s);
return false;
}
}
// ============ ACK ============
private JSONObject ack(String type) {
JSONObject j = new JSONObject();
try {
j.put("type", "ack");
j.put("ack", type);
} catch (JSONException e) {
throw new RuntimeException(e);
}
return j;
}
// ============ 停止 ============
public void stop() {
running = false;
try {
serverSocket.close();
} catch (Exception ignored) {
}
for (ClientSession s : clients.values()) closeSession(s);
for (ClientSession s : unAuthSessions.values()) closeSession(s);
acceptExecutor.shutdownNow();
clientExecutor.shutdownNow();
scheduler.shutdownNow();
System.out.println("LanCommunicationManager stopped");
}
// ============ 会话类 ============
public static class ClientSession {
public final Socket socket;
public final DataOutputStream out;
public volatile long lastSeen = System.currentTimeMillis();
public volatile String clientId;
public ClientSession(Socket socket) throws IOException {
this.socket = socket;
this.out = new DataOutputStream(socket.getOutputStream());
}
public void updateLastSeen() {
lastSeen = System.currentTimeMillis();
}
}
public void closeSession(ClientSession session) {
if (session == null) return;
try {
Socket socket = session.socket;
// 1. 从已认证表移除
if (session.clientId != null) {
ClientSession removed = clients.remove(session.clientId);
if (removed != null && listener != null) {
listener.onClientDisconnected(session.clientId);
}
}
// 2. 从未认证表移除
unAuthSessions.remove(socket);
// 3. 关闭输出流
try {
session.out.close();
} catch (Exception ignored) {
}
// 4. 关闭 socket
try {
if (!socket.isClosed()) socket.close();
} catch (Exception ignored) {
}
} catch (Exception e) {
e.printStackTrace();
}
}
}