first commit
This commit is contained in:
434
Ultron.Proxy.Server/ServerHost.cs
Normal file
434
Ultron.Proxy.Server/ServerHost.cs
Normal file
@@ -0,0 +1,434 @@
|
||||
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
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user