Browse Source

fix Availability Statistics

pull/478/head
Gang Zhuo 9 years ago
parent
commit
8d863f1d5f
2 changed files with 98 additions and 27 deletions
  1. +89
    -27
      shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
  2. +9
    -0
      shadowsocks-csharp/Program.cs

+ 89
- 27
shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs View File

@@ -141,15 +141,14 @@ namespace Shadowsocks.Controller
private void Run(object _) private void Run(object _)
{ {
UpdateRecords(); UpdateRecords();
Save();
Reset(); Reset();
FilterRawStatistics();
} }


private void UpdateRecords() private void UpdateRecords()
{ {
var records = new Dictionary<string, StatisticsRecord>(); var records = new Dictionary<string, StatisticsRecord>();

UpdateRecordsState state = new UpdateRecordsState();
state.counter = _controller.GetCurrentConfiguration().configs.Count;
foreach (var server in _controller.GetCurrentConfiguration().configs) foreach (var server in _controller.GetCurrentConfiguration().configs)
{ {
var id = server.Identifier(); var id = server.Identifier();
@@ -169,44 +168,76 @@ namespace Shadowsocks.Controller
{ {
MyPing ping = new MyPing(server, Repeat); MyPing ping = new MyPing(server, Repeat);
ping.Completed += ping_Completed; ping.Completed += ping_Completed;
ping.Start(record);
ping.Start(new PingState { state = state, record = record });
}
else if (!record.IsEmptyData())
{
AppendRecord(id, record);
} }
} }


foreach (var kv in records.Where(kv => !kv.Value.IsEmptyData()))
if (!Config.Ping)
{ {
AppendRecord(kv.Key, kv.Value);
Save();
FilterRawStatistics();
} }
} }


private void ping_Completed(object sender, MyPing.CompletedEventArgs e) private void ping_Completed(object sender, MyPing.CompletedEventArgs e)
{ {
PingState pingState = (PingState)e.UserState;
UpdateRecordsState state = pingState.state;
Server server = e.Server; Server server = e.Server;
StatisticsRecord record = (StatisticsRecord)e.UserState;
StatisticsRecord record = pingState.record;
record.SetResponse(e.RoundtripTime); record.SetResponse(e.RoundtripTime);
if (!record.IsEmptyData())
{
AppendRecord(server.Identifier(), record);
}
Logging.Debug($"Ping {server.FriendlyName()} {e.RoundtripTime.Count} times, {(100 - record.PackageLoss * 100)}% packages loss, min {record.MinResponse} ms, max {record.MaxResponse} ms, avg {record.AverageResponse} ms"); Logging.Debug($"Ping {server.FriendlyName()} {e.RoundtripTime.Count} times, {(100 - record.PackageLoss * 100)}% packages loss, min {record.MinResponse} ms, max {record.MaxResponse} ms, avg {record.AverageResponse} ms");
if (Interlocked.Decrement(ref state.counter) == 0)
{
Save();
FilterRawStatistics();
}
} }


private void AppendRecord(string serverIdentifier, StatisticsRecord record) private void AppendRecord(string serverIdentifier, StatisticsRecord record)
{ {
List<StatisticsRecord> records;
if (!RawStatistics.TryGetValue(serverIdentifier, out records))
try
{
List<StatisticsRecord> records;
lock (RawStatistics)
{
if (!RawStatistics.TryGetValue(serverIdentifier, out records))
{
records = new List<StatisticsRecord>();
RawStatistics[serverIdentifier] = records;
}
}
records.Add(record);
}
catch (Exception e)
{ {
records = new List<StatisticsRecord>();
RawStatistics[serverIdentifier] = records;
Logging.LogUsefulException(e);
} }
records.Add(record);
} }


private void Save() private void Save()
{ {
Logging.Debug($"save statistics to {AvailabilityStatisticsFile}");
if (RawStatistics.Count == 0) if (RawStatistics.Count == 0)
{ {
return; return;
} }
try try
{ {
var content = JsonConvert.SerializeObject(RawStatistics, Formatting.None);
string content;
#if DEBUG
content = JsonConvert.SerializeObject(RawStatistics, Formatting.Indented);
#else
content = JsonConvert.SerializeObject(RawStatistics, Formatting.None);
#endif
File.WriteAllText(AvailabilityStatisticsFile, content); File.WriteAllText(AvailabilityStatisticsFile, content);
} }
catch (IOException e) catch (IOException e)
@@ -226,17 +257,25 @@ namespace Shadowsocks.Controller


private void FilterRawStatistics() private void FilterRawStatistics()
{ {
if (RawStatistics == null) return;
if (FilteredStatistics == null)
try
{ {
FilteredStatistics = new Statistics();
}
Logging.Debug("filter raw statistics");
if (RawStatistics == null) return;
if (FilteredStatistics == null)
{
FilteredStatistics = new Statistics();
}


foreach (var serverAndRecords in RawStatistics)
foreach (var serverAndRecords in RawStatistics)
{
var server = serverAndRecords.Key;
var filteredRecords = serverAndRecords.Value.FindAll(IsValidRecord);
FilteredStatistics[server] = filteredRecords;
}
}
catch (Exception e)
{ {
var server = serverAndRecords.Key;
var filteredRecords = serverAndRecords.Value.FindAll(IsValidRecord);
FilteredStatistics[server] = filteredRecords;
Logging.LogUsefulException(e);
} }
} }


@@ -304,6 +343,17 @@ namespace Shadowsocks.Controller
}, (k, v) => (v + n)); }, (k, v) => (v + n));
} }


class UpdateRecordsState
{
public int counter;
}

class PingState
{
public UpdateRecordsState state;
public StatisticsRecord record;
}

class MyPing class MyPing
{ {
//arguments for ICMP tests //arguments for ICMP tests
@@ -329,7 +379,10 @@ namespace Shadowsocks.Controller
public void Start(object userstate) public void Start(object userstate)
{ {
if (server.server == "") if (server.server == "")
{
FireCompleted(new Exception("Invalid Server"), userstate);
return; return;
}
new Task(() => ICMPTest(0, userstate)).Start(); new Task(() => ICMPTest(0, userstate)).Start();
} }


@@ -355,6 +408,7 @@ namespace Shadowsocks.Controller
{ {
Logging.Error($"An exception occured while eveluating {server.FriendlyName()}"); Logging.Error($"An exception occured while eveluating {server.FriendlyName()}");
Logging.LogUsefulException(e); Logging.LogUsefulException(e);
FireCompleted(e, userstate);
} }
} }


@@ -378,6 +432,7 @@ namespace Shadowsocks.Controller
{ {
Logging.Error($"An exception occured while eveluating {server.FriendlyName()}"); Logging.Error($"An exception occured while eveluating {server.FriendlyName()}");
Logging.LogUsefulException(ex); Logging.LogUsefulException(ex);
FireCompleted(ex, e.UserState);
} }
} }


@@ -391,17 +446,24 @@ namespace Shadowsocks.Controller
} }
else else
{ {
Completed?.Invoke(this, new CompletedEventArgs
{
Server = server,
RoundtripTime = RoundtripTime,
UserState = userstate
});
FireCompleted(null, userstate);
} }
} }


private void FireCompleted(Exception error, object userstate)
{
Completed?.Invoke(this, new CompletedEventArgs
{
Error = error,
Server = server,
RoundtripTime = RoundtripTime,
UserState = userstate
});
}

public class CompletedEventArgs : EventArgs public class CompletedEventArgs : EventArgs
{ {
public Exception Error;
public Server Server; public Server Server;
public List<int?> RoundtripTime; public List<int?> RoundtripTime;
public object UserState; public object UserState;


+ 9
- 0
shadowsocks-csharp/Program.cs View File

@@ -21,6 +21,7 @@ namespace Shadowsocks
Utils.ReleaseMemory(true); Utils.ReleaseMemory(true);
using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode())) using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode()))
{ {
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
Application.EnableVisualStyles(); Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false); Application.SetCompatibleTextRenderingDefault(false);
@@ -53,5 +54,13 @@ namespace Shadowsocks
Application.Run(); Application.Run();
} }
} }
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Logging.Error(e.ExceptionObject?.ToString());
MessageBox.Show($"Unexpect error, shadowsocks exited.{Environment.NewLine} {e.ExceptionObject?.ToString()}",
"Shadowsocks", MessageBoxButtons.OK, MessageBoxIcon.Error);
Application.Exit();
}
} }
} }

Loading…
Cancel
Save