@@ -107,67 +107,30 @@ namespace Shadowsocks.Controller | |||
var bytes = inbound - lastInbound; | |||
_lastInboundCounter[id] = inbound; | |||
var inboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds); | |||
_inboundSpeedRecords.GetOrAdd(id, new List<int> {inboundSpeed}).Add(inboundSpeed); | |||
_inboundSpeedRecords.GetOrAdd(id, (k) => | |||
{ | |||
List<int> records = new List<int>(); | |||
records.Add(inboundSpeed); | |||
return records; | |||
}); | |||
var lastOutbound = _lastOutboundCounter[id]; | |||
var outbound = _outboundCounter[id]; | |||
bytes = outbound - lastOutbound; | |||
_lastOutboundCounter[id] = outbound; | |||
var outboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds); | |||
_outboundSpeedRecords.GetOrAdd(id, new List<int> {outboundSpeed}).Add(outboundSpeed); | |||
_outboundSpeedRecords.GetOrAdd(id, (k) => | |||
{ | |||
List<int> records = new List<int>(); | |||
records.Add(outboundSpeed); | |||
return records; | |||
}); | |||
Logging.Debug( | |||
$"{id}: current/max inbound {inboundSpeed}/{_inboundSpeedRecords[id].Max()} KiB/s, current/max outbound {outboundSpeed}/{_outboundSpeedRecords[id].Max()} KiB/s"); | |||
} | |||
} | |||
private async Task<ICMPResult> ICMPTest(Server server) | |||
{ | |||
Logging.Debug("Ping " + server.FriendlyName()); | |||
if (server.server == "") return null; | |||
var result = new ICMPResult(server); | |||
try | |||
{ | |||
var IP = | |||
Dns.GetHostAddresses(server.server) | |||
.First( | |||
ip => | |||
ip.AddressFamily == AddressFamily.InterNetwork || | |||
ip.AddressFamily == AddressFamily.InterNetworkV6); | |||
var ping = new Ping(); | |||
foreach (var _ in Enumerable.Range(0, Repeat)) | |||
{ | |||
try | |||
{ | |||
var reply = await ping.SendTaskAsync(IP, TimeoutMilliseconds); | |||
if (reply.Status.Equals(IPStatus.Success)) | |||
{ | |||
result.RoundtripTime.Add((int?) reply.RoundtripTime); | |||
} | |||
else | |||
{ | |||
result.RoundtripTime.Add(null); | |||
} | |||
//Do ICMPTest in a random frequency | |||
Thread.Sleep(TimeoutMilliseconds + new Random().Next()%TimeoutMilliseconds); | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.Error($"An exception occured while eveluating {server.FriendlyName()}"); | |||
Logging.LogUsefulException(e); | |||
} | |||
} | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.Error($"An exception occured while eveluating {server.FriendlyName()}"); | |||
Logging.LogUsefulException(e); | |||
} | |||
return result; | |||
} | |||
private void Reset() | |||
{ | |||
_inboundSpeedRecords.Clear(); | |||
@@ -183,7 +146,7 @@ namespace Shadowsocks.Controller | |||
FilterRawStatistics(); | |||
} | |||
private async void UpdateRecords() | |||
private void UpdateRecords() | |||
{ | |||
var records = new Dictionary<string, StatisticsRecord>(); | |||
@@ -202,14 +165,11 @@ namespace Shadowsocks.Controller | |||
records[id] = record; | |||
else | |||
records.Add(id, record); | |||
} | |||
if (Config.Ping) | |||
{ | |||
var icmpResults = await TaskEx.WhenAll(_controller.GetCurrentConfiguration().configs.Select(ICMPTest)); | |||
foreach (var result in icmpResults.Where(result => result != null)) | |||
if (Config.Ping) | |||
{ | |||
records[result.Server.Identifier()].SetResponse(result.RoundtripTime); | |||
MyPing ping = new MyPing(server, Repeat, record); | |||
ping.Completed += ping_Completed; | |||
ping.Start(); | |||
} | |||
} | |||
@@ -219,15 +179,22 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
private void ping_Completed(object sender, MyPing.CompletedEventArgs e) | |||
{ | |||
Server server = ((MyPing)sender).server; | |||
StatisticsRecord record = (StatisticsRecord)((MyPing)sender).userstate; | |||
record.SetResponse(e.RoundtripTime); | |||
} | |||
private void AppendRecord(string serverIdentifier, StatisticsRecord record) | |||
{ | |||
List<StatisticsRecord> records; | |||
if (!RawStatistics.TryGetValue(serverIdentifier, out records)) | |||
{ | |||
records = new List<StatisticsRecord>(); | |||
RawStatistics[serverIdentifier] = records; | |||
} | |||
records.Add(record); | |||
RawStatistics[serverIdentifier] = records; | |||
} | |||
private void Save() | |||
@@ -298,21 +265,10 @@ namespace Shadowsocks.Controller | |||
private static int GetSpeedInKiBPerSecond(long bytes, double seconds) | |||
{ | |||
var result = (int) (bytes/seconds)/1024; | |||
var result = (int)(bytes / seconds) / 1024; | |||
return result; | |||
} | |||
private class ICMPResult | |||
{ | |||
internal readonly List<int?> RoundtripTime = new List<int?>(); | |||
internal readonly Server Server; | |||
internal ICMPResult(Server server) | |||
{ | |||
Server = server; | |||
} | |||
} | |||
public void Dispose() | |||
{ | |||
_recorder.Dispose(); | |||
@@ -321,44 +277,131 @@ namespace Shadowsocks.Controller | |||
public void UpdateLatency(Server server, int latency) | |||
{ | |||
List<int> records; | |||
_latencyRecords.TryGetValue(server.Identifier(), out records); | |||
if (records == null) | |||
_latencyRecords.GetOrAdd(server.Identifier(), (k) => | |||
{ | |||
records = new List<int>(); | |||
} | |||
records.Add(latency); | |||
_latencyRecords[server.Identifier()] = records; | |||
List<int> records = new List<int>(); | |||
records.Add(latency); | |||
return records; | |||
}); | |||
} | |||
public void UpdateInboundCounter(Server server, long n) | |||
{ | |||
long count; | |||
if (_inboundCounter.TryGetValue(server.Identifier(), out count)) | |||
{ | |||
count += n; | |||
} | |||
else | |||
_inboundCounter.AddOrUpdate(server.Identifier(), (k) => | |||
{ | |||
count = n; | |||
_lastInboundCounter[server.Identifier()] = 0; | |||
} | |||
_inboundCounter[server.Identifier()] = count; | |||
_lastInboundCounter.GetOrAdd(server.Identifier(), 0); | |||
return n; | |||
}, (k, v) => (v + n)); | |||
} | |||
public void UpdateOutboundCounter(Server server, long n) | |||
{ | |||
long count; | |||
if (_outboundCounter.TryGetValue(server.Identifier(), out count)) | |||
_outboundCounter.AddOrUpdate(server.Identifier(), (k) => | |||
{ | |||
_lastOutboundCounter.GetOrAdd(server.Identifier(), 0); | |||
return n; | |||
}, (k, v) => (v + n)); | |||
} | |||
class MyPing | |||
{ | |||
//arguments for ICMP tests | |||
public const int TimeoutMilliseconds = 500; | |||
public EventHandler<CompletedEventArgs> Completed; | |||
public Server server; | |||
public object userstate; | |||
private int repeat; | |||
private IPAddress ip; | |||
private Ping ping; | |||
private List<int?> RoundtripTime; | |||
public MyPing(Server server, int repeat, object userstate) | |||
{ | |||
this.server = server; | |||
this.repeat = repeat; | |||
this.userstate = userstate; | |||
RoundtripTime = new List<int?>(repeat); | |||
ping = new Ping(); | |||
ping.PingCompleted += Ping_PingCompleted; | |||
} | |||
public void Start() | |||
{ | |||
Logging.Debug("Ping " + server.FriendlyName()); | |||
if (server.server == "") | |||
return; | |||
new Task(() => ICMPTest(0)).Start(); | |||
} | |||
private void ICMPTest(int delay) | |||
{ | |||
try | |||
{ | |||
if (ip == null) | |||
{ | |||
ip = Dns.GetHostAddresses(server.server) | |||
.First( | |||
ip => | |||
ip.AddressFamily == AddressFamily.InterNetwork || | |||
ip.AddressFamily == AddressFamily.InterNetworkV6); | |||
} | |||
repeat--; | |||
if (delay > 0) | |||
Thread.Sleep(delay); | |||
ping.SendAsync(ip, TimeoutMilliseconds, null); | |||
} | |||
catch (Exception e) | |||
{ | |||
Logging.Error($"An exception occured while eveluating {server.FriendlyName()}"); | |||
Logging.LogUsefulException(e); | |||
} | |||
} | |||
private void Ping_PingCompleted(object sender, PingCompletedEventArgs e) | |||
{ | |||
count += n; | |||
try | |||
{ | |||
if (e.Reply.Status == IPStatus.Success) | |||
{ | |||
RoundtripTime.Add((int?)e.Reply.RoundtripTime); | |||
} | |||
else | |||
{ | |||
RoundtripTime.Add(null); | |||
} | |||
TestNext(); | |||
} | |||
catch (Exception ex) | |||
{ | |||
Logging.Error($"An exception occured while eveluating {server.FriendlyName()}"); | |||
Logging.LogUsefulException(ex); | |||
} | |||
} | |||
else | |||
private void TestNext() | |||
{ | |||
count = n; | |||
_lastOutboundCounter[server.Identifier()] = 0; | |||
if (repeat > 0) | |||
{ | |||
//Do ICMPTest in a random frequency | |||
int delay = TimeoutMilliseconds + new Random().Next() % TimeoutMilliseconds; | |||
new Task(() => ICMPTest(delay)).Start(); | |||
} | |||
else | |||
{ | |||
Completed?.Invoke(this, new CompletedEventArgs | |||
{ | |||
RoundtripTime = RoundtripTime | |||
}); | |||
} | |||
} | |||
public class CompletedEventArgs : EventArgs | |||
{ | |||
public List<int?> RoundtripTime; | |||
} | |||
_outboundCounter[server.Identifier()] = count; | |||
} | |||
} | |||
} |
@@ -5,7 +5,6 @@ using System.Net; | |||
using System.Net.Sockets; | |||
using System.Text; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using Newtonsoft.Json; | |||
using Shadowsocks.Controller.Strategy; | |||
@@ -311,7 +310,7 @@ namespace Shadowsocks.Controller | |||
{ | |||
if (_config.availabilityStatistics) | |||
{ | |||
new Task(() => availabilityStatistics.UpdateLatency(server, (int) latency.TotalMilliseconds)).Start(); | |||
availabilityStatistics.UpdateLatency(server, (int)latency.TotalMilliseconds); | |||
} | |||
} | |||
@@ -320,7 +319,7 @@ namespace Shadowsocks.Controller | |||
Interlocked.Add(ref inboundCounter, n); | |||
if (_config.availabilityStatistics) | |||
{ | |||
new Task(() => availabilityStatistics.UpdateInboundCounter(server, n)).Start(); | |||
availabilityStatistics.UpdateInboundCounter(server, n); | |||
} | |||
} | |||
@@ -329,7 +328,7 @@ namespace Shadowsocks.Controller | |||
Interlocked.Add(ref outboundCounter, n); | |||
if (_config.availabilityStatistics) | |||
{ | |||
new Task(() => availabilityStatistics.UpdateOutboundCounter(server, n)).Start(); | |||
availabilityStatistics.UpdateOutboundCounter(server, n); | |||
} | |||
} | |||
@@ -3,11 +3,6 @@ | |||
<package id="Caseless.Fody" version="1.4.1" targetFramework="net40-client" developmentDependency="true" /> | |||
<package id="Costura.Fody" version="1.3.3.0" targetFramework="net40-client" developmentDependency="true" /> | |||
<package id="Fody" version="1.29.4" targetFramework="net40-client" developmentDependency="true" /> | |||
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net40-client" /> | |||
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net40-client" /> | |||
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net40-client" /> | |||
<package id="Microsoft.Net.Http" version="2.0.20710.0" targetFramework="net4-client" /> | |||
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net40-client" /> | |||
<package id="StringEx.CS" version="0.2" targetFramework="net40-client" /> | |||
<package id="System.Net.Http" version="2.0.20710.0" targetFramework="net40-client" /> | |||
</packages> |
@@ -66,18 +66,6 @@ | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="Microsoft.CSharp" /> | |||
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="Microsoft.VisualBasic" /> | |||
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Newtonsoft.Json.8.0.2\lib\net40\Newtonsoft.Json.dll</HintPath> | |||
@@ -88,27 +76,7 @@ | |||
<Reference Include="System" /> | |||
<Reference Include="System.Data" /> | |||
<Reference Include="System.Drawing" /> | |||
<Reference Include="System.IO, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="System.Net" /> | |||
<Reference Include="System.Net.Http, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="System.Net.Http.WebRequest, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="System.Runtime, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="System.Threading.Tasks, Version=2.6.10.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="System.Windows.Forms" /> | |||
<Reference Include="System.Windows.Forms.DataVisualization" /> | |||
<Reference Include="System.Xaml" /> | |||
@@ -330,10 +298,8 @@ | |||
<PropertyGroup> | |||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> | |||
</PropertyGroup> | |||
<Error Condition="!Exists('3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" /> | |||
<Error Condition="!Exists('3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets'))" /> | |||
</Target> | |||
<Import Project="3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" /> | |||
<Import Project="3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets" Condition="Exists('3rd\Fody.1.29.4\build\portable-net+sl+win+wpa+wp\Fody.targets')" /> | |||
<UsingTask TaskName="CosturaCleanup" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" TaskFactory="CodeTaskFactory"> | |||
<ParameterGroup> | |||
@@ -1,8 +0,0 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<packages> | |||
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" /> | |||
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net45" /> | |||
<package id="Microsoft.Bcl.Build" version="1.0.21" targetFramework="net45" /> | |||
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" /> | |||
<package id="System.Net.Http" version="4.0.0" targetFramework="net45" /> | |||
</packages> |
@@ -35,29 +35,9 @@ | |||
<StartupObject /> | |||
</PropertyGroup> | |||
<ItemGroup> | |||
<Reference Include="Microsoft.Threading.Tasks, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="Microsoft.Threading.Tasks.Extensions, Version=1.0.12.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="Microsoft.Threading.Tasks.Extensions.Desktop, Version=1.0.168.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="System" /> | |||
<Reference Include="System.Net" /> | |||
<Reference Include="System.Net.Http" /> | |||
<Reference Include="System.Net.Http.Extensions, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="System.Net.Http.Primitives, Version=4.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>..\shadowsocks-csharp\3rd\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="System.Net.Http.WebRequest" /> | |||
</ItemGroup> | |||
<Choose> | |||
@@ -82,9 +62,6 @@ | |||
<Name>shadowsocks-csharp</Name> | |||
</ProjectReference> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Include="packages.config" /> | |||
</ItemGroup> | |||
<Choose> | |||
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'"> | |||
<ItemGroup> | |||
@@ -105,13 +82,6 @@ | |||
</Choose> | |||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> | |||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
<Import Project="..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets" Condition="Exists('..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" /> | |||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild"> | |||
<PropertyGroup> | |||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText> | |||
</PropertyGroup> | |||
<Error Condition="!Exists('..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\shadowsocks-csharp\3rd\Microsoft.Bcl.Build.1.0.21\build\Microsoft.Bcl.Build.targets'))" /> | |||
</Target> | |||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. | |||
Other similar extension points exist, see Microsoft.Common.targets. | |||
<Target Name="BeforeBuild"> | |||