目录C# 实现 HTTP 服务器三种方案方案 1内置 HttpListener零第三方库轻量Windows/Linux 跨平台完整示例关键说明方案 2ASP.NET Core工业级、推荐正式项目最简控制台 Web 服务方案 3Socket 原生手写 HTTP底层原理学习不推荐生产三种方案选型对比常用扩展补充1. HttpListener 开启 HTTPS2. 并发优化3. 静态文件返回HttpListener项目中已验证过实例(复制过去即可用)Nancy 自动加载模块底层原理使用完整封装 HttpListener HTTP 服务类使用说明1. 访问测试2. 内置能力清单3. 端口权限说明4. 扩展方向按需自己加C# 实现 HTTP 服务器三种方案方案 1内置HttpListener零第三方库轻量Windows/Linux 跨平台无需 NuGet.NET Framework /.NET Core /.NET 5 通用适合小型接口、本地工具服务。完整示例using System; using System.Net; using System.Text; using System.Threading.Tasks; class SimpleHttpServer { static async Task Main(string[] args) { // 监听地址末尾必须带 / string prefix http://localhost:8080/; using HttpListener listener new HttpListener(); listener.Prefixes.Add(prefix); try { listener.Start(); Console.WriteLine($服务启动{prefix}); Console.WriteLine(按任意键停止服务); // 循环接收请求 while (true) { // 异步等待客户端请求 HttpListenerContext ctx await listener.GetContextAsync(); // 新开线程处理请求不阻塞下一个连接 _ ProcessRequestAsync(ctx); } } catch (HttpListenerException ex) { Console.WriteLine($启动失败{ex.Message}); Console.WriteLine(Windows需要管理员权限或更换端口); } finally { listener.Stop(); } } /// summary处理单个HTTP请求/summary static async Task ProcessRequestAsync(HttpListenerContext ctx) { HttpListenerRequest req ctx.Request; HttpListenerResponse resp ctx.Response; try { // 1. 获取请求信息 string path req.Url.AbsolutePath; string method req.HttpMethod; Console.WriteLine($[{DateTime.Now}] {method} {path}); // 2. 路由分发 string responseText path switch { / 首页 Hello HttpListener, /api/info {\msg\:\接口数据\,\code\:200}, _ 404 Not Found }; // 3. 设置响应头 if (path.StartsWith(/api/)) { resp.ContentType application/json;charsetutf-8; } else { resp.ContentType text/html;charsetutf-8; } resp.StatusCode path /404 ? 404 : 200; byte[] buffer Encoding.UTF8.GetBytes(responseText); resp.ContentLength64 buffer.Length; // 4. 输出响应内容 await resp.OutputStream.WriteAsync(buffer); } catch (Exception ex) { Console.WriteLine($请求异常{ex.Message}); resp.StatusCode 500; } finally { resp.OutputStream.Close(); resp.Close(); } } }关键说明权限问题Windows监听80、443需要管理员运行程序自定义 8080 等端口一般无需权限Linux监听 1024 以下端口需 sudo跨域支持加在响应头csharp运行resp.Headers.Add(Access-Control-Allow-Origin, *); resp.Headers.Add(Access-Control-Allow-Methods, GET,POST,OPTIONS); if (method OPTIONS) { resp.StatusCode 204; return; }读取 POST 表单 / JSONusing var reader new StreamReader(req.InputStream, Encoding.UTF8); string postBody await reader.ReadToEndAsync();方案 2ASP.NET Core工业级、推荐正式项目功能完整路由、中间件、静态文件、鉴权、Swagger、HTTPS适合生产服务。最简控制台 Web 服务创建项目dotnet new console dotnet add package Microsoft.AspNetCoreProgram.csusing Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; var builder WebApplication.CreateBuilder(args); var app builder.Build(); // 路由 app.MapGet(/, () Hello ASP.NET Core Http Server); app.MapGet(/api/data, () Results.Json(new { code 200, data 测试接口 })); app.MapPost(/api/post, async (HttpRequest r) { string body await r.ReadAsStringAsync(); return Results.Ok(new { receive body }); }); // 监听端口 app.Run(http://localhost:8080);运行dotnet run直接完整 HTTP 服务。方案 3Socket 原生手写 HTTP底层原理学习不推荐生产手动解析 TCP 流、构造 HTTP 报文仅学习网络底层原理使用using System; using System.Net; using System.Net.Sockets; using System.Text; class RawSocketHttp { static void Main() { TcpListener server new TcpListener(IPAddress.Loopback, 8080); server.Start(); Console.WriteLine(原生Socket HTTP服务 8080); while (true) { TcpClient client server.AcceptTcpClient(); _ HandleClient(client); } } static async Task HandleClient(TcpClient client) { using client; var stream client.GetStream(); byte[] buf new byte[4096]; int len await stream.ReadAsync(buf); string req Encoding.UTF8.GetString(buf, 0, len); Console.WriteLine(req); // 构造HTTP响应报文 string html h1Raw Socket Http/h1; string response $HTTP/1.1 200 OK\r\nContent-Type:text/html;charsetutf-8\r\nContent-Length:{html.Length}\r\n\r\n{html}; byte[] resBuf Encoding.UTF8.GetBytes(response); await stream.WriteAsync(resBuf); } }缺点需要手动处理分包、Cookie、编码、长连接、POST 解析、路由开发成本极高。三种方案选型对比表格方案优点缺点使用场景HttpListener无第三方包、轻量、上手快路由 / 中间件需自己封装小工具、本地后台、简易接口ASP.NET Core成熟框架、路由 / 中间件 / ORM、高性能依赖 AspNetCore 库WebAPI、后台服务、生产项目原生 Socket完全可控底层重复造轮子、易出 BUG学习 TCP/HTTP 协议极少商用常用扩展补充1. HttpListener 开启 HTTPS需要先给端口绑定证书Windowsnetsh http/ Linux openssl前缀改为https://localhost:8443/。2. 并发优化示例中使用_ ProcessRequestAsync(ctx)实现多请求并发不会串行阻塞。3. 静态文件返回HttpListener读取本地文件流写入resp.OutputStream配合Content-Type区分图片、js、css。项目中已验证过实例(复制过去即可用)基于 Nancy 框架写的简易 HTTP 接口服务Nancy 自动加载模块底层原理当执行new NancyHost(uri)创建宿主时Nancy 内部会执行反射逻辑扫描当前程序集当前 exe/dll里所有继承自 NancyModule 的公共类自动实例化这个Module对象自动读取构造函数里写的Post(/, 处理委托)注册 HTTP 路由全程不需要你写任何调用、实例化代码框架自动完成。public class Module : NancyModule { private LazyITestDataServer SqlSugar field ?? App.StaServices.ResolveLazyITestDataServer(); private LazyILogger Logger field ?? App.StaServices.ResolveLazyILogger(); private object uplock new object(); class RecvData { public string Code { get; set; } public TestInfo[] TestInfo { get; set; } } public Module() { base.Post(/, x { RecvData[] recvDatas; try { string recvdata Request.Body.AsString(); //.Value.Information(收到插入请求: {请求}, recvdata); recvDatas JsonConvert.DeserializeObjectRecvData[](recvdata); } catch (Exception ex) { Logger.Value.Information($收到CCD插入请求数据解析失败错误:{ex.Message}); return $Json 解析失败 {ex.Message}; } if (recvDatas null || recvDatas.Length 0) return $数据长度为空; try { foreach (var item in recvDatas) { if (item.Code null) { Logger.Value.Information($收到CCD插入请求:条码为空!!!); return 条码为空; } if (item.TestInfo.Length null || item.TestInfo.Length 0) { Logger.Value.Information($收到CCD插入请求:{item.Code},数据为空); return Value为 空; } // item.TestInfo.Value Encoding.UTF8.GetString(Encoding.Default.GetBytes(item.TestInfo.Value)); var testinfo SqlSugar.Value.GetBatTestInfo(item.Code); if (testinfo null) { Logger.Value.Information($收到CCD插入请求:{item.Code},条码错误); return 条码错误; } Logger.Value.Information($收到CCD插入请求:{item.Code},数据已插入数据库); SqlSugar.Value.DirectAddTestInfo(testinfo, item.TestInfo); } Thread.Sleep(70); return OK; } catch (Exception ex) { Logger.Value.Information($收到CCD插入请求处理错误:{ex.Message}); return $服务器错误 {ex.Message}; } }); } }使用public void Start() { Task.Run(() { host new NancyHost(new Uri(http://localhost:8088)); host.Start(); }); }完整封装 HttpListener HTTP 服务类功能包含跨域、OPTIONS 预检、GET/POST JSON 解析、路由分发、统一返回格式、404、异常捕获、UTF8 中文不乱码无第三方依赖.NET Framework /.NET Core /.NET 5/6/7/8 通用。using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Text; using System.Text.Json; using System.Threading.Tasks; namespace SimpleHttpServer { /// summary /// 轻量级HTTP服务封装类 /// 基于系统原生HttpListener实现无第三方依赖 /// 内置功能跨域CORS、OPTIONS预检处理、GET/POST JSON路由、统一返回格式、全局异常捕获、并发请求处理 /// /summary public class SimpleHttpServer { /// summary /// 底层HTTP监听器实例 /// /summary private readonly HttpListener _listener; /// summary /// 服务监听地址前缀集合例如 http://localhost:8080/ /// /summary private readonly string[] _prefixes; /// summary /// 路由字典 /// Key格式请求方法:请求路径例GET:/hello、POST:/api/login /// Value当前路由对应的业务处理委托接收请求对象返回统一API结果 /// /summary private readonly Dictionarystring, FuncHttpListenerRequest, TaskApiResult _routeMap new(); /// summary /// 构造函数初始化HTTP服务并绑定监听地址 /// /summary /// param nameprefixes监听地址多个地址可传入多个参数地址末尾必须带 //param public SimpleHttpServer(params string[] prefixes) { _prefixes prefixes; _listener new HttpListener(); // 将所有监听地址注册到监听器 foreach (var pre in prefixes) { _listener.Prefixes.Add(pre); } } #region 路由注册方法 /// summary /// 注册GET类型接口路由 /// /summary /// param namepath接口路径例/hello/param /// param namehandler接口业务处理方法传入请求对象异步返回统一ApiResult/param public void MapGet(string path, FuncHttpListenerRequest, TaskApiResult handler) { string routeKey $GET:{path}; _routeMap[routeKey] handler; } /// summary /// 注册POST JSON类型接口路由 /// /summary /// param namepath接口路径例/api/login/param /// param namehandler接口业务处理方法传入请求对象异步返回统一ApiResult/param public void MapPost(string path, FuncHttpListenerRequest, TaskApiResult handler) { string routeKey $POST:{path}; _routeMap[routeKey] handler; } #endregion #region 服务启停控制 /// summary /// 启动HTTP监听服务 /// 启动后自动开启异步循环接收客户端请求不会阻塞主线程 /// /summary public void Start() { _listener.Start(); Console.WriteLine($【服务启动成功】监听地址{string.Join(、, _prefixes)}); // 后台异步运行请求循环不阻塞主线程 _ RunLoopAsync(); } /// summary /// 停止HTTP监听服务关闭所有连接 /// /summary public void Stop() { if (_listener.IsListening) _listener.Stop(); Console.WriteLine(【服务已停止】); } /// summary /// 后台循环任务持续等待并接收客户端HTTP连接 /// 每收到一个请求单独开异步任务处理实现并发 /// /summary private async Task RunLoopAsync() { try { // 服务运行期间持续接收请求 while (_listener.IsListening) { // 异步等待客户端连接无请求时会挂起不占用CPU HttpListenerContext ctx await _listener.GetContextAsync(); // 丢弃返回值并行处理请求不阻塞下一次接收 _ HandleRequestAsync(ctx); } } catch (HttpListenerException ex) { Console.WriteLine($【监听循环异常】{ex.Message}); } } #endregion #region 请求核心处理逻辑 /// summary /// 处理单次客户端HTTP请求完整流程 /// 包含跨域头写入、OPTIONS预检拦截、路由匹配、业务执行、JSON响应输出、异常兜底 /// /summary /// param namectx单次请求上下文包含请求Request和响应Response对象/param private async Task HandleRequestAsync(HttpListenerContext ctx) { HttpListenerRequest req ctx.Request; HttpListenerResponse resp ctx.Response; try { // 1. 全局写入跨域响应头解决前端浏览器跨域报错 resp.Headers.Add(Access-Control-Allow-Origin, *); resp.Headers.Add(Access-Control-Allow-Methods, GET,POST,OPTIONS); resp.Headers.Add(Access-Control-Allow-Headers, Content-Type); // 2. 处理浏览器OPTIONS预检请求直接返回204无内容 if (req.HttpMethod.Equals(OPTIONS, StringComparison.OrdinalIgnoreCase)) { resp.StatusCode 204; return; } // 3. 拼接路由匹配键请求方法请求路径 string path req.Url.AbsolutePath; string routeKey ${req.HttpMethod}:{path}; ApiResult result; // 4. 判断是否存在注册好的路由 if (_routeMap.TryGetValue(routeKey, out FuncHttpListenerRequest, TaskApiResult handler)) { // 执行接口业务逻辑拿到返回数据 result await handler.Invoke(req); } else { // 无匹配路由返回404接口不存在 result new ApiResult(404, 请求接口不存在, null); } // 5. 统一以JSON格式返回数据设置UTF8编码避免中文乱码 resp.ContentType application/json;charsetutf-8; // 序列化返回实体格式化输出方便调试 string jsonText JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented true }); byte[] responseBuffer Encoding.UTF8.GetBytes(jsonText); resp.ContentLength64 responseBuffer.Length; // 将JSON字节写入响应输出流返回给客户端 await resp.OutputStream.WriteAsync(responseBuffer); } catch (Exception ex) { // 全局异常捕获业务代码报错统一返回500错误JSON不直接断开连接 ApiResult errorResult new ApiResult(500, $服务器内部异常{ex.Message}, null); resp.ContentType application/json;charsetutf-8; string errorJson JsonSerializer.Serialize(errorResult); byte[] errorBuf Encoding.UTF8.GetBytes(errorJson); resp.ContentLength64 errorBuf.Length; await resp.OutputStream.WriteAsync(errorBuf); // 控制台打印异常详情方便排查 Console.WriteLine($【请求异常】{ex}); } finally { // 无论成功失败最终关闭输出流与响应释放资源 resp.OutputStream.Close(); resp.Close(); } } #endregion #region 工具静态方法 /// summary /// 读取POST请求体内的JSON字符串并自动反序列化为指定实体对象 /// /summary /// typeparam nameT需要反序列化的实体类型/typeparam /// param namereqHTTP请求对象/param /// returns反序列化后的实体请求体为空时返回类型默认值/returns public static async TaskT ReadJsonBodyT(HttpListenerRequest req) { // 使用流读取器读取请求原始流UTF8编码解析 using StreamReader reader new StreamReader(req.InputStream, Encoding.UTF8); string bodyText await reader.ReadToEndAsync(); // 请求体为空直接返回默认值 if (string.IsNullOrWhiteSpace(bodyText)) return default; // JSON字符串转实体 return JsonSerializer.DeserializeT(bodyText); } #endregion } /// summary /// 全局统一API返回数据模型 /// 所有接口固定返回 Code、Msg、Data 三段式JSON结构 /// /summary public class ApiResult { /// summary /// 业务状态码200成功、404接口不存在、500服务器异常、自定义错误码-1等 /// /summary public int Code { get; set; } /// summary /// 提示信息用于前端展示文案 /// /summary public string Msg { get; set; } /// summary /// 业务返回数据主体无数据时为null /// /summary public object Data { get; set; } /// summary /// 构造方法完整赋值返回模型 /// /summary /// param namecode状态码/param /// param namemsg提示消息/param /// param namedata返回数据/param public ApiResult(int code, string msg, object data) { Code code; Msg msg; Data data; } /// summary /// 快速构建成功返回对象 /// /summary /// param namedata要返回的业务数据/param /// param namemsg自定义成功提示默认“操作成功”/param /// returnsCode200的ApiResult实例/returns public static ApiResult Success(object data, string msg 操作成功) { return new ApiResult(200, msg, data); } /// summary /// 快速构建失败返回对象 /// /summary /// param namemsg错误提示文案/param /// param namecode自定义错误码默认-1/param /// returns对应错误码、无Data的ApiResult实例/returns public static ApiResult Fail(string msg, int code -1) { return new ApiResult(code, msg, null); } } #region 测试用实体类 /// summary /// 登录接口接收的POST JSON实体示例 /// /summary public class LoginDto { /// summary /// 账号名 /// /summary public string Username { get; set; } /// summary /// 密码 /// /summary public string Password { get; set; } } #endregion /// summary /// 程序入口测试类演示服务启动、路由注册、接口调用 /// /summary class Program { /// summary /// 程序主入口方法 /// /summary static async Task Main(string[] args) { // 实例化HTTP服务监听本地8080端口 SimpleHttpServer server new SimpleHttpServer(http://localhost:8080/); // 注册GET测试接口 /hello server.MapGet(/hello, req { var returnData new { CurrentTime DateTime.Now.ToString(HH:mm:ss), Tip 这是GET测试接口 }; return Task.FromResult(ApiResult.Success(returnData)); }); // 注册POST登录接口 /api/login server.MapPost(/api/login, async req { // 读取请求JSON并转为LoginDto实体 LoginDto loginParam await SimpleHttpServer.ReadJsonBodyLoginDto(req); // 参数校验 if (loginParam null || string.IsNullOrEmpty(loginParam.Username)) { return ApiResult.Fail(用户名不能为空); } // 模拟登录成功返回Token var loginResult new { Token $token_{Guid.NewGuid()}, UserName loginParam.Username }; return ApiResult.Success(loginResult, 登录成功); }); // 启动HTTP服务开始监听请求 server.Start(); Console.WriteLine(\n提示按键盘任意键即可关闭服务); Console.ReadKey(); // 停止服务释放端口 server.Stop(); } } }使用说明1. 访问测试GET 请求http://localhost:8080/helloPOST JSON 请求Postman/axios 地址http://localhost:8080/api/login请求体json{ Username: admin, Password: 123456 }2. 内置能力清单自动跨域自带 CORS 跨域前端浏览器直接调用无报错OPTIONS 预检自动处理前端带自定义请求头不会 405统一 JSON 返回结构所有接口返回{Code,Msg,Data}POST JSON 读取工具方法ReadJsonBodyT一键解析请求体全局异常捕获代码报错不会直接断开连接返回 500 JSON路由注册分离MapGet / MapPost清晰管理接口并发处理每个请求独立异步 Task不会串行阻塞3. 端口权限说明监听 80、443 等 1024 以下端口Windows 需要管理员运行程序Linux 需要 sudo8080、9090 高位端口普通权限即可运行4. 扩展方向按需自己加文件上传读取req.InputStream二进制流保存本地静态文件访问判断路径是 .html/.js/.png读取本地文件返回流URL 参数解析req.QueryString[key]获取 GET 参数HTTPS 支持构造https://localhost:8443/前缀并绑定证书日志持久化把请求日志写入文本文件