@@ -2,12 +2,20 @@ | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.IO; | using System.IO; | ||||
using System.Linq; | using System.Linq; | ||||
using System.Net; | |||||
using SimpleJson; | |||||
using System.Net.Http; | |||||
using System.Net.NetworkInformation; | using System.Net.NetworkInformation; | ||||
using System.Threading; | |||||
using System.Threading.Tasks; | |||||
using System.Windows.Forms; | |||||
using Shadowsocks.Model; | using Shadowsocks.Model; | ||||
using SimpleJson = SimpleJson.SimpleJson; | |||||
using Timer = System.Threading.Timer; | |||||
namespace Shadowsocks.Controller | namespace Shadowsocks.Controller | ||||
{ | { | ||||
using DataUnit = KeyValuePair<string, string>; | |||||
using DataList = List<KeyValuePair<string, string>>; | |||||
class AvailabilityStatistics | class AvailabilityStatistics | ||||
{ | { | ||||
private const string StatisticsFilesName = "shadowsocks.availability.csv"; | 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 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()); | var dataLine = string.Join(Delimiter, data.Select(kv => kv.Value).ToArray()); | ||||
string[] lines; | string[] lines; | ||||
if (!File.Exists(AvailabilityStatisticsFile)) | if (!File.Exists(AvailabilityStatisticsFile)) | ||||
@@ -102,7 +142,10 @@ namespace Shadowsocks.Controller | |||||
private class State | 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> | <configuration> | ||||
<startup> | <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> | <ApplicationManifest>app.manifest</ApplicationManifest> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <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="Microsoft.VisualBasic" /> | ||||
<Reference Include="PresentationCore" /> | <Reference Include="PresentationCore" /> | ||||
<Reference Include="PresentationFramework" /> | <Reference Include="PresentationFramework" /> | ||||
<Reference Include="System" /> | <Reference Include="System" /> | ||||
<Reference Include="System.Data" /> | <Reference Include="System.Data" /> | ||||
<Reference Include="System.Drawing" /> | <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" /> | ||||
<Reference Include="System.Windows.Forms.DataVisualization" /> | <Reference Include="System.Windows.Forms.DataVisualization" /> | ||||
<Reference Include="System.Xaml" /> | <Reference Include="System.Xaml" /> | ||||
@@ -225,6 +252,7 @@ | |||||
<None Include="Data\proxy.pac.txt.gz" /> | <None Include="Data\proxy.pac.txt.gz" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<None Include="packages.config" /> | |||||
<None Include="Resources\ss20.png" /> | <None Include="Resources\ss20.png" /> | ||||
<None Include="Resources\ss16.png" /> | <None Include="Resources\ss16.png" /> | ||||
<None Include="Resources\ss24.png" /> | <None Include="Resources\ss24.png" /> | ||||
@@ -267,6 +295,11 @@ | |||||
</BootstrapperPackage> | </BootstrapperPackage> | ||||
</ItemGroup> | </ItemGroup> | ||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | <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. | <!-- 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. | Other similar extension points exist, see Microsoft.Common.targets. | ||||
<Target Name="BeforeBuild"> | <Target Name="BeforeBuild"> | ||||