@@ -2,12 +2,20 @@ | |||
using System.Collections.Generic; | |||
using System.IO; | |||
using System.Linq; | |||
using System.Net; | |||
using SimpleJson; | |||
using System.Net.Http; | |||
using System.Net.NetworkInformation; | |||
using System.Threading; | |||
using System.Threading.Tasks; | |||
using System.Windows.Forms; | |||
using Shadowsocks.Model; | |||
using SimpleJson = SimpleJson.SimpleJson; | |||
using Timer = System.Threading.Timer; | |||
namespace Shadowsocks.Controller | |||
{ | |||
using DataUnit = KeyValuePair<string, string>; | |||
using DataList = List<KeyValuePair<string, string>>; | |||
class AvailabilityStatistics | |||
{ | |||
private const string StatisticsFilesName = "shadowsocks.availability.csv"; | |||
@@ -51,35 +59,67 @@ namespace Shadowsocks.Controller | |||
} | |||
} | |||
private void Evaluate(object obj) | |||
//hardcode | |||
//TODO: backup reliable isp&geolocation provider or a local database is required | |||
private static async Task<DataList> getGeolocationAndISP() | |||
{ | |||
Logging.Debug("Retrive information of geolocation and isp"); | |||
const string api = "http://ip-api.com/json"; | |||
var jsonString = await new HttpClient().GetStringAsync(api); | |||
var ret = new DataList | |||
{ | |||
new DataUnit(State.Geolocation, State.Unknown), | |||
new DataUnit(State.ISP, State.Unknown), | |||
}; | |||
dynamic obj; | |||
if (!global::SimpleJson.SimpleJson.TryDeserializeObject(jsonString, out obj)) return ret; | |||
string country = obj["country"]; | |||
string city = obj["city"]; | |||
string isp = obj["isp"]; | |||
string regionName= obj["regionName"]; | |||
if (country == null || city == null || isp == null || regionName == null) return ret; | |||
ret[0] = new DataUnit(State.Geolocation, $"{country} {regionName} {city}"); | |||
ret[1] = new DataUnit(State.ISP, isp); | |||
return ret; | |||
} | |||
private static async Task<List<DataList>> ICMPTest(Server server) | |||
{ | |||
Logging.Debug("eveluating " + server.FriendlyName()); | |||
var ping = new Ping(); | |||
var state = (State) obj; | |||
foreach (var server in _servers) | |||
var ret = new List<DataList>(); | |||
foreach (var timestamp in Enumerable.Range(0, Repeat).Select(_ => DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"))) | |||
{ | |||
//ICMP echo. we can also set options and special bytes | |||
var reply = await ping.SendTaskAsync(server.server, Timeout); | |||
ret.Add(new List<KeyValuePair<string, string>> | |||
{ | |||
new KeyValuePair<string, string>("Timestamp", timestamp), | |||
new KeyValuePair<string, string>("Server", server.FriendlyName()), | |||
new KeyValuePair<string, string>("Status", reply?.Status.ToString()), | |||
new KeyValuePair<string, string>("RoundtripTime", reply?.RoundtripTime.ToString()) | |||
//new KeyValuePair<string, string>("data", reply.Buffer.ToString()); // The data of reply | |||
}); | |||
} | |||
return ret; | |||
} | |||
private async void Evaluate(object obj) | |||
{ | |||
var geolocationAndIsp = getGeolocationAndISP(); | |||
foreach (var dataLists in await TaskEx.WhenAll(_servers.Select(ICMPTest))) | |||
{ | |||
Logging.Debug("eveluating " + server.FriendlyName()); | |||
foreach (var _ in Enumerable.Range(0, Repeat)) | |||
await geolocationAndIsp; | |||
foreach (var dataList in dataLists) | |||
{ | |||
//TODO: do simple analyze of data to provide friendly message, like package loss. | |||
var timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); | |||
//ICMP echo. we can also set options and special bytes | |||
//seems no need to use SendPingAsync: | |||
var reply = ping.Send(server.server, Timeout); | |||
state.Data = new List<KeyValuePair<string, string>> | |||
{ | |||
new KeyValuePair<string, string>("Timestamp", timestamp), | |||
new KeyValuePair<string, string>("Server", server.FriendlyName()), | |||
new KeyValuePair<string, string>("Status", reply?.Status.ToString()), | |||
new KeyValuePair<string, string>("RoundtripTime", reply?.RoundtripTime.ToString()) | |||
}; | |||
//state.data.Add(new KeyValuePair<string, string>("data", reply.Buffer.ToString())); // The data of reply | |||
Append(state.Data); | |||
Append(dataList, geolocationAndIsp.Result); | |||
} | |||
} | |||
} | |||
private static void Append(List<KeyValuePair<string, string>> data) | |||
private static void Append(DataList dataList, IEnumerable<DataUnit> extra) | |||
{ | |||
var data = dataList.Concat(extra); | |||
var dataLine = string.Join(Delimiter, data.Select(kv => kv.Value).ToArray()); | |||
string[] lines; | |||
if (!File.Exists(AvailabilityStatisticsFile)) | |||
@@ -102,7 +142,10 @@ namespace Shadowsocks.Controller | |||
private class State | |||
{ | |||
public List<KeyValuePair<string, string>> Data = new List<KeyValuePair<string, string>>(); | |||
public DataList dataList = new DataList(); | |||
public const string Geolocation = "Geolocation"; | |||
public const string ISP = "ISP"; | |||
public const string Unknown = "Unknown"; | |||
} | |||
} | |||
} |
@@ -1,6 +1,19 @@ | |||
<?xml version="1.0"?> | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<configuration> | |||
<startup> | |||
<supportedRuntime version="v4.0"/> | |||
<supportedRuntime version="v2.0.50727"/> | |||
</startup></configuration> | |||
<supportedRuntime version="v4.0" /> | |||
<supportedRuntime version="v2.0.50727" /> | |||
</startup> | |||
<runtime> | |||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"> | |||
<dependentAssembly> | |||
<assemblyIdentity name="System.Runtime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> | |||
<bindingRedirect oldVersion="0.0.0.0-2.6.8.0" newVersion="2.6.8.0" /> | |||
</dependentAssembly> | |||
<dependentAssembly> | |||
<assemblyIdentity name="System.Threading.Tasks" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" /> | |||
<bindingRedirect oldVersion="0.0.0.0-2.6.8.0" newVersion="2.6.8.0" /> | |||
</dependentAssembly> | |||
</assemblyBinding> | |||
</runtime> | |||
</configuration> |
@@ -0,0 +1,6 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | |||
<packages> | |||
<package id="Microsoft.Bcl" version="1.1.8" targetFramework="net4-client" /> | |||
<package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net4-client" /> | |||
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net4-client" /> | |||
</packages> |
@@ -62,12 +62,39 @@ | |||
<ApplicationManifest>app.manifest</ApplicationManifest> | |||
</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="PresentationCore" /> | |||
<Reference Include="PresentationFramework" /> | |||
<Reference Include="System" /> | |||
<Reference Include="System.Data" /> | |||
<Reference Include="System.Drawing" /> | |||
<Reference Include="System.IO, Version=2.6.8.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.1.1.8\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" /> | |||
<Reference Include="System.Runtime, Version=2.6.8.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.1.1.8\lib\net40\System.Runtime.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="System.Threading.Tasks, Version=2.6.8.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL"> | |||
<HintPath>3rd\Microsoft.Bcl.1.1.8\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" /> | |||
@@ -225,6 +252,7 @@ | |||
<None Include="Data\proxy.pac.txt.gz" /> | |||
</ItemGroup> | |||
<ItemGroup> | |||
<None Include="packages.config" /> | |||
<None Include="Resources\ss20.png" /> | |||
<None Include="Resources\ss16.png" /> | |||
<None Include="Resources\ss24.png" /> | |||
@@ -267,6 +295,11 @@ | |||
</BootstrapperPackage> | |||
</ItemGroup> | |||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | |||
<Import Project="3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" /> | |||
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''"> | |||
<Error Condition="!Exists('3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" /> | |||
<Error Condition="Exists('3rd\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" /> | |||
</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"> | |||