张世豪
23 小时以前 03b0fb0ba2de86bcfff277778826547c0e37a93f
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
package jiekou;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import java.io.*;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
/**
 * TCP客户端工具类
 * 实现发卡机与服务器的所有通信接口
 */
public class TcpClientUtil {
    private static final Logger logger = LoggerFactory.getLogger(TcpClientUtil.class);
 
    private Socket socket;
    private OutputStream outputStream;
    private InputStream inputStream;
    private BufferedReader reader;
    private volatile boolean running = false;
    private MessageListener messageListener;
 
    // 重连相关配置
    private String serverIp;
    private int serverPort;
    private String deviceId;
    private final AtomicBoolean reconnecting = new AtomicBoolean(false);
    private static final int RECONNECT_INTERVAL = 3000; // 重连间隔3秒
 
    // JSON解析模式
    private static final Pattern JSON_PATTERN = Pattern.compile("\"([^\"]+)\"\\s*:\\s*(\"[^\"]*\"|[^,\\}\\s]*)");
    private static final Pattern ARRAY_PATTERN = Pattern.compile("\\{([^}]*)\\}");
 
    /**
     * 连接TCP服务器
     * @param ip 服务器IP地址
     * @param port 服务器端口
     * @return 连接是否成功
     */
    public boolean connect(String ip, int port) {
        try {
            // 保存连接参数用于重连
            this.serverIp = ip;
            this.serverPort = port;
 
            // 1. 创建Socket连接服务器
            this.socket = new Socket(ip, port);
            // 移除超时设置,让监听线程可以无限等待
            socket.setSoTimeout(0);
 
            // 2. 获取输入输出流
            this.outputStream = socket.getOutputStream();
            this.inputStream = socket.getInputStream();
            this.reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
 
            logger.info("成功连接到服务器: {}:{}", ip, port);
            reconnecting.set(false); // 重置重连状态
            return true;
 
        } catch (IOException e) {
            logger.error("TCP连接异常: {}", e.getMessage(), e);
            return false;
        }
    }
 
    /**
     * 连接并登录服务器
     * @param ip 服务器IP地址
     * @param port 服务器端口
     * @param deviceId 设备ID
     * @return 登录是否成功
     */
    public boolean connectAndLogin(String ip, int port, String deviceId) {
        this.deviceId = deviceId; // 保存设备ID用于重连后重新登录
        if (!connect(ip, port)) {
            return false;
        }
 
        Map<String, Object> response = sendLoginRequest(deviceId);
        return response != null &&
                "logined".equals(response.get("cmd")) &&
                "100".equals(String.valueOf(response.get("code")));
    }
 
    /**
     * 发送登录请求
     * @param deviceId 设备ID
     * @return 登录响应数据
     */
    public Map<String, Object> sendLoginRequest(String deviceId) {
        try {
            // 构建登录请求JSON
            String loginRequest = buildLoginRequestJson(deviceId);
            logger.debug("发送登录请求: {}", loginRequest);
 
            // 发送登录请求
            boolean sendSuccess = sendMessage(loginRequest);
            if (!sendSuccess) {
                return null;
            }
 
            // 读取服务器响应
            String response = reader.readLine();
            logger.info("收到服务器登录响应: {}", response);
 
            // 解析响应并返回
            return parseJson(response);
 
        } catch (IOException e) {
            logger.error("登录请求异常: {}", e.getMessage(), e);
            return null;
        }
    }
 
    /**
     * 请求人员数据
     * @param deviceId 设备ID
     * @return 人员数据列表
     */
    public List<Map<String, Object>> requestPersonData(String deviceId) {
        try {
            // 构建人员数据请求JSON
            String batchGetRequest = buildBatchGetRequestJson(deviceId);
            logger.debug("发送人员数据请求: {}", batchGetRequest);
 
            // 发送请求
            boolean sendSuccess = sendMessage(batchGetRequest);
            if (!sendSuccess) {
                return null;
            }
 
            // 读取服务器响应(人员数据)
            String response = reader.readLine();
            logger.info("收到人员数据: {}", response);
 
            // 解析响应
            Map<String, Object> responseMap = parseJson(response);
            if ("send".equals(responseMap.get("cmd")) && "101".equals(String.valueOf(responseMap.get("code")))) {
                // 解析人员数据
                String dataStr = (String) responseMap.get("data");
                List<Map<String, Object>> personList = parsePersonDataArray(dataStr);
 
                // 发送成功应答
                sendSentAck();
 
                return personList;
            }
 
            return null;
 
        } catch (IOException e) {
            logger.error("请求人员数据异常: {}", e.getMessage(), e);
            return null;
        }
    }
 
    /**
     * 发送人员数据接收成功应答
     */
    public boolean sendSentAck() {
        try {
            String ackJson = "{\"code\":101,\"cmd\":\"sent\",\"data\":null}";
            return sendMessage(ackJson);
        } catch (Exception e) {
            logger.error("发送成功应答异常: {}", e.getMessage(), e);
            return false;
        }
    }
 
    /**
     * 发送删除人员成功应答
     * @param userId 用户ID
     * @return 发送是否成功
     */
    public boolean sendDeleteAck(String userId) {
        try {
            String ackJson = "{\"code\":102,\"cmd\":\"deleted\",\"data\":null}";
            boolean success = sendMessage(ackJson);
            if (success) {
                logger.info("发送删除人员成功应答,用户ID: {}", userId);
            }
            return success;
        } catch (Exception e) {
            logger.error("发送删除人员成功应答异常: {}", e.getMessage(), e);
            return false;
        }
    }
 
    /**
     * 发送开锁成功应答
     * @param doorNo 门编号
     * @return 发送是否成功
     */
    public boolean sendOpenDoorAck(String doorNo) {
        try {
            String ackJson = "{\"code\":103,\"cmd\":\"opened\",\"data\":null}";
            boolean success = sendMessage(ackJson);
            if (success) {
                logger.info("发送开锁成功应答,门编号: {}", doorNo);
            }
            return success;
        } catch (Exception e) {
            logger.error("发送开锁成功应答异常: {}", e.getMessage(), e);
            return false;
        }
    }
 
    /**
     * 发送升级成功应答
     * @param updateUrl 升级文件URL
     * @return 发送是否成功
     */
    public boolean sendUpdateAck(String updateUrl) {
        try {
            String ackJson = "{\"code\":104,\"cmd\":\"upDated\",\"data\":null}";
            boolean success = sendMessage(ackJson);
            if (success) {
                logger.info("发送升级成功应答,升级URL: {}", updateUrl);
            }
            return success;
        } catch (Exception e) {
            logger.error("发送升级成功应答异常: {}", e.getMessage(), e);
            return false;
        }
    }
 
    /**
     * 发送自定义命令
     * @param code 命令代码
     * @param cmd 命令类型
     * @param data 数据
     * @return 发送是否成功
     */
    public boolean sendCommand(String code, String cmd, String data) {
        try {
            String json = buildCommandJson(code, cmd, data);
            boolean success = sendMessage(json);
            if (success) {
                logger.debug("发送命令: {}", json);
            }
            return success;
        } catch (Exception e) {
            logger.error("发送命令异常: {}", e.getMessage(), e);
            return false;
        }
    }
 
    /**
     * 通用消息发送方法
     * @param message 要发送的消息
     * @return 发送是否成功
     */
    public boolean sendMessage(String message) {
        if (!isConnected()) {
            logger.error("连接未建立,无法发送消息");
            return false;
        }
 
        try {
            outputStream.write((message + "\n").getBytes(StandardCharsets.UTF_8));
            outputStream.flush();
            return true;
        } catch (IOException e) {
            logger.error("发送消息异常: {}", e.getMessage(), e);
            return false;
        }
    }
 
    /**
     * 读取服务器响应
     * @return 服务器响应数据
     */
    public String readResponse() {
        try {
            return reader.readLine();
        } catch (IOException e) {
            logger.error("读取响应异常: {}", e.getMessage(), e);
            return null;
        }
    }
 
    /**
     * 读取并解析服务器响应
     * @return 解析后的响应Map
     */
    public Map<String, Object> readAndParseResponse() {
        String response = readResponse();
        if (response != null) {
            return parseJson(response);
        }
        return null;
    }
 
    /**
     * 断线重连功能
     */
    private void reconnect() {
        if (reconnecting.getAndSet(true)) {
            logger.debug("重连操作已在进行中,跳过本次重连");
            return;
        }
 
        logger.info("开始断线重连...");
        while (running) {
            try {
                logger.info("尝试重连,目标服务器: {}:{}", serverIp, serverPort);
 
                // 关闭旧连接
                closeResources(reader, inputStream, outputStream, socket);
 
                // 等待一段时间后重连
                Thread.sleep(RECONNECT_INTERVAL);
 
                // 尝试重新连接
                if (connect(serverIp, serverPort)) {
                    // 重新登录
                    if (deviceId != null) {
                        Map<String, Object> loginResponse = sendLoginRequest(deviceId);
                        if (loginResponse != null &&
                                "logined".equals(loginResponse.get("cmd")) &&
                                "100".equals(String.valueOf(loginResponse.get("code")))) {
 
                            logger.info("重连并登录成功");
                            reconnecting.set(false);
 
                            // 重新启动消息监听
                            if (messageListener != null) {
                                startMessageListener(messageListener);
                            }
                            return;
                        }
                    } else {
                        // 如果没有设备ID,只连接不登录
                        logger.info("重连成功");
                        reconnecting.set(false);
                        if (messageListener != null) {
                            startMessageListener(messageListener);
                        }
                        return;
                    }
                }
 
                logger.warn("重连失败,{} 毫秒后重试", RECONNECT_INTERVAL);
 
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                logger.warn("重连过程被中断");
                break;
            } catch (Exception e) {
                logger.error("重连异常: {}", e.getMessage());
            }
        }
 
        reconnecting.set(false);
        if (running) {
            logger.error("重连循环退出,将在下次断开后重新尝试");
        }
    }
 
    /**
     * 启动消息监听器
     * @param listener 消息监听回调接口
     */
    public void startMessageListener(MessageListener listener) {
        this.messageListener = listener;
        running = true;
        new Thread(() -> {
            try {
                while (running && socket != null && !socket.isClosed()) {
                    try {
                        String message = reader.readLine();
                        if (message != null) {
                            logger.debug("收到服务器消息: {}", message);
 
                            // 解析消息
                            Map<String, Object> parsedMessage = parseJson(message);
 
                            // 回调给监听器
                            if (messageListener != null) {
                                messageListener.onMessageReceived(message, parsedMessage);
                            }
                        } else {
                            // 读到null表示连接已关闭
                            logger.warn("连接被服务器关闭");
                            break;
                        }
                    } catch (IOException e) {
                        if (running) {
                            logger.error("读取消息异常: {}", e.getMessage());
                            // 如果不是主动停止,尝试重连
                            if (running && !reconnecting.get()) {
                                reconnect();
                            }
                            break;
                        }
                    }
                }
            } finally {
                logger.info("消息监听线程结束");
                // 如果还在运行状态但连接已断开,尝试重连
                if (running && !reconnecting.get()) {
                    reconnect();
                }
            }
        }, "TCP-Client-Listener").start();
    }
 
    /**
     * 停止消息监听
     */
    public void stopMessageListener() {
        running = false;
        messageListener = null;
        reconnecting.set(false); // 停止重连
    }
 
    /**
     * 解析简单JSON字符串为Map
     * @param json JSON字符串
     * @return 解析后的Map
     */
    public Map<String, Object> parseJson(String json) {
        Map<String, Object> result = new HashMap<>();
        if (json == null || json.trim().isEmpty()) {
            return result;
        }
 
        // 移除首尾的{}
        String content = json.trim();
        if (content.startsWith("{") && content.endsWith("}")) {
            content = content.substring(1, content.length() - 1).trim();
        }
 
        Matcher matcher = JSON_PATTERN.matcher(content);
        while (matcher.find()) {
            String key = matcher.group(1);
            String value = matcher.group(2).trim();
 
            // 移除值的引号(如果存在)
            if (value.startsWith("\"") && value.endsWith("\"")) {
                value = value.substring(1, value.length() - 1);
            }
 
            // 处理null值
            if ("null".equals(value)) {
                result.put(key, null);
            } else {
                result.put(key, value);
            }
        }
 
        return result;
    }
 
    /**
     * 解析人员数据数组
     * @param dataStr 数据字符串
     * @return 人员列表
     */
    private List<Map<String, Object>> parsePersonDataArray(String dataStr) {
        List<Map<String, Object>> result = new ArrayList<>();
        if (dataStr == null || dataStr.trim().isEmpty()) {
            return result;
        }
 
        // 使用正则表达式匹配数组中的每个对象
        Matcher matcher = ARRAY_PATTERN.matcher(dataStr);
        while (matcher.find()) {
            String objStr = matcher.group(1);
            Map<String, Object> person = parsePersonObject(objStr);
            if (!person.isEmpty()) {
                result.add(person);
            }
        }
 
        return result;
    }
 
    /**
     * 解析单个人员对象
     * @param objStr 对象字符串
     * @return 人员信息Map
     */
    private Map<String, Object> parsePersonObject(String objStr) {
        Map<String, Object> person = new HashMap<>();
        if (objStr == null || objStr.trim().isEmpty()) {
            return person;
        }
 
        // 分割键值对
        String[] pairs = objStr.split(",");
        for (String pair : pairs) {
            String[] keyValue = pair.split(":", 2);
            if (keyValue.length == 2) {
                String key = keyValue[0].trim();
                String value = keyValue[1].trim();
 
                // 移除值的引号(如果存在)
                if (value.startsWith("'") && value.endsWith("'")) {
                    value = value.substring(1, value.length() - 1);
                } else if (value.startsWith("\"") && value.endsWith("\"")) {
                    value = value.substring(1, value.length() - 1);
                }
 
                person.put(key, value);
            }
        }
 
        return person;
    }
 
    /**
     * 构建登录请求JSON
     * @return JSON字符串
     */
    private String buildLoginRequestJson(String deviceId) {
        return "{\"code\":100,\"cmd\":\"login\",\"deviceId\":\"" + deviceId + "\"}";
    }
 
    /**
     * 构建人员数据请求JSON
     * @return JSON字符串
     */
    private String buildBatchGetRequestJson(String deviceId) {
        return "{\"code\":101,\"cmd\":\"batchGet\",\"deviceId\":\"" + deviceId + "\"}";
    }
 
    /**
     * 构建通用命令JSON
     * @param code 命令代码
     * @param cmd 命令类型
     * @param data 数据
     * @return JSON字符串
     */
    private String buildCommandJson(String code, String cmd, String data) {
        if (data != null) {
            return "{\"code\":\"" + code + "\",\"cmd\":\"" + cmd + "\",\"data\":\"" + data + "\"}";
        } else {
            return "{\"code\":\"" + code + "\",\"cmd\":\"" + cmd + "\",\"data\":null}";
        }
    }
 
    /**
     * 关闭连接
     */
    public void disconnect() {
        stopMessageListener();
        closeResources(reader, inputStream, outputStream, socket);
        logger.info("断开与服务器的连接");
    }
 
    /**
     * 检查连接状态
     * @return 是否连接
     */
    public boolean isConnected() {
        return socket != null && !socket.isClosed() && socket.isConnected();
    }
 
    /**
     * 关闭所有资源
     */
    private static void closeResources(Closeable... resources) {
        for (Closeable resource : resources) {
            if (resource != null) {
                try {
                    resource.close();
                } catch (IOException e) {
                    logger.error("关闭资源时发生异常: {}", e.getMessage(), e);
                }
            }
        }
    }
 
    /**
     * 消息监听回调接口
     */
    public interface MessageListener {
        /**
         * 当收到服务器消息时回调
         * @param rawMessage 原始消息字符串
         * @param parsedMessage 解析后的消息Map
         */
        void onMessageReceived(String rawMessage, Map<String, Object> parsedMessage);
 
    }
}