Ultron.Proxy/Ultron.Proxy.Server/ServerHost.cs

434 lines
16 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Ultron.Proxy.Utils;
using Ultron.Proxy.Interfaces;
namespace Ultron.Proxy
{
//+------------------------+
//| NAT |
//| |
//| |
//| +----------+ | +-----------+
//| | | | | |
//| | client |------------> provider |
//| | | | | |
//| +----+-----+ | +------^----+
//| | | |
//| | | |
//| | | |
//| +----V-----+ | |
//| | | | |
//| | IIS | | |
//| | | | |
//| +----------+ | +------+-------+
//| | | |
//| | | consumer |
//| | | |
//+------------------------+ +--------------+
public class ServerHost
{
//服务端代理转发端口
public static int ClientServicePort = 9973;
//服务端配置通讯端口
public static int ConfigServicePort = 12307;
//远端管理端口
public static int WebManagementPort = 0;
public ClientConnectionManager ConnectionManager = null;
//inject
internal static ILogger Logger;
public ServerHost(ILogger logger)
{
Logger = logger;
}
//必须设置远程端口才可以通信
public ServerHost SetWebPort(int port)
{
WebManagementPort = port;
return this;
}
public async Task Start()
{
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
CancellationTokenSource ctsConfig = new CancellationTokenSource();
CancellationTokenSource ctsHttp = new CancellationTokenSource();
CancellationTokenSource ctsConsumer = new CancellationTokenSource();
//1.反向连接池配置
ConnectionManager = ClientConnectionManager.GetInstance();
//注册客户端发生连接时的事件
ConnectionManager.AppTcpClientMapConfigConnected += ConnectionManager_AppAdded;
Logger.Debug("Ultron.Proxy server started");
//2.开启http服务
if (WebManagementPort > 0)
StartHttpService(ctsHttp);
//3.开启配置服务
try
{
await StartConfigService(ctsConfig);
}
catch (Exception ex)
{
Logger.Debug(ex.Message);
}
finally
{
Logger.Debug("all closed");
ctsConfig.Cancel();
//listenerConsumer.Stop();
}
////4.通过已配置的端口集合开启侦听
//foreach (var kv in ConnectionManager.PortAppMap)
//{
// ListenConsumeAsync(kv.Key, ctsConsumer.Token);
//}
}
private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
Logger.Error(e.Exception.ToString(), e.Exception);
}
#region HTTPServer
private async Task StartHttpService(CancellationTokenSource ctsHttp)
{
try
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add($"http://127.0.0.1:{WebManagementPort}/");
//TcpListener listenerConfigService = new TcpListener(IPAddress.Any, WebManagementPort);
Logger.Debug("Listening HTTP request on port " + WebManagementPort.ToString() + "...");
await AcceptHttpRequest(listener, ctsHttp);
}
catch (HttpListenerException ex)
{
Logger.Debug("Please run this program in administrator mode." + ex);
ServerHost.Logger.Error(ex.ToString(), ex);
}
catch (Exception ex)
{
Logger.Debug(ex);
ServerHost.Logger.Error(ex.ToString(), ex);
}
}
private async Task AcceptHttpRequest(HttpListener httpService, CancellationTokenSource ctsHttp)
{
httpService.Start();
while (true)
{
var client = await httpService.GetContextAsync();
ProcessHttpRequestAsync(client);
}
}
private async Task ProcessHttpRequestAsync(HttpListenerContext context)
{
try
{
var request = context.Request;
var response = context.Response;
response.ContentEncoding = Encoding.UTF8;
response.ContentType = "text/html;charset=utf-8";
//getJson
StringBuilder json = new StringBuilder("[ ");
foreach (var app in this.ConnectionManager.PortAppMap)
{
json.Append("{ ");
json.Append(KV2Json("port", app.Key)).C();
json.Append(KV2Json("clientId", app.Value.ClientIdAppId.ClientID)).C();
json.Append(KV2Json("appId", app.Value.ClientIdAppId.AppID)).C();
//反向连接
json.Append(KV2Json("revconns"));
json.Append("[ ");
foreach (var reverseClient in app.Value.ReverseClients)
{
json.Append("{ ");
if (reverseClient.Connected)
{
json.Append(KV2Json("lEndPoint", reverseClient.Client.LocalEndPoint.ToString())).C();
json.Append(KV2Json("rEndPoint", reverseClient.Client.RemoteEndPoint.ToString()));
}
//json.Append(KV2Json("p", c)).C();
//json.Append(KV2Json("port", ca.Key));
json.Append("}");
json.C();
}
json.D();
json.Append("]").C(); ;
//隧道状态
json.Append(KV2Json("tunnels"));
json.Append("[ ");
foreach (var tunnel in app.Value.Tunnels)
{
json.Append("{ ");
if (tunnel.ClientServerClient.Connected)
json.Append(KV2Json("clientServerClient", tunnel.ClientServerClient?.Client.LocalEndPoint.ToString())).C();
if (tunnel.ConsumerClient.Connected)
json.Append(KV2Json("consumerClient", tunnel.ConsumerClient?.Client.LocalEndPoint.ToString())).C();
json.D();
//json.Append(KV2Json("p", c)).C();
//json.Append(KV2Json("port", ca.Key));
json.Append("}");
json.C();
}
json.D();
json.Append("]");
json.Append("}").C();
}
json.D();
json.Append("]");
await response.OutputStream.WriteAsync(HtmlUtil.GetContent(json.ToString()));
//await response.OutputStream.WriteAsync(HtmlUtil.GetContent(request.RawUrl));
response.OutputStream.Close();
}
catch (Exception e)
{
Logger.Error(e.Message, e);
throw;
}
}
private string KV2Json(string key)
{
return "\"" + key + "\":";
}
private string KV2Json(string key, object value)
{
return "\"" + key + "\":\"" + value.ToString() + "\"";
}
#endregion
private async Task StartConfigService(CancellationTokenSource accepting)
{
TcpListener listenerConfigService = new TcpListener(IPAddress.Any, ConfigServicePort);
Logger.Debug("Listening config request on port " + ConfigServicePort.ToString() + "...");
var taskResultConfig = AcceptConfigRequest(listenerConfigService);
await taskResultConfig; //block here to hold open the server
}
/// <summary>
/// 有连接连上则开始侦听新的端口
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ConnectionManager_AppAdded(object sender, AppChangedEventArgs e)
{
Logger.Debug("AppTcpClientMapReverseConnected事件已触发");
int port = 0;
foreach (var kv in ConnectionManager.PortAppMap)
{
if (kv.Value.ClientIdAppId.AppID == e.App.AppID &&
kv.Value.ClientIdAppId.ClientID == e.App.ClientID) port = kv.Key;
}
if (port == 0) throw new Exception("app未注册");
var ct = new CancellationToken();
ListenConsumeAsync(port, ct);
}
#region
//配置服务,客户端可以通过这个服务接收现有的空闲端口
//accept a config request.
//request:
// 2 1 1
// clientid appid nouse
//
//response:
// 2 1 1 ...N
// clientid appid port
private async Task AcceptConfigRequest(TcpListener listenerConfigService)
{
listenerConfigService.Start(100);
while (true)
{
var client = await listenerConfigService.AcceptTcpClientAsync();
ProcessConfigRequestAsync(client);
}
}
private async Task ProcessConfigRequestAsync(TcpClient client)
{
try
{
//长度固定4个字节
int configRequestLength = 3;
byte[] appRequestBytes = new byte[configRequestLength];
Logger.Debug("config request received.");
var nstream = client.GetStream();
//1.读取配置请求1
int resultByte = await nstream.ReadAsync(appRequestBytes);
Logger.Debug("appRequestBytes received.");
if (resultByte == 0)
{
CloseClient(client);
return;
}
//2.根据配置请求1获取更多配置信息
int appCount = (int)appRequestBytes[2];
byte[] consumerPortBytes = new byte[appCount * 2];
int resultByte2 = await nstream.ReadAsync(consumerPortBytes);
Logger.Debug("consumerPortBytes received.");
if (resultByte2 == 0)
{
CloseClient(client);
return;
}
//3.分配配置ID并且写回给客户端
try
{
byte[] arrangedIds = ConnectionManager.ArrageConfigIds(appRequestBytes, consumerPortBytes);
Logger.Debug("apprequest arranged");
await nstream.WriteAsync(arrangedIds);
}
catch (Exception ex)
{ Logger.Debug(ex.ToString()); }
Logger.Debug("arrangedIds written.");
}
catch (Exception e)
{
Logger.Debug(e);
throw;
}
}
#endregion
/// <summary>
/// 同时侦听来自consumer的链接和到provider的链接
/// </summary>
/// <param name="consumerlistener"></param>
/// <param name="ct"></param>
/// <returns></returns>
async Task ListenConsumeAsync(int consumerPort, CancellationToken ct)
{
try
{
var consumerlistener = new TcpListener(IPAddress.Any, consumerPort);
consumerlistener.Start(1000);
//给两个listen同时监听3端
var clientCounter = 0;
while (!ct.IsCancellationRequested)
{
//目标的代理服务联通了才去处理consumer端的请求。
Logger.Debug("listening serviceClient....Port:" + consumerPort);
TcpClient consumerClient = await consumerlistener.AcceptTcpClientAsync();
//记录tcp隧道消费端
TcpTunnel tunnel = new TcpTunnel();
tunnel.ConsumerClient = consumerClient;
ClientConnectionManager.GetInstance().PortAppMap[consumerPort].Tunnels.Add(tunnel);
Logger.Debug("consumer已连接" + consumerClient.Client.RemoteEndPoint.ToString());
//消费端连接成功,连接
//需要端口
TcpClient s2pClient = await ConnectionManager.GetClient(consumerPort);
//记录tcp隧道客户端
tunnel.ClientServerClient = s2pClient;
//✳关键过程✳
//连接完之后发送一个字节过去促使客户端建立转发隧道
await s2pClient.GetStream().WriteAsync(new byte[] { 1 }, 0, 1);
clientCounter++;
TcpTransferAsync(consumerlistener, consumerClient, s2pClient, clientCounter, ct);
}
}
catch (Exception e)
{
Logger.Debug(e);
}
}
#region datatransfer
//3端互相传输数据
async Task TcpTransferAsync(TcpListener consumerlistener, TcpClient consumerClient, TcpClient providerClient,
int clientIndex,
CancellationToken ct)
{
try
{
ServerHost.Logger.Debug($"New client ({clientIndex}) connected");
CancellationTokenSource transfering = new CancellationTokenSource();
var providerStream = providerClient.GetStream();
var consumerStream = consumerClient.GetStream();
Task taskC2PLooping = ToStaticTransfer(transfering.Token, consumerStream, providerStream);
Task taskP2CLooping = StreamTransfer(transfering.Token, providerStream, consumerStream);
//任何一端传输中断或者故障,则关闭所有连接
var comletedTask = await Task.WhenAny(taskC2PLooping, taskP2CLooping);
//comletedTask.
Logger.Debug($"Transferring ({clientIndex}) STOPPED");
consumerClient.Close();
providerClient.Close();
transfering.Cancel();
}
catch (Exception e)
{
Logger.Debug(e);
throw;
}
}
private async Task StreamTransfer(CancellationToken ct, NetworkStream fromStream, NetworkStream toStream)
{
await fromStream.CopyToAsync(toStream, ct);
}
private async Task ToStaticTransfer(CancellationToken ct, NetworkStream fromStream, NetworkStream toStream, Func<byte[], Task<bool>> beforeTransferHandle = null)
{
await fromStream.CopyToAsync(toStream, ct);
}
private void CloseClient(TcpClient client)
{
Logger.Debug("invalid request,Closing client:" + client.Client.RemoteEndPoint.ToString());
client.Close();
Logger.Debug("Closed client:" + client.Client.RemoteEndPoint.ToString());
}
#endregion
}
}