写点什么

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

作者:八苦-瞿昙
  • 2025-03-08
    四川
  • 本文字数:2679 字

    阅读完需:约 9 分钟

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

24 年终自己立了 flag: 25 年做些轮子玩(用于浪费生命,赚不了钱)

所以 25 年就准备用 c#写一个网络代理NZOrz(nginx 知道吧,就那玩意儿干的事),包含 udp/tcp/http1 2 3,

至于为啥不用 rust 写,主要由于某台电脑某些不可告知的原因不方便安装 rust,所以等我写完 c#的,后面有空再说吧(应该 25 年没时间了吧)

代码借鉴出处

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


  1. 借鉴 Kestrel 的 socket 处理核心 (理论上基于 Kestrel 也是可行的)

  2. 借鉴 Yarp 各项代理处理 (实现只有 http)


所以整体实现上都是 socket 上层做多线程处理,不编写与系统内核交互或者其他 io 事件库打交道的代码(为啥?要打跨平台交道,我不如直接用 rust 写 linux 的,window 写不写看心情,反正服务器是王道是不是)

局限

不得不先提一个局限,dotnet 的 socket 没有提供统一的跨进程 socket 转移 api,因为 dotnet 是跨平台的,不同系统存在差异,该 issue Migrate Socket between processes 已经多年没有下文了


所以不好做到热重启

初步完成进度

  • [X] TCP server core

  • [X] TCP proxy core

  • [X] dns (use system dns, no query from dns server )

  • [X] LoadBalancingPolicy

  • [X] Passive HealthCheck

  • [X] TCP Connected Active HealthCheck

  • [X] Configuration

  • [X] reload config and rebind

  • [X] 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


dotnet add package NZ.Orz --version 0.0.0.2-beta
复制代码


入口代码


using Microsoft.Extensions.DependencyInjection;using Microsoft.Extensions.Hosting;using NZ.Orz;using NZ.Orz.ReverseProxy.L4;
var app = NZApp.CreateBuilder(args) .UseJsonConfig() .Build();
await app.RunAsync();
复制代码


配置 文件 appsettings.json


{  "Logging": {    "LogLevel": {      "Default": "Information"    }  },  "ReverseProxy": {    "Routes": {      "apidemo": {        "Protocols": "TCP",        "Match": {          "Hosts": [ "*:5000" ]        },        "ClusterId": "apidemo",        "RetryCount": 1,        "Timeout": "00:00:11"      }    },    "Clusters": {      "apidemo": {        "LoadBalancingPolicy": "RoundRobin",        "HealthCheck": {          "Active": {            "Enable": false,            "Policy": "Connect"          }        },        "Destinations": [          {            "Address": "[::1]:5144"          },          {            "Address": "[::1]:5146"          },          {            "Address": "google.com:998"          },          {            "Address": "www.baidu.com"          },          {            "Address": "http://google.com"          },          {            "Address": "https://google.com"          }        ]      }    }  }}
复制代码


然后启动就行, 启动 log 大致如下


info: NZ.Orz.Server.ReverseProxy[18]      Config changed. Starting the following endpoints: [Protocols: TCP,Route: apidemo,EndPoint: 0.0.0.0:5000],[Protocols: TCP,Route: apidemo,EndPoint: [::]:5000]info: Microsoft.Hosting.Lifetime[0]      Application started. Press Ctrl+C to shut down.info: Microsoft.Hosting.Lifetime[0]      Hosting environment: Productioninfo: Microsoft.Hosting.Lifetime[0]      Content root path: D:\code\edge\l4proxy\src\L4Proxy\bin\Debug\net8.0
复制代码


当然,运行中 如果改动appsettings.json内容,会根据配置 重新监听变动端口/重建路由表等等


也算是一定程度弥补无法热重启的问题

改动 tcp 的数据

如果想改动 tcp 的数据,可以实现中间件 ITcpMiddleware


比如


public class EchoMiddleware : ITcpMiddleware{    public int Order => 0;
public Task<ReadOnlyMemory<byte>> OnRequest(ConnectionContext connection, ReadOnlyMemory<byte> source, CancellationToken cancellationToken, TcpConnectionDelegate next) { Console.WriteLine($"{DateTime.Now} {connection.LocalEndPoint.ToString()} request size: {source.Length}"); return Task.FromResult(source); }
public Task<ReadOnlyMemory<byte>> OnResponse(ConnectionContext connection, ReadOnlyMemory<byte> source, CancellationToken cancellationToken, TcpConnectionDelegate next) { Console.WriteLine($"{DateTime.Now} {connection.SelectedDestination.EndPoint.ToString()} reponse size: {source.Length}"); //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(); //connection.Abort(); return Task.FromResult(source); }}
复制代码


然后注入 ioc 就行


var app = NZApp.CreateBuilder(args)    .ConfigServices(services =>    {        services.AddSingleton<ITcpMiddleware, EchoMiddleware>();    })    .UseJsonConfig()    .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 呢?毕竟借鉴代码也不易呀 哈哈哈哈哈

发布于: 刚刚阅读数: 5
用户头像

八苦-瞿昙

关注

一个假和尚,不懂人情世故。 2018-11-23 加入

会点点技术,能写些代码,只爱静静。 g hub: https://github.com/fs7744 黑历史:https://www.cnblogs.com/fs7744

评论

发布
暂无评论
终于写完轮子一部分:tcp代理 了,记录一下_csharp_八苦-瞿昙_InfoQ写作社区