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);
}
}
}