using System; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; namespace IvfTl.ControlHost { /// /// control 进程内的本地 HTTP 小服务,只监听 127.0.0.1:port。 /// 阶段1:/ping、/status。阶段2 扩展 /serial/pause|resume、/shutdown。 /// public class ControlHttpServer { private readonly int _port; private readonly Func _statusProvider; private readonly Action _log; private HttpListener _listener; private CancellationTokenSource _cts; public ControlHttpServer(int port, Func statusProvider, Action log) { _port = port; _statusProvider = statusProvider; _log = log ?? (_ => { }); } public void Start() { _listener = new HttpListener(); _listener.Prefixes.Add($"http://127.0.0.1:{_port}/"); _listener.Start(); _cts = new CancellationTokenSource(); _log($"ControlHttpServer 监听 http://127.0.0.1:{_port}/"); Task.Run(() => Loop(_cts.Token)); } private async Task Loop(CancellationToken token) { while (!token.IsCancellationRequested) { HttpListenerContext ctx; try { ctx = await _listener.GetContextAsync(); } catch (Exception ex) { if (!token.IsCancellationRequested) _log("HttpListener 异常:" + ex.Message); break; } try { Handle(ctx); } catch (Exception ex) { _log("处理请求异常:" + ex.Message); } } } private void Handle(HttpListenerContext ctx) { string path = ctx.Request.Url.AbsolutePath.TrimEnd('/').ToLowerInvariant(); string body; int code = 200; switch (path) { case "/ping": case "/status": body = JsonConvert.SerializeObject(_statusProvider()); break; default: code = 404; body = "{\"ok\":false,\"error\":\"not found\"}"; break; } byte[] buf = Encoding.UTF8.GetBytes(body); ctx.Response.StatusCode = code; ctx.Response.ContentType = "application/json"; ctx.Response.ContentLength64 = buf.Length; ctx.Response.OutputStream.Write(buf, 0, buf.Length); ctx.Response.OutputStream.Close(); } public void Stop() { try { _cts?.Cancel(); _listener?.Stop(); _listener?.Close(); } catch (Exception ex) { _log("ControlHttpServer 停止异常:" + ex.Message); } } } }