找回密码
 立即注册
首页 业界区 业界 终于写完轮子一部分:tcp代理 了,记录一下 ...

终于写完轮子一部分:tcp代理 了,记录一下

固拆棚 2025-6-4 21:57:05
24年终自己立了flag: 25年做些轮子玩(用于浪费生命,赚不了钱)
所以25年就准备用c#写一个网络代理NZOrz(nginx知道吧,就那玩意儿干的事),包含 udp/tcp/http1 2 3,
至于为啥不用rust写,主要由于某台电脑某些不可告知的原因不方便安装rust,所以等我写完c#的,后面有空再说吧(应该25年没时间了吧)
代码借鉴出处

秉承将生命浪费到底的造轮子精神,实力不行就尽可能借鉴(抄袭,读书人的事,怎么能说呢)
所以这里首先列举一下借鉴出处

  • 借鉴 Kestrel 的 socket 处理核心 (理论上基于Kestrel也是可行的)
  • 借鉴 Yarp 各项代理处理  (实现只有http)
所以整体实现上都是socket 上层做多线程处理,不编写与系统内核交互或者其他io事件库打交道的代码
(为啥?要打跨平台交道,我不如直接用 rust 写 linux的,window写不写看心情,反正服务器是王道是不是)
局限

不得不先提一个局限,dotnet 的socket 没有提供统一的跨进程socket转移api,因为dotnet是跨平台的,不同系统存在差异,该issue Migrate Socket between processes 已经多年没有下文了
所以不好做到热重启
初步完成进度


  • TCP server core
  • TCP proxy core
  • dns (use system dns, no query from dns server )
  • LoadBalancingPolicy
  • Passive HealthCheck
  • TCP Connected Active HealthCheck
  • Configuration
  • reload config and rebind
  • Log
  • UDP server core
  • Config Validators
  • UDP proxy core
  • HTTP1 server core
  • HTTP2 server core
  • HTTP3 server core
  • HTTP proxy core
  • Metrics
对,目前主要是完成了基础的 tcp 部分(代理协议不支持,毕竟有那么多,时间也有限),下一步以 udp 为优先,(文档吗?等我先完成再说)
tcp代理使用

目前没有提供现成打包好的exe或者docker镜像,毕竟离完成还有很远的距离
要玩可以这样
建一个 net8.0 或者net9.0 的Console 项目
安装package
  1. dotnet add package NZ.Orz --version 0.0.0.2-beta
复制代码
入口代码
  1. using Microsoft.Extensions.DependencyInjection;
  2. using Microsoft.Extensions.Hosting;
  3. using NZ.Orz;
  4. using NZ.Orz.ReverseProxy.L4;
  5. var app = NZApp.CreateBuilder(args)
  6.     .UseJsonConfig()
  7.     .Build();
  8. await app.RunAsync();
复制代码
配置 文件 appsettings.json
  1. {
  2.   "Logging": {
  3.     "LogLevel": {
  4.       "Default": "Information"
  5.     }
  6.   },
  7.   "ReverseProxy": {
  8.     "Routes": {
  9.       "apidemo": {
  10.         "Protocols": "TCP",
  11.         "Match": {
  12.           "Hosts": [ "*:5000" ]
  13.         },
  14.         "ClusterId": "apidemo",
  15.         "RetryCount": 1,
  16.         "Timeout": "00:00:11"
  17.       }
  18.     },
  19.     "Clusters": {
  20.       "apidemo": {
  21.         "LoadBalancingPolicy": "RoundRobin",
  22.         "HealthCheck": {
  23.           "Active": {
  24.             "Enable": false,
  25.             "Policy": "Connect"
  26.           }
  27.         },
  28.         "Destinations": [
  29.           {
  30.             "Address": "[::1]:5144"
  31.           },
  32.           {
  33.             "Address": "[::1]:5146"
  34.           },
  35.           {
  36.             "Address": "google.com:998"
  37.           },
  38.           {
  39.             "Address": "www.baidu.com"
  40.           },
  41.           {
  42.             "Address": "http://google.com"
  43.           },
  44.           {
  45.             "Address": "https://google.com"
  46.           }
  47.         ]
  48.       }
  49.     }
  50.   }
  51. }
复制代码
然后启动就行, 启动log大致如下
  1. info: NZ.Orz.Server.ReverseProxy[18]
  2.       Config changed. Starting the following endpoints: [Protocols: TCP,Route: apidemo,EndPoint: 0.0.0.0:5000],[Protocols: TCP,Route: apidemo,EndPoint: [::]:5000]
  3. info: Microsoft.Hosting.Lifetime[0]
  4.       Application started. Press Ctrl+C to shut down.
  5. info: Microsoft.Hosting.Lifetime[0]
  6.       Hosting environment: Production
  7. info: Microsoft.Hosting.Lifetime[0]
  8.       Content root path: D:\code\edge\l4proxy\src\L4Proxy\bin\Debug\net8.0
复制代码
当然,运行中 如果改动appsettings.json内容,会根据配置 重新监听变动端口/重建路由表等等
也算是一定程度弥补无法热重启的问题
改动tcp的数据

如果想改动tcp的数据,可以实现中间件 ITcpMiddleware
比如
  1. public class EchoMiddleware : ITcpMiddleware
  2. {
  3.     public int Order => 0;
  4.     public Task<ReadOnlyMemory<byte>> OnRequest(ConnectionContext connection, ReadOnlyMemory<byte> source, CancellationToken cancellationToken, TcpConnectionDelegate next)
  5.     {
  6.         Console.WriteLine($"{DateTime.Now} {connection.LocalEndPoint.ToString()} request size: {source.Length}");
  7.         return Task.FromResult(source);
  8.     }
  9.     public Task<ReadOnlyMemory<byte>> OnResponse(ConnectionContext connection, ReadOnlyMemory<byte> source, CancellationToken cancellationToken, TcpConnectionDelegate next)
  10.     {
  11.         Console.WriteLine($"{DateTime.Now} {connection.SelectedDestination.EndPoint.ToString()} reponse size: {source.Length}");
  12.         //source = Encoding.UTF8.GetBytes("HTTP/1.1 400 Bad Request\r\nDate: Sun, 18 Oct 2012 10:36:20 GMT\r\nServer: Apache/2.2.14 (Win32)\r\nContent-Length: 0\r\nContent-Type: text/html; charset=iso-8859-1\r\nConnection: Closed\r\n\r\n").AsMemory();
  13.         //connection.Abort();
  14.         return Task.FromResult(source);
  15.     }
  16. }
复制代码
然后注入ioc就行
  1. var app = NZApp.CreateBuilder(args)
  2.     .ConfigServices(services =>
  3.     {
  4.         services.AddSingleton<ITcpMiddleware, EchoMiddleware>();
  5.     })
  6.     .UseJsonConfig()
  7.     .Build();
复制代码
配置简单说明

详细等以后写文档再说吧
Protocols 支持 TCP
Hosts 支持后缀匹配, 比如匹配所有实例5000端口就可以写 *:5000, 匹配某个实例如 192.1.1.1,3000端口就可以写 192.1.1.1:3000
(路由表实现采用 前缀树+字典+SIEVE cahce)
服务发现目前只支持 DNS, 但不支持指定 dns server, 因为 dns不支持,以后再说吧
HealthCheck 支持主动 被动 二选一,不支持一起用, 主动 暂时只支持 socket connect 成功检查
LoadBalancingPolicy 支持四种 Random , RoundRobin , LeastRequests , PowerOfTwoChoices
先就这样,其他等我慢慢实现
大家有空的话,能否在 GitHub https://github.com/fs7744/NZOrz 点个 star 呢?毕竟借鉴代码也不易呀 哈哈哈哈哈

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册