// MainWindow.xaml.cs using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Threading; using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Folding; using Newtonsoft.Json.Linq; using Flurl; using Flurl.Http; using Newtonsoft.Json; using ToolLibrary; using System.Buffers; using System.IO.Pipelines; using System.Text.Json; using JsonException = Newtonsoft.Json.JsonException; using System.Collections.Concurrent; namespace ISMSTcpCmdWpfAppDemo { public partial class MainWindow : Window { // TCP 相关字段 private TcpClient _tcpClient = new TcpClient(); private NetworkStream _tcpStream; private CancellationTokenSource _tcpCts; private List _tcpBuffer = new List(); private const int BufferSize = 1024; // Web API 相关字段 private HttpListener _httpListener = new HttpListener(); private const int WebApiPort = 38094; private SemaphoreSlim _reconnectLock = new SemaphoreSlim(1, 1); // 配置参数 private readonly TimeSpan IncompleteDataTimeout = TimeSpan.FromSeconds(5); // 核心组件 private readonly Pipe _pipe = new Pipe(); private CancellationTokenSource _cts = new(); private DateTime _lastDataTime = DateTime.MinValue; public MainWindow() { InitializeComponent(); SetupComponents(); _httpListener.Prefixes.Add($"http://127.0.0.1:{WebApiPort}/"); StartWebApi(); } private void SetupComponents() { // 初始化 AvalonEdit 的折叠功能 var foldingManager = FoldingManager.Install(txtReceived.TextArea); var foldingStrategy = new JsonFoldingStrategy(); txtReceived.TextChanged += (s, e) => foldingStrategy.UpdateFoldings(foldingManager, txtReceived.Document); } private void BtnDisconnect_Click(object sender, RoutedEventArgs e) { Dispose(); UpdateStatus("已断开", "Red"); } private async void BtnConnect_Click(object sender, RoutedEventArgs e) { await ConnectAsync(txtIP.Text, int.Parse(txtPort.Text), externalToken: _cts.Token); } #region TCP 功能 private async Task ConnectAsync(string ip, int port, int maxRetries = 5, CancellationToken externalToken = default) { bool isConnected = false; if (!await _reconnectLock.WaitAsync(0)) return; try { using var cts = CancellationTokenSource.CreateLinkedTokenSource(externalToken); int retryCount = 0; var baseDelay = TimeSpan.FromSeconds(2); while (!cts.Token.IsCancellationRequested) { try { _tcpClient?.Dispose(); _tcpClient = new TcpClient { NoDelay = true }; await _tcpClient.ConnectAsync(ip, port, cts.Token); _tcpStream = _tcpClient.GetStream(); _tcpCts?.Cancel(); _tcpCts = new CancellationTokenSource(); _ = StartProcessing(_tcpStream); UpdateStatus("已连接", "Green"); isConnected = true; return; } catch (Exception ex) when (retryCount < maxRetries) { retryCount++; var delay = baseDelay * Math.Pow(2, retryCount); UpdateStatus($"连接中... ({retryCount}/{maxRetries})", "Orange"); await Task.Delay((int)delay.TotalMilliseconds, cts.Token); } catch (Exception ex) { UpdateStatus("连接失败", "Red"); ShowError($"最终连接失败", ex); throw; } } } finally { _reconnectLock.Release(); if (!isConnected) UpdateStatus("连接失败", "Red"); } } // 启动处理器 public async Task StartProcessing(NetworkStream stream) { _tcpStream = stream; Task.Run(ReceiveDataLoop); Task.Run(ProcessDataLoop); Task.Run(MonitorBufferTimeout); } public void Dispose() { _cts.Cancel(); _tcpCts?.Cancel(); _tcpCts?.Dispose(); _pipe.Writer.Complete(); _pipe.Reader.Complete(); _tcpStream?.Dispose(); _tcpClient?.Dispose(); _tcpCts = new CancellationTokenSource(); _cts = new CancellationTokenSource(); } // 数据接收循环 private async Task ReceiveDataLoop() { var writer = _pipe.Writer; try { while (!_cts.IsCancellationRequested && _tcpStream != null) { Memory memory = writer.GetMemory(BufferSize); int bytesRead = await _tcpStream.ReadAsync(memory, _cts.Token); if (bytesRead == 0) break; writer.Advance(bytesRead); await writer.FlushAsync(); _lastDataTime = DateTime.Now; await Task.Delay(100); } } catch (OperationCanceledException) { _cts = new CancellationTokenSource(); } finally { await writer.CompleteAsync(); } } // 数据处理循环 private async Task ProcessDataLoop() { var reader = _pipe.Reader; try { while (!_cts.IsCancellationRequested) { ReadResult result = await reader.ReadAsync(); ReadOnlySequence buffer = result.Buffer; while (TryProcessBuffer(ref buffer, out ProcessResult processResult)) { if (processResult.IsValid) { if (processResult.IsJArray) HandleJArray(processResult.JsonData); else HandleJObject(processResult.JsonData); } else { HandleInvalidData(processResult.ErrorMessage ?? "Unknown error", processResult.ConsumedBytes); } await Task.Delay(100); } reader.AdvanceTo(buffer.Start, buffer.End); if (result.IsCompleted) break; await Task.Delay(100); // 添加小延迟防止CPU占用过高 } } finally { await reader.CompleteAsync(); } } // 核心解析逻辑 private bool TryProcessBuffer(ref ReadOnlySequence buffer, out ProcessResult result) { result = default; var reader = new SequenceReader(buffer); int JsonLength; // 优先查找 "OK" 前缀 if (reader.TryReadTo(out ReadOnlySpan _, new[] { (byte)'O', (byte)'K' }, advancePastDelimiter: true)) { //long prefixPosition = reader.Consumed - 2; // OK的长度为2 long prefixPosition = reader.Consumed-2; if (TryParseJson(ref reader, out string? json,out JsonLength, true)) { //int totalConsumed = (int)(prefixPosition + json.Length); result = ProcessResult.Success(true, json, JsonLength); buffer = buffer.Slice(JsonLength); return true; } } reader = new SequenceReader(buffer); // 尝试解析普通JSON if (TryParseJson(ref reader, out string? regularJson, out JsonLength, false)) { result = ProcessResult.Success(false, regularJson, (int)JsonLength); buffer = buffer.Slice(reader.Position); return true; } // 检测到不完整数据 if (buffer.Length > 0) { result = ProcessResult.Error("Incomplete data", 0); return false; } return false; } // 高性能JSON解析 private bool TryParseJson(ref SequenceReader reader, out string? json, out int JsonLength,bool isSkipOK = false) { json = null; JsonLength = 0; ReadOnlySpan originalBuffer = reader.UnreadSequence.ToArray(); int originalBufferLength = originalBuffer.Length; ReadOnlySpan buffer = originalBuffer; int skipBytes = 2; try { // 1. 处理跳过"OK"的逻辑 if (isSkipOK) { ReadOnlySpan okBytes = new byte[] { (byte)'O', (byte)'K' }; int okIndex = buffer.IndexOf(okBytes); if (okIndex != -1) buffer = buffer.Slice(okIndex + okBytes.Length); skipBytes = 2; int arrayStart = buffer.IndexOf((byte)'['); if (arrayStart != 0) return false; int arrayEnd = -1, bracketCount = 1; for (int i = 1; i < buffer.Length; i++) { if (buffer[i] == '[') bracketCount++; else if (buffer[i] == ']') bracketCount--; if (bracketCount != 0) continue; arrayEnd = i; break; } if (arrayEnd == -1) return false; // 3. 提取并验证数组内容 if (arrayStart != -1 && arrayEnd != -1 && arrayEnd > arrayStart) { //ReadOnlySpan arrayData = buffer.Slice(arrayStart, arrayEnd - arrayStart + 1); ReadOnlySpan arrayData = buffer.Slice(0, arrayEnd + 1); string arrayString = Encoding.UTF8.GetString(arrayData); JArray parsedArray = JArray.Parse(arrayString); // 验证数组有效性 json = arrayString; // 5. 计算实际消耗的字节数 int totalConsumed = arrayStart + arrayEnd; //if (totalConsumed > originalBufferLength) return false; if (totalConsumed>= reader.Length) { totalConsumed = (int)reader.Length-1; } try { reader.Advance(totalConsumed); } catch { reader.AdvanceToEnd(); } UpdateTextContent(json); JsonLength = (int)reader.Length; return true; } } else { if (buffer.Length>0) { string rawString = Encoding.UTF8.GetString(buffer); if (rawString.Contains("OK")) { return false; } if (IsTruncatedJson(rawString)) { return false; } //if (string.IsNullOrWhiteSpace) //{ //} try { JToken token = JToken.Parse(rawString); if (token.Type == JTokenType.Object) { int consumed = buffer.Length; json = rawString; reader.Advance(consumed); ShowAlarmMsg(rawString); JsonLength = consumed; return true; } } catch (Exception ex) { int consumed = buffer.Length; json = rawString; ShowError(rawString, ex); reader.Advance(consumed); JsonLength = consumed; return true; } } } // 6. 未找到有效数组时回退原始解析 return false; } catch (JsonReaderException ex) { // 错误处理(保持原有逻辑,略作调整) int errorCharIndex = ex.LinePosition - 1; if (errorCharIndex >= 0) { int errorByteIndex = skipBytes + Encoding.UTF8.GetByteCount( Encoding.UTF8.GetString(buffer).Substring(0, errorCharIndex) ); UpdateTextContent($"错误数据:{Encoding.UTF8.GetString(originalBuffer.Slice(0, errorByteIndex))}"); reader.Advance(errorByteIndex); } return false; } catch { reader.Advance(originalBufferLength); return false; } } public bool IsTruncatedJson(string str) { int balance = 10; foreach (char c in str) { if (c == '{') balance++; else if (c == '}') { if (balance == 0) return false; balance--; } } return balance >10; } // 超时监控 private async Task MonitorBufferTimeout() { while (!_cts.IsCancellationRequested) { await Task.Delay(1000); if (DateTime.Now - _lastDataTime > IncompleteDataTimeout) { if (_pipe.Reader.TryRead(out ReadResult result)) { HandleInvalidData("Data timeout", (int)result.Buffer.Length); _pipe.Reader.AdvanceTo(result.Buffer.End); } } } } // 示例处理方法(需根据业务实现) private void HandleJArray(string json) { TryParseJToken(json, out var token); _token = token; OnTcpResponseReceived(token); } private void HandleJObject(string json) { //发送报警信息 Task.Run(() => { TryParseJToken(json, out var token); HttpHelper.HttpPostRequest("http://localhost:38091/api/services/isas/AlarmLiveData/UploadISMSAlarmMsg", json as object); }); } private void HandleInvalidData(string reason, int bytesToSkip) { } #region 辅助方法 private bool TryParseJToken(string json, out JToken token) { try { token = JToken.Parse(json); return true; } catch { token = null; return false; } } #endregion #endregion #region Web API 功能 private async void StartWebApi() { try { _httpListener.Start(); webApiState.Text = "已启动"; while (_httpListener.IsListening) { var context = await _httpListener.GetContextAsync(); ProcessHttpRequest(context); await Task.Delay(100); } } catch (Exception ex) { ShowError("Web API 错误", ex); } } private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private readonly CancellationTokenSource _httpcts = new CancellationTokenSource(); private async Task ProcessHttpRequest(HttpListenerContext context) { try { if (context.Request.HttpMethod != "GET" || context.Request.Url.AbsolutePath != "/api") { SendHttpResponse(context.Response, 404, "Not Found"); return; } var dzparam = context.Request.QueryString["dz"]; var faultRptparam = context.Request.QueryString["FaultRpt"]; // 互斥锁替代Running标记 if (!await _semaphore.WaitAsync(TimeSpan.Zero)) { SendHttpResponse(context.Response, 503, "Error: 服务器繁忙"); return; } try { WebResult result; using var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(180)); if (!string.IsNullOrEmpty(dzparam)) { result = await ProcessRequestAsync(dzparam, "定值", timeoutCts.Token); } else if (!string.IsNullOrEmpty(faultRptparam)) { result = await ProcessRequestAsync(faultRptparam, "故障报告", timeoutCts.Token); } else { result = WebResult.ErrorResult(400, new Exception("无效参数")); } SendHttpResponse(context.Response, 200, JsonConvert.SerializeObject(result)); } finally { _semaphore.Release(); } } catch (OperationCanceledException) { SendHttpResponse(context.Response, 504, "Error: 请求超时"); } catch (Exception ex) { SendHttpResponse(context.Response, 500, $"Error: {ex.Message}"); } } private async Task> ProcessRequestAsync(string param, string logType, CancellationToken ct) { WirteHttpMsg($"收到{logType}信息:{param}"); SendTcpMsg(param); var tcs = new TaskCompletionSource(); JToken mtoken = null; var recvHandler = (JToken token) => { tcs.TrySetResult(true); mtoken = token; return true; }; try { // 注册接收完成回调 TcpResponseReceived += recvHandler; // 同时等待接收信号和超时 var delayTask = Task.Delay(1000*300, ct); var completedTask = await Task.WhenAny(tcs.Task, delayTask); if (completedTask == delayTask) throw new TimeoutException("操作超时"); return WebResult.Ok(_token); } finally { TcpResponseReceived -= recvHandler; } } // 在TCP数据接收处触发事件 public event Func TcpResponseReceived; // 当收到TCP响应时调用 private void OnTcpResponseReceived(JToken token) { TcpResponseReceived?.Invoke(token); } private void WirteHttpMsg(string msg) { Dispatcher.BeginInvoke(() => { if (MessageList.Items.Count > 100) { MessageList.Items.Clear(); } MessageList.Items.Add(msg); }); } private void SendHttpResponse(HttpListenerResponse response, int statusCode, string content) { WirteHttpMsg($"状态:{statusCode} 发送http消息:{content}"); response.StatusCode = statusCode; response.ContentType = "text/plain; charset=utf-8"; response.SendChunked = true; using (var writer = new StreamWriter(response.OutputStream, Encoding.UTF8, 4096)) { int chunkSize = 4096; for (int i = 0; i < content.Length; i += chunkSize) { int length = Math.Min(chunkSize, content.Length - i); writer.Write(content.AsSpan(i, length)); } } response.Close(); } #endregion #region UI 更新方法 private void UpdateStatus(string message, string color) { Dispatcher.Invoke(() => { txtStatus.Text = message; txtStatus.Foreground = color == "Green" ? System.Windows.Media.Brushes.Green : System.Windows.Media.Brushes.Red; }); } private void ShowError(string message, Exception ex) { Dispatcher.BeginInvoke(() => { txtErr.Text = $"{DateTime.Now:HH:mm:ss} - {message}: {ex.Message}"; }); } private void ShowRawData() { var text = Encoding.UTF8.GetString(_tcpBuffer.ToArray()); _tcpBuffer.Clear(); UpdateTextContent(text); } // 增加更新节流机制 // 替换StringBuilder为线程安全队列 // 替换StringBuilder为线程安全队列 private ConcurrentQueue _msgQueue = new ConcurrentQueue(); private DateTime _lastUpdateTime = DateTime.MinValue; private const int UpdateInterval = 200; private void UpdateTextContent(string content) { _msgQueue.Enqueue(content); // 线程安全入队 if ((DateTime.Now - _lastUpdateTime).TotalMilliseconds < UpdateInterval) return; Dispatcher.BeginInvoke(() => { // 批量处理队列 var sb = new StringBuilder(); while (_msgQueue.TryDequeue(out var msg)) // 线程安全出队 { sb.AppendLine(msg); } if (sb.Length > 0) { // 安全插入文本 var doc = txtReceived.Document; using (doc.RunUpdate()) { // 追加新内容到文档末尾 doc.Insert(doc.TextLength, sb.ToString()); // 动态计算清理位置 const int maxLength = 20000; const int trimSize = 10000; if (doc.TextLength > maxLength) { var removeLength = Math.Min(trimSize, doc.TextLength - (maxLength - trimSize)); doc.Remove(0, removeLength); } } txtReceived.ScrollToEnd(); } }); _lastUpdateTime = DateTime.Now; } #endregion #region 辅助方法 private bool IsValidJson(string input) { try { return JToken.Parse(input) != null; } catch { return false; } } JToken _token; private string FormatJson(string json) { try { _token = JToken.Parse(json); return _token.ToString(Newtonsoft.Json.Formatting.Indented); } catch { return json; } } private void SaveToFile(string content) { return; var fileName = $"log_{DateTime.Now:yyyyMMdd_HHmmss}.json"; File.WriteAllTextAsync(fileName, content); } #endregion #region 事件处理 protected override void OnClosed(EventArgs e) { _httpListener.Stop(); _tcpCts?.Cancel(); _tcpClient?.Close(); base.OnClosed(e); } private async void BtnSend_Click(object sender, RoutedEventArgs e) { try { await SendTcpMsg(txtSend.Text); } catch (Exception ex) { ShowError("发送失败", ex); } } // 修改SendTcpMsg方法,改为完全异步 public async Task SendTcpMsg(string msg) { try { if (_tcpClient?.Connected != true || _tcpStream == null) return false; var data = Encoding.UTF8.GetBytes(msg + "\n"); // 改为异步写入 await _tcpStream.WriteAsync(data, 0, data.Length); await _tcpStream.FlushAsync(); // UI更新放到主线程 Dispatcher.Invoke(() => { txtReceived.AppendText($"发送成功:{msg}\n"); txtReceived.ScrollToEnd(); }); return true; } catch (Exception ex) { Dispatcher.Invoke(() => ShowError("发送失败", ex)); return false; } } private void StartWebApi_Click(object sender, RoutedEventArgs e) => StartWebApi(); private void StopWebApi_Click(object sender, RoutedEventArgs e) { _httpListener.Stop(); webApiState.Text = "已停止"; } #endregion long alarmIndex = 0; private void ShowAlarmMsg(string msg) { if (alarmIndex > long.MaxValue - 1) { alarmIndex = 0; } alarmIndex++; Dispatcher.BeginInvoke(() => { if (AlarmMessageList.Items.Count > 200) { AlarmMessageList.Items.Clear(); } AlarmMessageList.Items.Add($"序号:{alarmIndex} {msg}"); }); } private void ClearServerButton_Click(object sender, RoutedEventArgs e) { txtReceived.Text = string.Empty; } private void ClearAlarm_Click(object sender, RoutedEventArgs e) { AlarmMessageList.Items.Clear(); } private void clearISMSMsgBtn_Click(object sender, RoutedEventArgs e) { txtReceived.Text = string.Empty; } } }