using HslCommunication.Core.IMessage; using System; using System.Net.Sockets; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using WIDESEAWCS_QuartzJob; namespace WIDESEAWCS_Tasks.SocketServer { public partial class TcpSocketServer { public async Task HandleClientAsync(TcpClient client, string clientId, CancellationToken cancellationToken, RobotSocketState robotCrane) { using (client) using (NetworkStream networkStream = client.GetStream()) using (StreamReader reader = new(networkStream, _textEncoding, false, 1024, true)) using (StreamWriter writer = new(networkStream, _textEncoding, 1024, true) { AutoFlush = true }) { CancellationTokenSource? localCts = null; if (_options.EnableHeartbeat || _options.IdleTimeoutSeconds > 0) { localCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); } try { while (!cancellationToken.IsCancellationRequested && client.Connected) { string? message; try { var ct = localCts?.Token ?? cancellationToken; message = await reader.ReadLineAsync().WaitAsync(ct); } catch (OperationCanceledException) { break; } if (message == null) { break; } UpdateClientStatus(clientId, message); string messageLower = message.ToLowerInvariant(); if (TryHandleRegister(messageLower, message, clientId, networkStream, cancellationToken)) { continue; } if (MessageReceived != null) { try { _ = MessageReceived.Invoke(message, false, client, robotCrane); } catch { } } } } finally { try { localCts?.Cancel(); localCts?.Dispose(); } catch { } RemoveClient(clientId); try { _ = RobotReceived.Invoke(clientId); } catch { } } } } private bool TryHandleRegister(string messageLower, string message, string clientId, NetworkStream networkStream, CancellationToken cancellationToken) { if (!messageLower.StartsWith("register,")) { return false; } string deviceId = message.Substring("register,".Length).Trim(); if (!string.IsNullOrEmpty(deviceId)) { lock (_syncRoot) { _deviceBindings[deviceId] = clientId; } _ = WriteToClientAsync(clientId, networkStream, $"Registered,{deviceId}", cancellationToken); } return true; } private void UpdateClientStatus(string clientId, string message) { lock (_syncRoot) { _clientLastActive[clientId] = DateTime.Now; if (!_clientEncodings.ContainsKey(clientId)) { if (_options.AutoDetectEncoding && _autoDetectedGb2312 != null) { bool isUtf8 = TryParseJsonSilent(message) || IsLikelyUtf8(_textEncoding.GetBytes(message)); _clientEncodings[clientId] = isUtf8 ? _textEncoding : _autoDetectedGb2312; } else { _clientEncodings[clientId] = _textEncoding; } } } } private async Task WriteToClientAsync(string clientId, NetworkStream networkStream, string message, CancellationToken cancellationToken) { SemaphoreSlim? sem = null; Encoding? enc = null; lock (_syncRoot) { _clientLocks.TryGetValue(clientId, out sem); _clientEncodings.TryGetValue(clientId, out enc); } enc ??= _textEncoding; if (sem != null) await sem.WaitAsync(cancellationToken); try { var data = enc.GetBytes(message + "\n"); await networkStream.WriteAsync(data, 0, data.Length, cancellationToken); } finally { if (sem != null) sem.Release(); } } private static bool TryParseJsonSilent(string message) { if (string.IsNullOrWhiteSpace(message)) return false; char c = message.TrimStart()[0]; if (c != '{' && c != '[') return false; try { JsonDocument.Parse(message); return true; } catch { return false; } } private static bool IsLikelyUtf8(byte[] data) { int i = 0; while (i < data.Length) { byte b = data[i]; if (b <= 0x7F) { i++; continue; } if (b >= 0xC2 && b <= 0xDF) { if (i + 1 >= data.Length) return false; if ((data[i + 1] & 0xC0) != 0x80) return false; i += 2; continue; } if (b >= 0xE0 && b <= 0xEF) { if (i + 2 >= data.Length) return false; if ((data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80) return false; i += 3; continue; } if (b >= 0xF0 && b <= 0xF4) { if (i + 3 >= data.Length) return false; if ((data[i + 1] & 0xC0) != 0x80 || (data[i + 2] & 0xC0) != 0x80 || (data[i + 3] & 0xC0) != 0x80) return false; i += 4; continue; } return false; } return true; } } }