using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Text; using System.Threading; using JmNcReceiver.Models; namespace JmNcReceiver { /// /// 机明 NC 对外提交 — 接收端演示源码(学习 / 联调用)。 /// 默认监听 http://127.0.0.1:1235/ /// 用法:JmNcReceiver.exe [监听前缀] [--fail] [--pretty] /// internal static class Program { private static bool _forceFail; private static bool _prettyJson = true; private static volatile bool _running = true; private static int Main(string[] args) { string prefix = "http://127.0.0.1:1235/"; ParseArgs(args, ref prefix); string receivedDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Received"); Directory.CreateDirectory(receivedDir); if (!HttpListener.IsSupported) { Console.WriteLine("当前系统不支持 HttpListener。"); return 1; } var listener = new HttpListener(); listener.Prefixes.Add(prefix); try { listener.Start(); } catch (HttpListenerException ex) { Console.WriteLine("无法监听 {0}", prefix); Console.WriteLine("原因: {0}", ex.Message); Console.WriteLine(); Console.WriteLine("提示: 非管理员账户监听 localhost 以外地址时,可能需要:"); Console.WriteLine(" netsh http add urlacl url={0} user=%USERNAME%", prefix); return 2; } PrintBanner(prefix, receivedDir); NcOps.PrintHelp(); Console.CancelKeyPress += (s, e) => { e.Cancel = true; _running = false; try { listener.Stop(); } catch { } }; var acceptThread = new Thread(() => AcceptLoop(listener, receivedDir)) { IsBackground = true, Name = "HttpAccept" }; acceptThread.Start(); RunCommandLoop(receivedDir); _running = false; try { listener.Stop(); } catch { } try { listener.Close(); } catch { } return 0; } private static void AcceptLoop(HttpListener listener, string receivedDir) { while (_running && listener.IsListening) { HttpListenerContext context; try { context = listener.GetContext(); } catch (HttpListenerException) { break; } catch (ObjectDisposedException) { break; } ThreadPool.QueueUserWorkItem(_ => HandleRequest(context, receivedDir)); } } private static void ParseArgs(string[] args, ref string prefix) { if (args == null) return; foreach (string raw in args) { if (string.IsNullOrWhiteSpace(raw)) continue; string a = raw.Trim(); if (string.Equals(a, "--fail", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "-fail", StringComparison.OrdinalIgnoreCase)) { _forceFail = true; continue; } if (string.Equals(a, "--pretty", StringComparison.OrdinalIgnoreCase) || string.Equals(a, "-pretty", StringComparison.OrdinalIgnoreCase)) { _prettyJson = true; continue; } if (a.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || a.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { prefix = a.EndsWith("/") ? a : (a + "/"); } } } private static void PrintBanner(string prefix, string receivedDir) { Console.WriteLine("========================================"); Console.WriteLine(" 机明 NC 接收端演示 (JmNcReceiver)"); Console.WriteLine(" 监听: {0}", prefix); Console.WriteLine(" 落盘: {0}", receivedDir); Console.WriteLine(" 强制失败: {0}", _forceFail ? "是 (--fail)" : "否"); Console.WriteLine("========================================"); Console.WriteLine(); } private static void RunCommandLoop(string receivedDir) { while (_running) { Console.Write("> "); string line; try { line = Console.ReadLine(); } catch { break; } if (line == null) break; line = line.Trim(); if (line.Length == 0) continue; string[] parts = line.Split(new[] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries); string cmd = parts[0].ToLowerInvariant(); string arg = parts.Length > 1 ? parts[1].Trim() : null; try { if (cmd == "quit" || cmd == "exit") { _running = false; break; } if (cmd == "help" || cmd == "?") { NcOps.PrintHelp(); continue; } if (cmd == "summary") { NcOps.PrintSummary(NcSubmitStore.Latest); continue; } if (cmd == "text") { var items = NcSubmitStore.Latest; string text = NcOps.ToPlainText(items); Console.WriteLine(text); string outPath = Path.Combine(receivedDir, "latest-plain.txt"); File.WriteAllText(outPath, text, new UTF8Encoding(false)); Console.WriteLine("已写入 {0}", outPath); continue; } if (cmd == "dump") { var items = NcSubmitStore.Latest; if (items == null) { Console.WriteLine("(尚无批次)"); continue; } string outPath = Path.Combine(receivedDir, "latest-object.json"); File.WriteAllText(outPath, NcOps.ToPrettyJson(items), new UTF8Encoding(false)); Console.WriteLine("已写入 {0}", outPath); continue; } if (cmd == "extract") { string outDir = Path.Combine(receivedDir, "extracted"); int n = NcOps.ExtractStreams(NcSubmitStore.Latest, outDir); Console.WriteLine(n == 0 ? "(本批无 fileStream 可解码)" : ("共写出 " + n + " 个文件")); continue; } if (cmd == "load") { string path = string.IsNullOrEmpty(arg) ? Path.Combine(receivedDir, "latest-object.json") : arg; if (!File.Exists(path)) { Console.WriteLine("文件不存在: {0}", path); continue; } string json = File.ReadAllText(path, Encoding.UTF8); var items = NcOps.Deserialize(json); NcSubmitStore.SetLatest(items); Console.WriteLine(NcOps.ToPlainText(items)); continue; } Console.WriteLine("未知命令。输入 help 查看。"); } catch (Exception ex) { Console.WriteLine("命令失败: {0}", ex.Message); } } } private static void HandleRequest(HttpListenerContext context, string receivedDir) { HttpListenerRequest request = context.Request; HttpListenerResponse response = context.Response; try { if (string.Equals(request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase)) { WriteText(response, 200, "JmNcReceiver OK. POST JSON array (NcSubmitItem[]) to this URL."); return; } if (!string.Equals(request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase)) { WriteText(response, 405, "Method Not Allowed"); return; } string json = ReadRequestBodyAsUtf8(request); string stamp = DateTime.Now.ToString("yyyyMMdd_HHmmss_fff"); string baseName = "nc_" + stamp; string rawPath = Path.Combine(receivedDir, baseName + ".json"); File.WriteAllText(rawPath, json ?? string.Empty, new UTF8Encoding(false)); IList items = null; string err = null; try { items = NcOps.Deserialize(json); NcSubmitStore.SetLatest(items); } catch (Exception ex) { err = ex.Message; } if (items != null) { string plain = NcOps.ToPlainText(items); Console.WriteLine(); Console.WriteLine(plain); File.WriteAllText(Path.Combine(receivedDir, baseName + ".txt"), plain, new UTF8Encoding(false)); if (_prettyJson) { File.WriteAllText(Path.Combine(receivedDir, baseName + ".pretty.json"), NcOps.ToPrettyJson(items), new UTF8Encoding(false)); } } else { Console.WriteLine(); Console.WriteLine("[{0}] 收到 POST,但反序列化失败: {1}", stamp, err); Console.WriteLine("原文已保存: {0}", rawPath); } if (_forceFail) { WriteText(response, 500, "Forced failure (--fail). Payload saved."); return; } if (items == null) { WriteText(response, 400, "Invalid JSON: " + (err ?? "unknown")); return; } WriteText(response, 200, "OK " + items.Count + " item(s)"); } catch (Exception ex) { try { WriteText(response, 500, ex.Message); } catch { } } finally { try { response.OutputStream.Close(); } catch { } } } /// 强制按 UTF-8 读 Body,避免 HttpListener 在无 charset 时按系统默认编码误读。 private static string ReadRequestBodyAsUtf8(HttpListenerRequest request) { using (var ms = new MemoryStream()) { request.InputStream.CopyTo(ms); byte[] bytes = ms.ToArray(); if (bytes.Length >= 3 && bytes[0] == 0xEF && bytes[1] == 0xBB && bytes[2] == 0xBF) { return Encoding.UTF8.GetString(bytes, 3, bytes.Length - 3); } return Encoding.UTF8.GetString(bytes); } } private static void WriteText(HttpListenerResponse response, int status, string text) { byte[] buf = Encoding.UTF8.GetBytes(text ?? string.Empty); response.StatusCode = status; response.ContentType = "text/plain; charset=utf-8"; response.ContentEncoding = Encoding.UTF8; response.ContentLength64 = buf.Length; response.OutputStream.Write(buf, 0, buf.Length); } } }