Files
shadowsocks-windows/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs
T

117 lines
4.3 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Threading;
using Shadowsocks.Model;
using System.Reflection;
namespace Shadowsocks.Controller
{
class AvailabilityStatistics
{
private static readonly string StatisticsFilesName = "shadowsocks.availability.csv";
private static readonly string Delimiter = ",";
private static readonly int Timeout = 500;
private static readonly int Repeat = 4; //repeat times every evaluation
private static readonly int Interval = 10 * 60 * 1000; //evaluate proxies every 15 minutes
private Timer timer = null;
private State state = null;
private List<Server> servers;
public static string AvailabilityStatisticsFile;
//static constructor to initialize every public static fields before refereced
static AvailabilityStatistics()
{
string temppath = Path.GetTempPath();
AvailabilityStatisticsFile = Path.Combine(temppath, StatisticsFilesName);
}
public bool Set(bool enabled)
{
try
{
if (enabled)
{
if (timer?.Change(0, Interval) == null)
{
state = new State();
timer = new Timer(Evaluate, state, 0, Interval);
}
}
else
{
timer?.Dispose();
}
return true;
}
catch (Exception e)
{
Logging.LogUsefulException(e);
return false;
}
}
private void Evaluate(object obj)
{
Ping ping = new Ping();
State state = (State) obj;
foreach (var server in servers)
{
Logging.Debug("eveluating " + server.FriendlyName());
foreach (var _ in Enumerable.Range(0, Repeat))
{
//TODO: do simple analyze of data to provide friendly message, like package loss.
string 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
try
{
PingReply reply = ping.Send(server.server, Timeout);
state.data = new List<KeyValuePair<string, string>>();
state.data.Add(new KeyValuePair<string, string>("Timestamp", timestamp));
state.data.Add(new KeyValuePair<string, string>("Server", server.FriendlyName()));
state.data.Add(new KeyValuePair<string, string>("Status", reply.Status.ToString()));
state.data.Add(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);
}
catch (Exception e)
{
Logging.LogUsefulException(e);
}
}
}
}
private static void Append(List<KeyValuePair<string, string>> data)
{
string dataLine = string.Join(Delimiter, data.Select(kv => kv.Value).ToArray());
string[] lines;
if (!File.Exists(AvailabilityStatisticsFile))
{
string headerLine = string.Join(Delimiter, data.Select(kv => kv.Key).ToArray());
lines = new string[] { headerLine, dataLine };
}
else
{
lines = new string[] { dataLine };
}
File.AppendAllLines(AvailabilityStatisticsFile, lines);
}
internal void UpdateConfiguration(Configuration _config)
{
Set(_config.availabilityStatistics);
servers = _config.configs;
}
private class State
{
public List<KeyValuePair<string, string>> data = new List<KeyValuePair<string, string>>();
}
}
}