# Conflicts: # shadowsocks-csharp/Controller/Service/TCPRelay.cs # shadowsocks-csharp/Controller/ShadowsocksController.cs # shadowsocks-csharp/View/MenuViewController.cstags/3.2
@@ -107,67 +107,30 @@ namespace Shadowsocks.Controller | |||||
var bytes = inbound - lastInbound; | var bytes = inbound - lastInbound; | ||||
_lastInboundCounter[id] = inbound; | _lastInboundCounter[id] = inbound; | ||||
var inboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds); | 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 lastOutbound = _lastOutboundCounter[id]; | ||||
var outbound = _outboundCounter[id]; | var outbound = _outboundCounter[id]; | ||||
bytes = outbound - lastOutbound; | bytes = outbound - lastOutbound; | ||||
_lastOutboundCounter[id] = outbound; | _lastOutboundCounter[id] = outbound; | ||||
var outboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds); | 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( | Logging.Debug( | ||||
$"{id}: current/max inbound {inboundSpeed}/{_inboundSpeedRecords[id].Max()} KiB/s, current/max outbound {outboundSpeed}/{_outboundSpeedRecords[id].Max()} KiB/s"); | $"{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() | private void Reset() | ||||
{ | { | ||||
_inboundSpeedRecords.Clear(); | _inboundSpeedRecords.Clear(); | ||||
@@ -178,15 +141,14 @@ namespace Shadowsocks.Controller | |||||
private void Run(object _) | private void Run(object _) | ||||
{ | { | ||||
UpdateRecords(); | UpdateRecords(); | ||||
Save(); | |||||
Reset(); | Reset(); | ||||
FilterRawStatistics(); | |||||
} | } | ||||
private async 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(); | ||||
@@ -202,43 +164,80 @@ namespace Shadowsocks.Controller | |||||
records[id] = record; | records[id] = record; | ||||
else | else | ||||
records.Add(id, record); | records.Add(id, record); | ||||
if (Config.Ping) | |||||
{ | |||||
MyPing ping = new MyPing(server, Repeat); | |||||
ping.Completed += ping_Completed; | |||||
ping.Start(new PingState { state = state, record = record }); | |||||
} | |||||
else if (!record.IsEmptyData()) | |||||
{ | |||||
AppendRecord(id, record); | |||||
} | |||||
} | } | ||||
if (Config.Ping) | |||||
if (!Config.Ping) | |||||
{ | { | ||||
var icmpResults = await TaskEx.WhenAll(_controller.GetCurrentConfiguration().configs.Select(ICMPTest)); | |||||
foreach (var result in icmpResults.Where(result => result != null)) | |||||
{ | |||||
records[result.Server.Identifier()].SetResponse(result.RoundtripTime); | |||||
} | |||||
Save(); | |||||
FilterRawStatistics(); | |||||
} | } | ||||
} | |||||
foreach (var kv in records.Where(kv => !kv.Value.IsEmptyData())) | |||||
private void ping_Completed(object sender, MyPing.CompletedEventArgs e) | |||||
{ | |||||
PingState pingState = (PingState)e.UserState; | |||||
UpdateRecordsState state = pingState.state; | |||||
Server server = e.Server; | |||||
StatisticsRecord record = pingState.record; | |||||
record.SetResponse(e.RoundtripTime); | |||||
if (!record.IsEmptyData()) | |||||
{ | { | ||||
AppendRecord(kv.Key, kv.Value); | |||||
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"); | |||||
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>(); | |||||
Logging.LogUsefulException(e); | |||||
} | } | ||||
records.Add(record); | |||||
RawStatistics[serverIdentifier] = records; | |||||
} | } | ||||
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) | ||||
@@ -258,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); | |||||
} | } | ||||
} | } | ||||
@@ -298,21 +305,10 @@ namespace Shadowsocks.Controller | |||||
private static int GetSpeedInKiBPerSecond(long bytes, double seconds) | private static int GetSpeedInKiBPerSecond(long bytes, double seconds) | ||||
{ | { | ||||
var result = (int) (bytes/seconds)/1024; | |||||
var result = (int)(bytes / seconds) / 1024; | |||||
return result; | 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() | public void Dispose() | ||||
{ | { | ||||
_recorder.Dispose(); | _recorder.Dispose(); | ||||
@@ -321,44 +317,158 @@ namespace Shadowsocks.Controller | |||||
public void UpdateLatency(Server server, int latency) | 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) | public void UpdateInboundCounter(Server server, long n) | ||||
{ | { | ||||
long count; | |||||
if (_inboundCounter.TryGetValue(server.Identifier(), out count)) | |||||
_inboundCounter.AddOrUpdate(server.Identifier(), (k) => | |||||
{ | { | ||||
count += n; | |||||
} | |||||
else | |||||
{ | |||||
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) | 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 UpdateRecordsState | |||||
{ | |||||
public int counter; | |||||
} | |||||
class PingState | |||||
{ | |||||
public UpdateRecordsState state; | |||||
public StatisticsRecord record; | |||||
} | |||||
class MyPing | |||||
{ | |||||
//arguments for ICMP tests | |||||
public const int TimeoutMilliseconds = 500; | |||||
public EventHandler<CompletedEventArgs> Completed; | |||||
private Server server; | |||||
private int repeat; | |||||
private IPAddress ip; | |||||
private Ping ping; | |||||
private List<int?> RoundtripTime; | |||||
public MyPing(Server server, int repeat) | |||||
{ | { | ||||
count += n; | |||||
this.server = server; | |||||
this.repeat = repeat; | |||||
RoundtripTime = new List<int?>(repeat); | |||||
ping = new Ping(); | |||||
ping.PingCompleted += Ping_PingCompleted; | |||||
} | |||||
public void Start(object userstate) | |||||
{ | |||||
if (server.server == "") | |||||
{ | |||||
FireCompleted(new Exception("Invalid Server"), userstate); | |||||
return; | |||||
} | |||||
new Task(() => ICMPTest(0, userstate)).Start(); | |||||
} | } | ||||
else | |||||
private void ICMPTest(int delay, object userstate) | |||||
{ | { | ||||
count = n; | |||||
_lastOutboundCounter[server.Identifier()] = 0; | |||||
try | |||||
{ | |||||
Logging.Debug($"Ping {server.FriendlyName()}"); | |||||
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, userstate); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.Error($"An exception occured while eveluating {server.FriendlyName()}"); | |||||
Logging.LogUsefulException(e); | |||||
FireCompleted(e, userstate); | |||||
} | |||||
} | |||||
private void Ping_PingCompleted(object sender, PingCompletedEventArgs e) | |||||
{ | |||||
try | |||||
{ | |||||
if (e.Reply.Status == IPStatus.Success) | |||||
{ | |||||
Logging.Debug($"Ping {server.FriendlyName()} {e.Reply.RoundtripTime} ms"); | |||||
RoundtripTime.Add((int?)e.Reply.RoundtripTime); | |||||
} | |||||
else | |||||
{ | |||||
Logging.Debug($"Ping {server.FriendlyName()} timeout"); | |||||
RoundtripTime.Add(null); | |||||
} | |||||
TestNext(e.UserState); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
Logging.Error($"An exception occured while eveluating {server.FriendlyName()}"); | |||||
Logging.LogUsefulException(ex); | |||||
FireCompleted(ex, e.UserState); | |||||
} | |||||
} | |||||
private void TestNext(object userstate) | |||||
{ | |||||
if (repeat > 0) | |||||
{ | |||||
//Do ICMPTest in a random frequency | |||||
int delay = TimeoutMilliseconds + new Random().Next() % TimeoutMilliseconds; | |||||
new Task(() => ICMPTest(delay, userstate)).Start(); | |||||
} | |||||
else | |||||
{ | |||||
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 Exception Error; | |||||
public Server Server; | |||||
public List<int?> RoundtripTime; | |||||
public object UserState; | |||||
} | } | ||||
_outboundCounter[server.Identifier()] = count; | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -47,20 +47,7 @@ namespace Shadowsocks.Controller | |||||
Process[] existingPolipo = Process.GetProcessesByName("ss_privoxy"); | Process[] existingPolipo = Process.GetProcessesByName("ss_privoxy"); | ||||
foreach (Process p in existingPolipo) | foreach (Process p in existingPolipo) | ||||
{ | { | ||||
try | |||||
{ | |||||
p.CloseMainWindow(); | |||||
p.WaitForExit(100); | |||||
if (!p.HasExited) | |||||
{ | |||||
p.Kill(); | |||||
p.WaitForExit(); | |||||
} | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
KillProcess(p); | |||||
} | } | ||||
string polipoConfig = Resources.privoxy_conf; | string polipoConfig = Resources.privoxy_conf; | ||||
_runningPort = this.GetFreePort(); | _runningPort = this.GetFreePort(); | ||||
@@ -86,20 +73,30 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
if (_process != null) | if (_process != null) | ||||
{ | { | ||||
try | |||||
{ | |||||
_process.Kill(); | |||||
_process.WaitForExit(); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
KillProcess(_process); | |||||
_process = null; | _process = null; | ||||
} | } | ||||
RefreshTrayArea(); | RefreshTrayArea(); | ||||
} | } | ||||
private static void KillProcess(Process p) | |||||
{ | |||||
try | |||||
{ | |||||
p.CloseMainWindow(); | |||||
p.WaitForExit(100); | |||||
if (!p.HasExited) | |||||
{ | |||||
p.Kill(); | |||||
p.WaitForExit(); | |||||
} | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
} | |||||
private int GetFreePort() | private int GetFreePort() | ||||
{ | { | ||||
int defaultPort = 8123; | int defaultPort = 8123; | ||||
@@ -1,5 +1,6 @@ | |||||
using System; | using System; | ||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Linq; | |||||
using System.Net; | using System.Net; | ||||
using System.Net.Sockets; | using System.Net.Sockets; | ||||
using System.Timers; | using System.Timers; | ||||
@@ -17,10 +18,7 @@ namespace Shadowsocks.Controller | |||||
private DateTime _lastSweepTime; | private DateTime _lastSweepTime; | ||||
private Configuration _config; | private Configuration _config; | ||||
public ISet<TCPHandler> Handlers | |||||
{ | |||||
get; set; | |||||
} | |||||
public ISet<TCPHandler> Handlers { get; set; } | |||||
public TCPRelay(ShadowsocksController controller, Configuration conf) | public TCPRelay(ShadowsocksController controller, Configuration conf) | ||||
{ | { | ||||
@@ -32,19 +30,14 @@ namespace Shadowsocks.Controller | |||||
public bool Handle(byte[] firstPacket, int length, Socket socket, object state) | public bool Handle(byte[] firstPacket, int length, Socket socket, object state) | ||||
{ | { | ||||
if (socket.ProtocolType != ProtocolType.Tcp) | |||||
{ | |||||
if (socket.ProtocolType != ProtocolType.Tcp | |||||
|| (length < 2 || firstPacket[0] != 5)) | |||||
return false; | return false; | ||||
} | |||||
if (length < 2 || firstPacket[0] != 5) | |||||
{ | |||||
return false; | |||||
} | |||||
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); | ||||
TCPHandler handler = new TCPHandler(this, _config); | TCPHandler handler = new TCPHandler(this, _config); | ||||
handler.connection = socket; | handler.connection = socket; | ||||
handler.controller = _controller; | handler.controller = _controller; | ||||
handler.relay = this; | |||||
handler.tcprelay = this; | |||||
handler.Start(firstPacket, length); | handler.Start(firstPacket, length); | ||||
IList<TCPHandler> handlersToClose = new List<TCPHandler>(); | IList<TCPHandler> handlersToClose = new List<TCPHandler>(); | ||||
@@ -56,12 +49,8 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
_lastSweepTime = now; | _lastSweepTime = now; | ||||
foreach (TCPHandler handler1 in Handlers) | foreach (TCPHandler handler1 in Handlers) | ||||
{ | |||||
if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) | if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) | ||||
{ | |||||
handlersToClose.Add(handler1); | handlersToClose.Add(handler1); | ||||
} | |||||
} | |||||
} | } | ||||
} | } | ||||
foreach (TCPHandler handler1 in handlersToClose) | foreach (TCPHandler handler1 in handlersToClose) | ||||
@@ -90,6 +79,11 @@ namespace Shadowsocks.Controller | |||||
class TCPHandler | class TCPHandler | ||||
{ | { | ||||
// Size of receive buffer. | |||||
public static readonly int RecvSize = 8192; | |||||
public static readonly int RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth | |||||
public static readonly int BufferSize = RecvSize + RecvReserveSize + 32; | |||||
// public Encryptor encryptor; | // public Encryptor encryptor; | ||||
public IEncryptor encryptor; | public IEncryptor encryptor; | ||||
public Server server; | public Server server; | ||||
@@ -97,63 +91,52 @@ namespace Shadowsocks.Controller | |||||
public IProxy remote; | public IProxy remote; | ||||
public Socket connection; | public Socket connection; | ||||
public ShadowsocksController controller; | public ShadowsocksController controller; | ||||
public TCPRelay relay; | |||||
public Configuration config; | |||||
public TCPRelay tcprelay; | |||||
public DateTime lastActivity; | public DateTime lastActivity; | ||||
private const int maxRetry = 4; | |||||
private int retryCount = 0; | |||||
private bool proxyConnected; | |||||
private bool destConnected; | |||||
private const int MaxRetry = 4; | |||||
private int _retryCount = 0; | |||||
private bool _proxyConnected; | |||||
private bool _destConnected; | |||||
private byte command; | |||||
private byte _command; | |||||
private byte[] _firstPacket; | private byte[] _firstPacket; | ||||
private int _firstPacketLength; | private int _firstPacketLength; | ||||
// Size of receive buffer. | |||||
public const int RecvSize = 8192; | |||||
public const int RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth | |||||
public const int BufferSize = RecvSize + RecvReserveSize + 32; | |||||
private int totalRead = 0; | |||||
private int totalWrite = 0; | |||||
// remote receive buffer | |||||
private byte[] remoteRecvBuffer = new byte[BufferSize]; | |||||
// remote send buffer | |||||
private byte[] remoteSendBuffer = new byte[BufferSize]; | |||||
// connection receive buffer | |||||
private byte[] connetionRecvBuffer = new byte[BufferSize]; | |||||
// connection send buffer | |||||
private byte[] connetionSendBuffer = new byte[BufferSize]; | |||||
// Received data string. | |||||
private bool connectionShutdown = false; | |||||
private bool remoteShutdown = false; | |||||
private bool closed = false; | |||||
private object encryptionLock = new object(); | |||||
private object decryptionLock = new object(); | |||||
private int _totalRead = 0; | |||||
private int _totalWrite = 0; | |||||
private byte[] _remoteRecvBuffer = new byte[BufferSize]; | |||||
private byte[] _remoteSendBuffer = new byte[BufferSize]; | |||||
private byte[] _connetionRecvBuffer = new byte[BufferSize]; | |||||
private byte[] _connetionSendBuffer = new byte[BufferSize]; | |||||
private bool _connectionShutdown = false; | |||||
private bool _remoteShutdown = false; | |||||
private bool _closed = false; | |||||
private object _encryptionLock = new object(); | |||||
private object _decryptionLock = new object(); | |||||
private DateTime _startConnectTime; | private DateTime _startConnectTime; | ||||
private DateTime _startReceivingTime; | private DateTime _startReceivingTime; | ||||
private DateTime _startSendingTime; | private DateTime _startSendingTime; | ||||
private int _bytesToSend; | private int _bytesToSend; | ||||
private TCPRelay tcprelay; // TODO: tcprelay ?= relay | |||||
private TCPRelay _tcprelay; // TODO: is _tcprelay equals tcprelay declared above? | |||||
private Configuration _config; | |||||
public TCPHandler(TCPRelay tcprelay, Configuration config) | public TCPHandler(TCPRelay tcprelay, Configuration config) | ||||
{ | { | ||||
this.tcprelay = tcprelay; | |||||
this.config = config; | |||||
this._tcprelay = tcprelay; | |||||
this._config = config; | |||||
} | } | ||||
public void CreateRemote() | public void CreateRemote() | ||||
{ | { | ||||
Server server = controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)connection.RemoteEndPoint); | Server server = controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)connection.RemoteEndPoint); | ||||
if (server == null || server.server == "") | if (server == null || server.server == "") | ||||
{ | |||||
throw new ArgumentException("No server configured"); | throw new ArgumentException("No server configured"); | ||||
} | |||||
encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false); | encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false); | ||||
this.server = server; | this.server = server; | ||||
} | } | ||||
@@ -168,72 +151,53 @@ namespace Shadowsocks.Controller | |||||
private void CheckClose() | private void CheckClose() | ||||
{ | { | ||||
if (connectionShutdown && remoteShutdown) | |||||
{ | |||||
if (_connectionShutdown && _remoteShutdown) | |||||
Close(); | Close(); | ||||
} | |||||
} | } | ||||
public void Close() | public void Close() | ||||
{ | { | ||||
lock (relay.Handlers) | |||||
lock (tcprelay.Handlers) | |||||
{ | { | ||||
relay.Handlers.Remove(this); | |||||
tcprelay.Handlers.Remove(this); | |||||
} | } | ||||
lock (this) | |||||
lock (this) { | |||||
if (_closed) return; | |||||
_closed = true; | |||||
} | |||||
try | |||||
{ | { | ||||
if (closed) | |||||
{ | |||||
return; | |||||
} | |||||
closed = true; | |||||
connection?.Shutdown(SocketShutdown.Both); | |||||
connection?.Close(); | |||||
} | } | ||||
if (connection != null) | |||||
catch (Exception e) | |||||
{ | { | ||||
try | |||||
{ | |||||
connection.Shutdown(SocketShutdown.Both); | |||||
connection.Close(); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
Logging.LogUsefulException(e); | |||||
} | } | ||||
if (remote != null) | |||||
try | |||||
{ | { | ||||
try | |||||
{ | |||||
remote.Shutdown(SocketShutdown.Both); | |||||
remote.Close(); | |||||
} | |||||
catch (Exception e) | |||||
{ | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
remote?.Shutdown(SocketShutdown.Both); | |||||
remote?.Close(); | |||||
} | } | ||||
lock (encryptionLock) | |||||
catch (Exception e) | |||||
{ | { | ||||
lock (decryptionLock) | |||||
Logging.LogUsefulException(e); | |||||
} | |||||
lock (_encryptionLock) | |||||
{ | |||||
lock (_decryptionLock) | |||||
{ | { | ||||
if (encryptor != null) | |||||
{ | |||||
((IDisposable)encryptor).Dispose(); | |||||
} | |||||
encryptor?.Dispose(); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
private void HandshakeReceive() | private void HandshakeReceive() | ||||
{ | { | ||||
if (closed) | |||||
{ | |||||
return; | |||||
} | |||||
if (_closed) return; | |||||
try | try | ||||
{ | { | ||||
int bytesRead = _firstPacketLength; | int bytesRead = _firstPacketLength; | ||||
if (bytesRead > 1) | if (bytesRead > 1) | ||||
{ | { | ||||
byte[] response = { 5, 0 }; | byte[] response = { 5, 0 }; | ||||
@@ -243,12 +207,10 @@ namespace Shadowsocks.Controller | |||||
response = new byte[] { 0, 91 }; | response = new byte[] { 0, 91 }; | ||||
Logging.Error("socks 5 protocol error"); | Logging.Error("socks 5 protocol error"); | ||||
} | } | ||||
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null); | |||||
connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(HandshakeSendCallback), null); | |||||
} | } | ||||
else | else | ||||
{ | |||||
Close(); | Close(); | ||||
} | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -259,10 +221,7 @@ namespace Shadowsocks.Controller | |||||
private void HandshakeSendCallback(IAsyncResult ar) | private void HandshakeSendCallback(IAsyncResult ar) | ||||
{ | { | ||||
if (closed) | |||||
{ | |||||
return; | |||||
} | |||||
if (_closed) return; | |||||
try | try | ||||
{ | { | ||||
connection.EndSend(ar); | connection.EndSend(ar); | ||||
@@ -274,7 +233,7 @@ namespace Shadowsocks.Controller | |||||
// +-----+-----+-------+------+----------+----------+ | // +-----+-----+-------+------+----------+----------+ | ||||
// Skip first 3 bytes | // Skip first 3 bytes | ||||
// TODO validate | // TODO validate | ||||
connection.BeginReceive(connetionRecvBuffer, 0, 3, 0, new AsyncCallback(handshakeReceive2Callback), null); | |||||
connection.BeginReceive(_connetionRecvBuffer, 0, 3, SocketFlags.None, new AsyncCallback(handshakeReceive2Callback), null); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -285,26 +244,20 @@ namespace Shadowsocks.Controller | |||||
private void handshakeReceive2Callback(IAsyncResult ar) | private void handshakeReceive2Callback(IAsyncResult ar) | ||||
{ | { | ||||
if (closed) | |||||
{ | |||||
return; | |||||
} | |||||
if (_closed) return; | |||||
try | try | ||||
{ | { | ||||
int bytesRead = connection.EndReceive(ar); | int bytesRead = connection.EndReceive(ar); | ||||
if (bytesRead >= 3) | if (bytesRead >= 3) | ||||
{ | { | ||||
command = connetionRecvBuffer[1]; | |||||
if (command == 1) | |||||
_command = _connetionRecvBuffer[1]; | |||||
if (_command == 1) | |||||
{ | { | ||||
byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; | byte[] response = { 5, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; | ||||
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ResponseCallback), null); | |||||
connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ResponseCallback), null); | |||||
} | } | ||||
else if (command == 3) | |||||
{ | |||||
else if (_command == 3) | |||||
HandleUDPAssociate(); | HandleUDPAssociate(); | ||||
} | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -326,33 +279,31 @@ namespace Shadowsocks.Controller | |||||
int port = endPoint.Port; | int port = endPoint.Port; | ||||
byte[] response = new byte[4 + address.Length + 2]; | byte[] response = new byte[4 + address.Length + 2]; | ||||
response[0] = 5; | response[0] = 5; | ||||
if (endPoint.AddressFamily == AddressFamily.InterNetwork) | |||||
switch (endPoint.AddressFamily) | |||||
{ | { | ||||
response[3] = 1; | |||||
} | |||||
else if (endPoint.AddressFamily == AddressFamily.InterNetworkV6) | |||||
{ | |||||
response[3] = 4; | |||||
case AddressFamily.InterNetwork: | |||||
response[3] = 1; | |||||
break; | |||||
case AddressFamily.InterNetworkV6: | |||||
response[3] = 4; | |||||
break; | |||||
} | } | ||||
address.CopyTo(response, 4); | address.CopyTo(response, 4); | ||||
response[response.Length - 1] = (byte)(port & 0xFF); | response[response.Length - 1] = (byte)(port & 0xFF); | ||||
response[response.Length - 2] = (byte)((port >> 8) & 0xFF); | response[response.Length - 2] = (byte)((port >> 8) & 0xFF); | ||||
connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ReadAll), true); | |||||
connection.BeginSend(response, 0, response.Length, SocketFlags.None, new AsyncCallback(ReadAll), true); | |||||
} | } | ||||
private void ReadAll(IAsyncResult ar) | private void ReadAll(IAsyncResult ar) | ||||
{ | { | ||||
if (closed) | |||||
{ | |||||
return; | |||||
} | |||||
if (_closed) return; | |||||
try | try | ||||
{ | { | ||||
if (ar.AsyncState != null) | if (ar.AsyncState != null) | ||||
{ | { | ||||
connection.EndSend(ar); | connection.EndSend(ar); | ||||
Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); | Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); | ||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); | |||||
connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -360,12 +311,10 @@ namespace Shadowsocks.Controller | |||||
if (bytesRead > 0) | if (bytesRead > 0) | ||||
{ | { | ||||
Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); | Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, RecvSize, "TCP Relay"); | ||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); | |||||
connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(ReadAll), null); | |||||
} | } | ||||
else | else | ||||
{ | |||||
Close(); | Close(); | ||||
} | |||||
} | } | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
@@ -380,10 +329,8 @@ namespace Shadowsocks.Controller | |||||
try | try | ||||
{ | { | ||||
connection.EndSend(ar); | connection.EndSend(ar); | ||||
StartConnect(); | StartConnect(); | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
@@ -405,10 +352,7 @@ namespace Shadowsocks.Controller | |||||
private class ServerTimer : Timer | private class ServerTimer : Timer | ||||
{ | { | ||||
public Server Server; | public Server Server; | ||||
public ServerTimer(int p) : base(p) | |||||
{ | |||||
} | |||||
public ServerTimer(int p) : base(p) { } | |||||
} | } | ||||
private void StartConnect() | private void StartConnect() | ||||
@@ -429,17 +373,17 @@ namespace Shadowsocks.Controller | |||||
// Setting up proxy | // Setting up proxy | ||||
IPEndPoint proxyEP; | IPEndPoint proxyEP; | ||||
if (config.useProxy) | |||||
if (_config.useProxy) | |||||
{ | { | ||||
parsed = IPAddress.TryParse(config.proxyServer, out ipAddress); | |||||
parsed = IPAddress.TryParse(_config.proxyServer, out ipAddress); | |||||
if (!parsed) | if (!parsed) | ||||
{ | { | ||||
IPHostEntry ipHostInfo = Dns.GetHostEntry(config.proxyServer); | |||||
IPHostEntry ipHostInfo = Dns.GetHostEntry(_config.proxyServer); | |||||
ipAddress = ipHostInfo.AddressList[0]; | ipAddress = ipHostInfo.AddressList[0]; | ||||
} | } | ||||
remote = new Socks5Proxy(); | remote = new Socks5Proxy(); | ||||
proxyEP = new IPEndPoint(ipAddress, config.proxyPort); | |||||
proxyEP = new IPEndPoint(ipAddress, _config.proxyPort); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
@@ -455,7 +399,7 @@ namespace Shadowsocks.Controller | |||||
proxyTimer.DestEndPoint = destEP; | proxyTimer.DestEndPoint = destEP; | ||||
proxyTimer.Server = server; | proxyTimer.Server = server; | ||||
proxyConnected = false; | |||||
_proxyConnected = false; | |||||
// Connect to the proxy server. | // Connect to the proxy server. | ||||
remote.BeginConnectProxy(proxyEP, new AsyncCallback(ProxyConnectCallback), proxyTimer); | remote.BeginConnectProxy(proxyEP, new AsyncCallback(ProxyConnectCallback), proxyTimer); | ||||
@@ -469,7 +413,7 @@ namespace Shadowsocks.Controller | |||||
private void proxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | private void proxyConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | ||||
{ | { | ||||
if (proxyConnected || destConnected) | |||||
if (_proxyConnected || _destConnected) | |||||
{ | { | ||||
return; | return; | ||||
} | } | ||||
@@ -483,7 +427,7 @@ namespace Shadowsocks.Controller | |||||
private void ProxyConnectCallback(IAsyncResult ar) | private void ProxyConnectCallback(IAsyncResult ar) | ||||
{ | { | ||||
Server server = null; | Server server = null; | ||||
if (closed) | |||||
if (_closed) | |||||
{ | { | ||||
return; | return; | ||||
} | } | ||||
@@ -499,19 +443,18 @@ namespace Shadowsocks.Controller | |||||
// Complete the connection. | // Complete the connection. | ||||
remote.EndConnectProxy(ar); | remote.EndConnectProxy(ar); | ||||
proxyConnected = true; | |||||
_proxyConnected = true; | |||||
Logging.Debug($"Socket connected to proxy {remote.ProxyEndPoint}"); | Logging.Debug($"Socket connected to proxy {remote.ProxyEndPoint}"); | ||||
_startConnectTime = DateTime.Now; | _startConnectTime = DateTime.Now; | ||||
ServerTimer connectTimer = new ServerTimer(3000); | ServerTimer connectTimer = new ServerTimer(3000); | ||||
connectTimer.AutoReset = false; | connectTimer.AutoReset = false; | ||||
connectTimer.Elapsed += destConnectTimer_Elapsed; | connectTimer.Elapsed += destConnectTimer_Elapsed; | ||||
connectTimer.Enabled = true; | connectTimer.Enabled = true; | ||||
connectTimer.Server = server; | connectTimer.Server = server; | ||||
destConnected = false; | |||||
_destConnected = false; | |||||
// Connect to the remote endpoint. | // Connect to the remote endpoint. | ||||
remote.BeginConnectDest(destEP, new AsyncCallback(ConnectCallback), connectTimer); | remote.BeginConnectDest(destEP, new AsyncCallback(ConnectCallback), connectTimer); | ||||
} | } | ||||
@@ -527,16 +470,14 @@ namespace Shadowsocks.Controller | |||||
private void destConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | private void destConnectTimer_Elapsed(object sender, ElapsedEventArgs e) | ||||
{ | { | ||||
if (destConnected) | |||||
if (_destConnected) | |||||
{ | { | ||||
return; | return; | ||||
} | } | ||||
Server server = ((ServerTimer)sender).Server; | Server server = ((ServerTimer)sender).Server; | ||||
IStrategy strategy = controller.GetCurrentStrategy(); | IStrategy strategy = controller.GetCurrentStrategy(); | ||||
if (strategy != null) | |||||
{ | |||||
strategy.SetFailure(server); | |||||
} | |||||
strategy?.SetFailure(server); | |||||
Logging.Info($"{server.FriendlyName()} timed out"); | Logging.Info($"{server.FriendlyName()} timed out"); | ||||
remote.Close(); | remote.Close(); | ||||
RetryConnect(); | RetryConnect(); | ||||
@@ -544,25 +485,19 @@ namespace Shadowsocks.Controller | |||||
private void RetryConnect() | private void RetryConnect() | ||||
{ | { | ||||
if (retryCount < maxRetry) | |||||
if (_retryCount < MaxRetry) | |||||
{ | { | ||||
Logging.Debug($"Connection failed, retry ({retryCount})"); | |||||
Logging.Debug($"Connection failed, retry ({_retryCount})"); | |||||
StartConnect(); | StartConnect(); | ||||
retryCount++; | |||||
_retryCount++; | |||||
} | } | ||||
else | else | ||||
{ | |||||
Close(); | Close(); | ||||
} | |||||
} | } | ||||
private void ConnectCallback(IAsyncResult ar) | private void ConnectCallback(IAsyncResult ar) | ||||
{ | { | ||||
Server server = null; | |||||
if (closed) | |||||
{ | |||||
return; | |||||
} | |||||
if (_closed) return; | |||||
try | try | ||||
{ | { | ||||
ServerTimer timer = (ServerTimer)ar.AsyncState; | ServerTimer timer = (ServerTimer)ar.AsyncState; | ||||
@@ -573,15 +508,13 @@ namespace Shadowsocks.Controller | |||||
// Complete the connection. | // Complete the connection. | ||||
remote.EndConnectDest(ar); | remote.EndConnectDest(ar); | ||||
destConnected = true; | |||||
Logging.Debug($"Socket connected to {remote.DestEndPoint}"); | |||||
_destConnected = true; | |||||
var latency = DateTime.Now - _startConnectTime; | var latency = DateTime.Now - _startConnectTime; | ||||
IStrategy strategy = controller.GetCurrentStrategy(); | IStrategy strategy = controller.GetCurrentStrategy(); | ||||
strategy?.UpdateLatency(server, latency); | strategy?.UpdateLatency(server, latency); | ||||
tcprelay.UpdateLatency(server, latency); | |||||
_tcprelay.UpdateLatency(server, latency); | |||||
StartPipe(); | StartPipe(); | ||||
} | } | ||||
@@ -593,10 +526,7 @@ namespace Shadowsocks.Controller | |||||
if (server != null) | if (server != null) | ||||
{ | { | ||||
IStrategy strategy = controller.GetCurrentStrategy(); | IStrategy strategy = controller.GetCurrentStrategy(); | ||||
if (strategy != null) | |||||
{ | |||||
strategy.SetFailure(server); | |||||
} | |||||
strategy?.SetFailure(server); | |||||
} | } | ||||
Logging.LogUsefulException(e); | Logging.LogUsefulException(e); | ||||
RetryConnect(); | RetryConnect(); | ||||
@@ -605,15 +535,12 @@ namespace Shadowsocks.Controller | |||||
private void StartPipe() | private void StartPipe() | ||||
{ | { | ||||
if (closed) | |||||
{ | |||||
return; | |||||
} | |||||
if (_closed) return; | |||||
try | try | ||||
{ | { | ||||
_startReceivingTime = DateTime.Now; | _startReceivingTime = DateTime.Now; | ||||
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null); | |||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null); | |||||
remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), null); | |||||
connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), null); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -624,49 +551,30 @@ namespace Shadowsocks.Controller | |||||
private void PipeRemoteReceiveCallback(IAsyncResult ar) | private void PipeRemoteReceiveCallback(IAsyncResult ar) | ||||
{ | { | ||||
if (closed) | |||||
{ | |||||
return; | |||||
} | |||||
if (_closed) return; | |||||
try | try | ||||
{ | { | ||||
int bytesRead = remote.EndReceive(ar); | int bytesRead = remote.EndReceive(ar); | ||||
totalRead += bytesRead; | |||||
tcprelay.UpdateInboundCounter(server, bytesRead); | |||||
_totalRead += bytesRead; | |||||
_tcprelay.UpdateInboundCounter(server, bytesRead); | |||||
if (bytesRead > 0) | if (bytesRead > 0) | ||||
{ | { | ||||
lastActivity = DateTime.Now; | lastActivity = DateTime.Now; | ||||
int bytesToSend; | int bytesToSend; | ||||
lock (decryptionLock) | |||||
lock (_decryptionLock) | |||||
{ | { | ||||
if (closed) | |||||
{ | |||||
return; | |||||
} | |||||
encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend); | |||||
if (_closed) return; | |||||
encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); | |||||
} | } | ||||
Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, bytesToSend, "TCP Relay", "@PipeRemoteReceiveCallback() (download)"); | |||||
connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null); | |||||
connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeConnectionSendCallback), null); | |||||
IStrategy strategy = controller.GetCurrentStrategy(); | IStrategy strategy = controller.GetCurrentStrategy(); | ||||
if (strategy != null) | |||||
{ | |||||
strategy.UpdateLastRead(server); | |||||
} | |||||
strategy?.UpdateLastRead(server); | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
connection.Shutdown(SocketShutdown.Send); | connection.Shutdown(SocketShutdown.Send); | ||||
connectionShutdown = true; | |||||
_connectionShutdown = true; | |||||
CheckClose(); | CheckClose(); | ||||
//if (totalRead == 0) | |||||
//{ | |||||
// // closed before anything received, reports as failure | |||||
// // disable this feature | |||||
// controller.GetCurrentStrategy().SetFailure(this.server); | |||||
//} | |||||
} | } | ||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
@@ -678,39 +586,58 @@ namespace Shadowsocks.Controller | |||||
private void PipeConnectionReceiveCallback(IAsyncResult ar) | private void PipeConnectionReceiveCallback(IAsyncResult ar) | ||||
{ | { | ||||
if (closed) | |||||
{ | |||||
return; | |||||
} | |||||
if (_closed) return; | |||||
try | try | ||||
{ | { | ||||
int bytesRead = connection.EndReceive(ar); | int bytesRead = connection.EndReceive(ar); | ||||
totalWrite += bytesRead; | |||||
_totalWrite += bytesRead; | |||||
if (bytesRead > 0) | if (bytesRead > 0) | ||||
{ | { | ||||
int atyp = _connetionRecvBuffer[0]; | |||||
string dst_addr; | |||||
int dst_port; | |||||
switch (atyp) | |||||
{ | |||||
case 1: // IPv4 address, 4 bytes | |||||
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString(); | |||||
dst_port = (_connetionRecvBuffer[5] << 8) + _connetionRecvBuffer[6]; | |||||
if ( _config.isVerboseLogging ) { | |||||
Logging.Info( $"connect to {dst_addr}:{dst_port}" ); | |||||
} | |||||
break; | |||||
case 3: // domain name, length + str | |||||
int len = _connetionRecvBuffer[1]; | |||||
dst_addr = System.Text.Encoding.UTF8.GetString(_connetionRecvBuffer, 2, len); | |||||
dst_port = (_connetionRecvBuffer[len + 2] << 8) + _connetionRecvBuffer[len + 3]; | |||||
if ( _config.isVerboseLogging ) { | |||||
Logging.Info( $"connect to {dst_addr}:{dst_port}" ); | |||||
} | |||||
break; | |||||
case 4: // IPv6 address, 16 bytes | |||||
dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(16).ToArray()).ToString(); | |||||
dst_port = (_connetionRecvBuffer[17] << 8) + _connetionRecvBuffer[18]; | |||||
if ( _config.isVerboseLogging ) { | |||||
Logging.Info( $"connect to [{dst_addr}]:{dst_port}" ); | |||||
} | |||||
break; | |||||
} | |||||
int bytesToSend; | int bytesToSend; | ||||
lock (encryptionLock) | |||||
lock (_encryptionLock) | |||||
{ | { | ||||
if (closed) | |||||
{ | |||||
return; | |||||
} | |||||
encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); | |||||
if (_closed) return; | |||||
encryptor.Encrypt(_connetionRecvBuffer, bytesRead, _connetionSendBuffer, out bytesToSend); | |||||
} | } | ||||
Logging.Debug(remote.LocalEndPoint, remote.DestEndPoint, bytesToSend, "TCP Relay", "@PipeConnectionReceiveCallback() (upload)"); | |||||
tcprelay.UpdateOutboundCounter(server, bytesToSend); | |||||
_tcprelay.UpdateOutboundCounter(server, bytesToSend); | |||||
_startSendingTime = DateTime.Now; | _startSendingTime = DateTime.Now; | ||||
_bytesToSend = bytesToSend; | _bytesToSend = bytesToSend; | ||||
remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); | |||||
remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, SocketFlags.None, new AsyncCallback(PipeRemoteSendCallback), null); | |||||
IStrategy strategy = controller.GetCurrentStrategy(); | IStrategy strategy = controller.GetCurrentStrategy(); | ||||
strategy?.UpdateLastWrite(server); | strategy?.UpdateLastWrite(server); | ||||
} | } | ||||
else | else | ||||
{ | { | ||||
remote.Shutdown(SocketShutdown.Send); | remote.Shutdown(SocketShutdown.Send); | ||||
remoteShutdown = true; | |||||
_remoteShutdown = true; | |||||
CheckClose(); | CheckClose(); | ||||
} | } | ||||
} | } | ||||
@@ -723,14 +650,11 @@ namespace Shadowsocks.Controller | |||||
private void PipeRemoteSendCallback(IAsyncResult ar) | private void PipeRemoteSendCallback(IAsyncResult ar) | ||||
{ | { | ||||
if (closed) | |||||
{ | |||||
return; | |||||
} | |||||
if (_closed) return; | |||||
try | try | ||||
{ | { | ||||
remote.EndSend(ar); | remote.EndSend(ar); | ||||
connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null); | |||||
connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeConnectionReceiveCallback), null); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -741,14 +665,11 @@ namespace Shadowsocks.Controller | |||||
private void PipeConnectionSendCallback(IAsyncResult ar) | private void PipeConnectionSendCallback(IAsyncResult ar) | ||||
{ | { | ||||
if (closed) | |||||
{ | |||||
return; | |||||
} | |||||
if (_closed) return; | |||||
try | try | ||||
{ | { | ||||
connection.EndSend(ar); | connection.EndSend(ar); | ||||
remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null); | |||||
remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, SocketFlags.None, new AsyncCallback(PipeRemoteReceiveCallback), null); | |||||
} | } | ||||
catch (Exception e) | catch (Exception e) | ||||
{ | { | ||||
@@ -5,7 +5,6 @@ using System.Net; | |||||
using System.Net.Sockets; | using System.Net.Sockets; | ||||
using System.Text; | using System.Text; | ||||
using System.Threading; | using System.Threading; | ||||
using System.Threading.Tasks; | |||||
using Newtonsoft.Json; | using Newtonsoft.Json; | ||||
using Shadowsocks.Controller.Strategy; | using Shadowsocks.Controller.Strategy; | ||||
@@ -23,6 +22,7 @@ namespace Shadowsocks.Controller | |||||
// interacts with low level logic | // interacts with low level logic | ||||
private Thread _ramThread; | private Thread _ramThread; | ||||
private Thread _trafficThread; | |||||
private Listener _listener; | private Listener _listener; | ||||
private PACServer _pacServer; | private PACServer _pacServer; | ||||
@@ -35,6 +35,7 @@ namespace Shadowsocks.Controller | |||||
public long inboundCounter = 0; | public long inboundCounter = 0; | ||||
public long outboundCounter = 0; | public long outboundCounter = 0; | ||||
public QueueLast<TrafficPerSecond> traffic; | |||||
private bool stopped = false; | private bool stopped = false; | ||||
@@ -45,10 +46,30 @@ namespace Shadowsocks.Controller | |||||
public string Path; | public string Path; | ||||
} | } | ||||
public class QueueLast<T> : Queue<T> | |||||
{ | |||||
public T Last { get; private set; } | |||||
public new void Enqueue(T item) | |||||
{ | |||||
Last = item; | |||||
base.Enqueue(item); | |||||
} | |||||
} | |||||
public class TrafficPerSecond | |||||
{ | |||||
public long inboundCounter; | |||||
public long outboundCounter; | |||||
public long inboundIncreasement; | |||||
public long outboundIncreasement; | |||||
} | |||||
public event EventHandler ConfigChanged; | public event EventHandler ConfigChanged; | ||||
public event EventHandler EnableStatusChanged; | public event EventHandler EnableStatusChanged; | ||||
public event EventHandler EnableGlobalChanged; | public event EventHandler EnableGlobalChanged; | ||||
public event EventHandler ShareOverLANStatusChanged; | public event EventHandler ShareOverLANStatusChanged; | ||||
public event EventHandler VerboseLoggingStatusChanged; | |||||
public event EventHandler TrafficChanged; | |||||
// when user clicked Edit PAC, and PAC file has already created | // when user clicked Edit PAC, and PAC file has already created | ||||
public event EventHandler<PathEventArgs> PACFileReadyToOpen; | public event EventHandler<PathEventArgs> PACFileReadyToOpen; | ||||
@@ -66,6 +87,7 @@ namespace Shadowsocks.Controller | |||||
StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); | StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); | ||||
_strategyManager = new StrategyManager(this); | _strategyManager = new StrategyManager(this); | ||||
StartReleasingMemory(); | StartReleasingMemory(); | ||||
StartTrafficStatistics(60); | |||||
} | } | ||||
public void Start() | public void Start() | ||||
@@ -205,6 +227,15 @@ namespace Shadowsocks.Controller | |||||
SaveConfig(_config); | SaveConfig(_config); | ||||
} | } | ||||
public void ToggleVerboseLogging(bool enabled) | |||||
{ | |||||
_config.isVerboseLogging = enabled; | |||||
SaveConfig(_config); | |||||
if ( VerboseLoggingStatusChanged != null ) { | |||||
VerboseLoggingStatusChanged(this, new EventArgs()); | |||||
} | |||||
} | |||||
public void SelectServerIndex(int index) | public void SelectServerIndex(int index) | ||||
{ | { | ||||
_config.index = index; | _config.index = index; | ||||
@@ -327,7 +358,7 @@ namespace Shadowsocks.Controller | |||||
{ | { | ||||
if (_config.availabilityStatistics) | if (_config.availabilityStatistics) | ||||
{ | { | ||||
new Task(() => availabilityStatistics.UpdateLatency(server, (int) latency.TotalMilliseconds)).Start(); | |||||
availabilityStatistics.UpdateLatency(server, (int)latency.TotalMilliseconds); | |||||
} | } | ||||
} | } | ||||
@@ -336,7 +367,7 @@ namespace Shadowsocks.Controller | |||||
Interlocked.Add(ref inboundCounter, n); | Interlocked.Add(ref inboundCounter, n); | ||||
if (_config.availabilityStatistics) | if (_config.availabilityStatistics) | ||||
{ | { | ||||
new Task(() => availabilityStatistics.UpdateInboundCounter(server, n)).Start(); | |||||
availabilityStatistics.UpdateInboundCounter(server, n); | |||||
} | } | ||||
} | } | ||||
@@ -345,7 +376,7 @@ namespace Shadowsocks.Controller | |||||
Interlocked.Add(ref outboundCounter, n); | Interlocked.Add(ref outboundCounter, n); | ||||
if (_config.availabilityStatistics) | if (_config.availabilityStatistics) | ||||
{ | { | ||||
new Task(() => availabilityStatistics.UpdateOutboundCounter(server, n)).Start(); | |||||
availabilityStatistics.UpdateOutboundCounter(server, n); | |||||
} | } | ||||
} | } | ||||
@@ -530,5 +561,41 @@ namespace Shadowsocks.Controller | |||||
} | } | ||||
} | } | ||||
private void StartTrafficStatistics(int queueMaxSize) | |||||
{ | |||||
traffic = new QueueLast<TrafficPerSecond>(); | |||||
for (int i = 0; i < queueMaxSize; i++) | |||||
{ | |||||
traffic.Enqueue(new TrafficPerSecond()); | |||||
} | |||||
_trafficThread = new Thread(new ThreadStart(() => TrafficStatistics(queueMaxSize))); | |||||
_trafficThread.IsBackground = true; | |||||
_trafficThread.Start(); | |||||
} | |||||
private void TrafficStatistics(int queueMaxSize) | |||||
{ | |||||
while (true) | |||||
{ | |||||
TrafficPerSecond previous = traffic.Last; | |||||
TrafficPerSecond current = new TrafficPerSecond(); | |||||
current.inboundCounter = inboundCounter; | |||||
current.outboundCounter = outboundCounter; | |||||
current.inboundIncreasement = inboundCounter - previous.inboundCounter; | |||||
current.outboundIncreasement = outboundCounter - previous.outboundCounter; | |||||
traffic.Enqueue(current); | |||||
if (traffic.Count > queueMaxSize) | |||||
traffic.Dequeue(); | |||||
if (TrafficChanged != null) | |||||
{ | |||||
TrafficChanged(this, new EventArgs()); | |||||
} | |||||
Thread.Sleep(1000); | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -23,6 +23,7 @@ Show QRCode...=显示二维码... | |||||
Scan QRCode from Screen...=扫描屏幕上的二维码... | Scan QRCode from Screen...=扫描屏幕上的二维码... | ||||
Availability Statistics=统计可用性 | Availability Statistics=统计可用性 | ||||
Show Logs...=显示日志... | Show Logs...=显示日志... | ||||
Verbose Logging=详细记录日志 | |||||
Updates...=更新... | Updates...=更新... | ||||
Check for Updates...=检查更新 | Check for Updates...=检查更新 | ||||
Check for Updates at Startup=启动时检查更新 | Check for Updates at Startup=启动时检查更新 | ||||
@@ -70,6 +71,8 @@ Change &Font=设置字体(&F) | |||||
&Top Most=置顶(&T) | &Top Most=置顶(&T) | ||||
&Show Toolbar=显示工具栏(&S) | &Show Toolbar=显示工具栏(&S) | ||||
Log Viewer=日志查看器 | Log Viewer=日志查看器 | ||||
Inbound=入站 | |||||
Outbound=出站 | |||||
# QRCode Form | # QRCode Form | ||||
@@ -109,3 +112,5 @@ Failed to decode QRCode=无法解析二维码 | |||||
Failed to update registry=无法修改注册表 | Failed to update registry=无法修改注册表 | ||||
System Proxy On: =系统代理已启用: | System Proxy On: =系统代理已启用: | ||||
Running: Port {0}=正在运行:端口 {0} | Running: Port {0}=正在运行:端口 {0} | ||||
Unexpect error, shadowsocks will be exit. Please report to=非预期错误,Shadowsocks将退出。请提交此错误到 | |||||
@@ -24,6 +24,7 @@ namespace Shadowsocks.Model | |||||
public bool useOnlinePac; | public bool useOnlinePac; | ||||
public bool availabilityStatistics; | public bool availabilityStatistics; | ||||
public bool autoCheckUpdate; | public bool autoCheckUpdate; | ||||
public bool isVerboseLogging; | |||||
public LogViewerConfig logViewer; | public LogViewerConfig logViewer; | ||||
public bool useProxy; | public bool useProxy; | ||||
public string proxyServer; | public string proxyServer; | ||||
@@ -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,19 @@ namespace Shadowsocks | |||||
Application.Run(); | Application.Run(); | ||||
} | } | ||||
} | } | ||||
private static int exited = 0; | |||||
private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) | |||||
{ | |||||
if (Interlocked.Increment(ref exited) == 1) | |||||
{ | |||||
Logging.Error(e.ExceptionObject?.ToString()); | |||||
MessageBox.Show(I18N.GetString("Unexpect error, shadowsocks will be exit. Please report to") + | |||||
" https://github.com/shadowsocks/shadowsocks-windows/issues " + | |||||
Environment.NewLine + (e.ExceptionObject?.ToString()), | |||||
"Shadowsocks Error", MessageBoxButtons.OK, MessageBoxIcon.Error); | |||||
Application.Exit(); | |||||
} | |||||
} | |||||
} | } | ||||
} | } |
@@ -182,7 +182,27 @@ namespace Shadowsocks.Properties { | |||||
return ((System.Drawing.Bitmap)(obj)); | return ((System.Drawing.Bitmap)(obj)); | ||||
} | } | ||||
} | } | ||||
/// <summary> | |||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||||
/// </summary> | |||||
internal static System.Drawing.Bitmap ssIn24 { | |||||
get { | |||||
object obj = ResourceManager.GetObject("ssIn24", resourceCulture); | |||||
return ((System.Drawing.Bitmap)(obj)); | |||||
} | |||||
} | |||||
/// <summary> | |||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | |||||
/// </summary> | |||||
internal static System.Drawing.Bitmap ssOut24 { | |||||
get { | |||||
object obj = ResourceManager.GetObject("ssOut24", resourceCulture); | |||||
return ((System.Drawing.Bitmap)(obj)); | |||||
} | |||||
} | |||||
/// <summary> | /// <summary> | ||||
/// Looks up a localized resource of type System.Drawing.Bitmap. | /// Looks up a localized resource of type System.Drawing.Bitmap. | ||||
/// </summary> | /// </summary> | ||||
@@ -148,6 +148,12 @@ | |||||
<data name="ss24" type="System.Resources.ResXFileRef, System.Windows.Forms"> | <data name="ss24" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||
<value>..\Resources\ss24.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | <value>..\Resources\ss24.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | ||||
</data> | </data> | ||||
<data name="ssIn24" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||||
<value>..\Resources\ssIn24.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | |||||
</data> | |||||
<data name="ssOut24" type="System.Resources.ResXFileRef, System.Windows.Forms"> | |||||
<value>..\Resources\ssOut24.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | |||||
</data> | |||||
<data name="ssw128" type="System.Resources.ResXFileRef, System.Windows.Forms"> | <data name="ssw128" type="System.Resources.ResXFileRef, System.Windows.Forms"> | ||||
<value>..\Resources\ssw128.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | <value>..\Resources\ssw128.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> | ||||
</data> | </data> | ||||
@@ -76,7 +76,8 @@ namespace Shadowsocks.Util | |||||
// | // | ||||
// just kidding | // just kidding | ||||
SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, | SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, | ||||
(UIntPtr)0xFFFFFFFF, (UIntPtr)0xFFFFFFFF); | |||||
(UIntPtr)0xFFFFFFFF, | |||||
(UIntPtr)0xFFFFFFFF); | |||||
} | } | ||||
} | } | ||||
@@ -87,7 +88,8 @@ namespace Shadowsocks.Util | |||||
using (MemoryStream sb = new MemoryStream()) | using (MemoryStream sb = new MemoryStream()) | ||||
{ | { | ||||
using (GZipStream input = new GZipStream(new MemoryStream(buf), | using (GZipStream input = new GZipStream(new MemoryStream(buf), | ||||
CompressionMode.Decompress, false)) | |||||
CompressionMode.Decompress, | |||||
false)) | |||||
{ | { | ||||
while ((n = input.Read(buffer, 0, buffer.Length)) > 0) | while ((n = input.Read(buffer, 0, buffer.Length)) > 0) | ||||
{ | { | ||||
@@ -100,29 +102,49 @@ namespace Shadowsocks.Util | |||||
public static string FormatBandwidth(long n) | public static string FormatBandwidth(long n) | ||||
{ | { | ||||
var result = GetBandwidthScale(n); | |||||
return $"{result.Item1:0.##}{result.Item2}"; | |||||
} | |||||
/// <summary> | |||||
/// Return scaled bandwidth | |||||
/// </summary> | |||||
/// <param name="n">Raw bandwidth</param> | |||||
/// <returns> | |||||
/// Item1: float, bandwidth with suitable scale (eg. 56) | |||||
/// Item2: string, scale unit name (eg. KiB) | |||||
/// Item3: long, scale unit (eg. 1024) | |||||
/// </returns> | |||||
public static Tuple<float, string, long> GetBandwidthScale(long n) | |||||
{ | |||||
long scale = 1; | |||||
float f = n; | float f = n; | ||||
string unit = "B"; | string unit = "B"; | ||||
if (f > 1024) | if (f > 1024) | ||||
{ | { | ||||
f = f / 1024; | f = f / 1024; | ||||
scale <<= 10; | |||||
unit = "KiB"; | unit = "KiB"; | ||||
} | } | ||||
if (f > 1024) | if (f > 1024) | ||||
{ | { | ||||
f = f / 1024; | f = f / 1024; | ||||
scale <<= 10; | |||||
unit = "MiB"; | unit = "MiB"; | ||||
} | } | ||||
if (f > 1024) | if (f > 1024) | ||||
{ | { | ||||
f = f / 1024; | f = f / 1024; | ||||
scale <<= 10; | |||||
unit = "GiB"; | unit = "GiB"; | ||||
} | } | ||||
if (f > 1024) | if (f > 1024) | ||||
{ | { | ||||
f = f / 1024; | f = f / 1024; | ||||
scale <<= 10; | |||||
unit = "TiB"; | unit = "TiB"; | ||||
} | } | ||||
return $"{f:0.##}{unit}"; | |||||
return new Tuple<float, string, long>(f, unit, scale); | |||||
} | } | ||||
[DllImport("kernel32.dll")] | [DllImport("kernel32.dll")] | ||||
@@ -29,6 +29,10 @@ | |||||
private void InitializeComponent() | private void InitializeComponent() | ||||
{ | { | ||||
this.components = new System.ComponentModel.Container(); | this.components = new System.ComponentModel.Container(); | ||||
System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea(); | |||||
System.Windows.Forms.DataVisualization.Charting.Legend legend1 = new System.Windows.Forms.DataVisualization.Charting.Legend(); | |||||
System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series(); | |||||
System.Windows.Forms.DataVisualization.Charting.Series series2 = new System.Windows.Forms.DataVisualization.Charting.Series(); | |||||
this.LogMessageTextBox = new System.Windows.Forms.TextBox(); | this.LogMessageTextBox = new System.Windows.Forms.TextBox(); | ||||
this.MainMenu = new System.Windows.Forms.MainMenu(this.components); | this.MainMenu = new System.Windows.Forms.MainMenu(this.components); | ||||
this.FileMenuItem = new System.Windows.Forms.MenuItem(); | this.FileMenuItem = new System.Windows.Forms.MenuItem(); | ||||
@@ -47,8 +51,15 @@ | |||||
this.WrapTextCheckBox = new System.Windows.Forms.CheckBox(); | this.WrapTextCheckBox = new System.Windows.Forms.CheckBox(); | ||||
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); | this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); | ||||
this.ToolbarFlowLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); | this.ToolbarFlowLayoutPanel = new System.Windows.Forms.FlowLayoutPanel(); | ||||
this.splitContainer1 = new System.Windows.Forms.SplitContainer(); | |||||
this.trafficChart = new System.Windows.Forms.DataVisualization.Charting.Chart(); | |||||
this.tableLayoutPanel1.SuspendLayout(); | this.tableLayoutPanel1.SuspendLayout(); | ||||
this.ToolbarFlowLayoutPanel.SuspendLayout(); | this.ToolbarFlowLayoutPanel.SuspendLayout(); | ||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); | |||||
this.splitContainer1.Panel1.SuspendLayout(); | |||||
this.splitContainer1.Panel2.SuspendLayout(); | |||||
this.splitContainer1.SuspendLayout(); | |||||
((System.ComponentModel.ISupportInitialize)(this.trafficChart)).BeginInit(); | |||||
this.SuspendLayout(); | this.SuspendLayout(); | ||||
// | // | ||||
// LogMessageTextBox | // LogMessageTextBox | ||||
@@ -57,13 +68,13 @@ | |||||
this.LogMessageTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | this.LogMessageTextBox.Dock = System.Windows.Forms.DockStyle.Fill; | ||||
this.LogMessageTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | this.LogMessageTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); | ||||
this.LogMessageTextBox.ForeColor = System.Drawing.Color.White; | this.LogMessageTextBox.ForeColor = System.Drawing.Color.White; | ||||
this.LogMessageTextBox.Location = new System.Drawing.Point(3, 40); | |||||
this.LogMessageTextBox.Location = new System.Drawing.Point(0, 0); | |||||
this.LogMessageTextBox.MaxLength = 2147483647; | this.LogMessageTextBox.MaxLength = 2147483647; | ||||
this.LogMessageTextBox.Multiline = true; | this.LogMessageTextBox.Multiline = true; | ||||
this.LogMessageTextBox.Name = "LogMessageTextBox"; | this.LogMessageTextBox.Name = "LogMessageTextBox"; | ||||
this.LogMessageTextBox.ReadOnly = true; | this.LogMessageTextBox.ReadOnly = true; | ||||
this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; | this.LogMessageTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; | ||||
this.LogMessageTextBox.Size = new System.Drawing.Size(378, 131); | |||||
this.LogMessageTextBox.Size = new System.Drawing.Size(378, 74); | |||||
this.LogMessageTextBox.TabIndex = 0; | this.LogMessageTextBox.TabIndex = 0; | ||||
// | // | ||||
// MainMenu | // MainMenu | ||||
@@ -144,9 +155,9 @@ | |||||
this.TopMostCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | this.TopMostCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | ||||
| System.Windows.Forms.AnchorStyles.Left))); | | System.Windows.Forms.AnchorStyles.Left))); | ||||
this.TopMostCheckBox.AutoSize = true; | this.TopMostCheckBox.AutoSize = true; | ||||
this.TopMostCheckBox.Location = new System.Drawing.Point(247, 3); | |||||
this.TopMostCheckBox.Location = new System.Drawing.Point(249, 3); | |||||
this.TopMostCheckBox.Name = "TopMostCheckBox"; | this.TopMostCheckBox.Name = "TopMostCheckBox"; | ||||
this.TopMostCheckBox.Size = new System.Drawing.Size(71, 25); | |||||
this.TopMostCheckBox.Size = new System.Drawing.Size(72, 23); | |||||
this.TopMostCheckBox.TabIndex = 3; | this.TopMostCheckBox.TabIndex = 3; | ||||
this.TopMostCheckBox.Text = "&Top Most"; | this.TopMostCheckBox.Text = "&Top Most"; | ||||
this.TopMostCheckBox.UseVisualStyleBackColor = true; | this.TopMostCheckBox.UseVisualStyleBackColor = true; | ||||
@@ -157,7 +168,7 @@ | |||||
this.ChangeFontButton.AutoSize = true; | this.ChangeFontButton.AutoSize = true; | ||||
this.ChangeFontButton.Location = new System.Drawing.Point(84, 3); | this.ChangeFontButton.Location = new System.Drawing.Point(84, 3); | ||||
this.ChangeFontButton.Name = "ChangeFontButton"; | this.ChangeFontButton.Name = "ChangeFontButton"; | ||||
this.ChangeFontButton.Size = new System.Drawing.Size(75, 25); | |||||
this.ChangeFontButton.Size = new System.Drawing.Size(75, 23); | |||||
this.ChangeFontButton.TabIndex = 2; | this.ChangeFontButton.TabIndex = 2; | ||||
this.ChangeFontButton.Text = "&Font"; | this.ChangeFontButton.Text = "&Font"; | ||||
this.ChangeFontButton.UseVisualStyleBackColor = true; | this.ChangeFontButton.UseVisualStyleBackColor = true; | ||||
@@ -168,7 +179,7 @@ | |||||
this.CleanLogsButton.AutoSize = true; | this.CleanLogsButton.AutoSize = true; | ||||
this.CleanLogsButton.Location = new System.Drawing.Point(3, 3); | this.CleanLogsButton.Location = new System.Drawing.Point(3, 3); | ||||
this.CleanLogsButton.Name = "CleanLogsButton"; | this.CleanLogsButton.Name = "CleanLogsButton"; | ||||
this.CleanLogsButton.Size = new System.Drawing.Size(75, 25); | |||||
this.CleanLogsButton.Size = new System.Drawing.Size(75, 23); | |||||
this.CleanLogsButton.TabIndex = 1; | this.CleanLogsButton.TabIndex = 1; | ||||
this.CleanLogsButton.Text = "&Clean Logs"; | this.CleanLogsButton.Text = "&Clean Logs"; | ||||
this.CleanLogsButton.UseVisualStyleBackColor = true; | this.CleanLogsButton.UseVisualStyleBackColor = true; | ||||
@@ -181,7 +192,7 @@ | |||||
this.WrapTextCheckBox.AutoSize = true; | this.WrapTextCheckBox.AutoSize = true; | ||||
this.WrapTextCheckBox.Location = new System.Drawing.Point(165, 3); | this.WrapTextCheckBox.Location = new System.Drawing.Point(165, 3); | ||||
this.WrapTextCheckBox.Name = "WrapTextCheckBox"; | this.WrapTextCheckBox.Name = "WrapTextCheckBox"; | ||||
this.WrapTextCheckBox.Size = new System.Drawing.Size(76, 25); | |||||
this.WrapTextCheckBox.Size = new System.Drawing.Size(78, 23); | |||||
this.WrapTextCheckBox.TabIndex = 0; | this.WrapTextCheckBox.TabIndex = 0; | ||||
this.WrapTextCheckBox.Text = "&Wrap Text"; | this.WrapTextCheckBox.Text = "&Wrap Text"; | ||||
this.WrapTextCheckBox.UseVisualStyleBackColor = true; | this.WrapTextCheckBox.UseVisualStyleBackColor = true; | ||||
@@ -191,15 +202,15 @@ | |||||
// | // | ||||
this.tableLayoutPanel1.ColumnCount = 1; | this.tableLayoutPanel1.ColumnCount = 1; | ||||
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); | this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); | ||||
this.tableLayoutPanel1.Controls.Add(this.LogMessageTextBox, 0, 1); | |||||
this.tableLayoutPanel1.Controls.Add(this.ToolbarFlowLayoutPanel, 0, 0); | this.tableLayoutPanel1.Controls.Add(this.ToolbarFlowLayoutPanel, 0, 0); | ||||
this.tableLayoutPanel1.Controls.Add(this.splitContainer1, 0, 1); | |||||
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; | this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; | ||||
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); | this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); | ||||
this.tableLayoutPanel1.Name = "tableLayoutPanel1"; | this.tableLayoutPanel1.Name = "tableLayoutPanel1"; | ||||
this.tableLayoutPanel1.RowCount = 2; | this.tableLayoutPanel1.RowCount = 2; | ||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | ||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); | |||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(384, 174); | |||||
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F)); | |||||
this.tableLayoutPanel1.Size = new System.Drawing.Size(384, 161); | |||||
this.tableLayoutPanel1.TabIndex = 2; | this.tableLayoutPanel1.TabIndex = 2; | ||||
// | // | ||||
// ToolbarFlowLayoutPanel | // ToolbarFlowLayoutPanel | ||||
@@ -212,17 +223,73 @@ | |||||
this.ToolbarFlowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; | this.ToolbarFlowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; | ||||
this.ToolbarFlowLayoutPanel.Location = new System.Drawing.Point(3, 3); | this.ToolbarFlowLayoutPanel.Location = new System.Drawing.Point(3, 3); | ||||
this.ToolbarFlowLayoutPanel.Name = "ToolbarFlowLayoutPanel"; | this.ToolbarFlowLayoutPanel.Name = "ToolbarFlowLayoutPanel"; | ||||
this.ToolbarFlowLayoutPanel.Size = new System.Drawing.Size(378, 31); | |||||
this.ToolbarFlowLayoutPanel.Size = new System.Drawing.Size(378, 29); | |||||
this.ToolbarFlowLayoutPanel.TabIndex = 2; | this.ToolbarFlowLayoutPanel.TabIndex = 2; | ||||
// | // | ||||
// splitContainer1 | |||||
// | |||||
this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
this.splitContainer1.Location = new System.Drawing.Point(3, 38); | |||||
this.splitContainer1.Name = "splitContainer1"; | |||||
this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; | |||||
// | |||||
// splitContainer1.Panel1 | |||||
// | |||||
this.splitContainer1.Panel1.Controls.Add(this.LogMessageTextBox); | |||||
// | |||||
// splitContainer1.Panel2 | |||||
// | |||||
this.splitContainer1.Panel2.Controls.Add(this.trafficChart); | |||||
this.splitContainer1.Size = new System.Drawing.Size(378, 120); | |||||
this.splitContainer1.SplitterDistance = 74; | |||||
this.splitContainer1.TabIndex = 3; | |||||
// | |||||
// trafficChart | |||||
// | |||||
chartArea1.AxisX.LabelStyle.Enabled = false; | |||||
chartArea1.AxisX.MajorGrid.Interval = 5D; | |||||
chartArea1.AxisX.MajorGrid.LineColor = System.Drawing.Color.LightGray; | |||||
chartArea1.AxisX.MajorTickMark.Enabled = false; | |||||
chartArea1.AxisY.IntervalAutoMode = System.Windows.Forms.DataVisualization.Charting.IntervalAutoMode.VariableCount; | |||||
chartArea1.AxisY.LabelAutoFitMaxFontSize = 8; | |||||
chartArea1.AxisY.LabelStyle.Interval = 0D; | |||||
chartArea1.AxisY.MajorGrid.LineColor = System.Drawing.Color.LightGray; | |||||
chartArea1.AxisY.MajorTickMark.Enabled = false; | |||||
chartArea1.AxisY2.MajorGrid.LineColor = System.Drawing.Color.LightGray; | |||||
chartArea1.AxisY2.Minimum = 0D; | |||||
chartArea1.Name = "ChartArea1"; | |||||
this.trafficChart.ChartAreas.Add(chartArea1); | |||||
this.trafficChart.Dock = System.Windows.Forms.DockStyle.Fill; | |||||
legend1.MaximumAutoSize = 25F; | |||||
legend1.Name = "Legend1"; | |||||
this.trafficChart.Legends.Add(legend1); | |||||
this.trafficChart.Location = new System.Drawing.Point(0, 0); | |||||
this.trafficChart.Name = "trafficChart"; | |||||
this.trafficChart.Palette = System.Windows.Forms.DataVisualization.Charting.ChartColorPalette.None; | |||||
series1.ChartArea = "ChartArea1"; | |||||
series1.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; | |||||
series1.IsXValueIndexed = true; | |||||
series1.Legend = "Legend1"; | |||||
series1.Name = "Inbound"; | |||||
series2.ChartArea = "ChartArea1"; | |||||
series2.ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line; | |||||
series2.IsXValueIndexed = true; | |||||
series2.Legend = "Legend1"; | |||||
series2.Name = "Outbound"; | |||||
this.trafficChart.Series.Add(series1); | |||||
this.trafficChart.Series.Add(series2); | |||||
this.trafficChart.Size = new System.Drawing.Size(378, 42); | |||||
this.trafficChart.TabIndex = 0; | |||||
this.trafficChart.Text = "chart1"; | |||||
// | |||||
// LogForm | // LogForm | ||||
// | // | ||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); | |||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); | |||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; | ||||
this.ClientSize = new System.Drawing.Size(384, 174); | |||||
this.ClientSize = new System.Drawing.Size(384, 161); | |||||
this.Controls.Add(this.tableLayoutPanel1); | this.Controls.Add(this.tableLayoutPanel1); | ||||
this.Menu = this.MainMenu; | this.Menu = this.MainMenu; | ||||
this.MinimumSize = new System.Drawing.Size(400, 213); | |||||
this.MinimumSize = new System.Drawing.Size(400, 200); | |||||
this.Name = "LogForm"; | this.Name = "LogForm"; | ||||
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; | ||||
this.Text = "Log Viewer"; | this.Text = "Log Viewer"; | ||||
@@ -233,6 +300,12 @@ | |||||
this.tableLayoutPanel1.PerformLayout(); | this.tableLayoutPanel1.PerformLayout(); | ||||
this.ToolbarFlowLayoutPanel.ResumeLayout(false); | this.ToolbarFlowLayoutPanel.ResumeLayout(false); | ||||
this.ToolbarFlowLayoutPanel.PerformLayout(); | this.ToolbarFlowLayoutPanel.PerformLayout(); | ||||
this.splitContainer1.Panel1.ResumeLayout(false); | |||||
this.splitContainer1.Panel1.PerformLayout(); | |||||
this.splitContainer1.Panel2.ResumeLayout(false); | |||||
((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); | |||||
this.splitContainer1.ResumeLayout(false); | |||||
((System.ComponentModel.ISupportInitialize)(this.trafficChart)).EndInit(); | |||||
this.ResumeLayout(false); | this.ResumeLayout(false); | ||||
} | } | ||||
@@ -257,5 +330,7 @@ | |||||
private System.Windows.Forms.FlowLayoutPanel ToolbarFlowLayoutPanel; | private System.Windows.Forms.FlowLayoutPanel ToolbarFlowLayoutPanel; | ||||
private System.Windows.Forms.MenuItem MenuItemSeparater; | private System.Windows.Forms.MenuItem MenuItemSeparater; | ||||
private System.Windows.Forms.MenuItem ShowToolbarMenuItem; | private System.Windows.Forms.MenuItem ShowToolbarMenuItem; | ||||
private System.Windows.Forms.SplitContainer splitContainer1; | |||||
private System.Windows.Forms.DataVisualization.Charting.Chart trafficChart; | |||||
} | } | ||||
} | } |
@@ -2,6 +2,9 @@ | |||||
using System.Drawing; | using System.Drawing; | ||||
using System.IO; | using System.IO; | ||||
using System.Windows.Forms; | using System.Windows.Forms; | ||||
using System.Windows.Forms.DataVisualization.Charting; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using Shadowsocks.Controller; | using Shadowsocks.Controller; | ||||
using Shadowsocks.Properties; | using Shadowsocks.Properties; | ||||
@@ -18,6 +21,15 @@ namespace Shadowsocks.View | |||||
const int BACK_OFFSET = 65536; | const int BACK_OFFSET = 65536; | ||||
ShadowsocksController controller; | ShadowsocksController controller; | ||||
#region chart | |||||
List<float> inboundPoints = new List<float>(); | |||||
List<float> outboundPoints = new List<float>(); | |||||
long maxSpeed = 0; | |||||
Tuple<float, string, long> bandwidthScale = new Tuple<float, string, long>(0, "B", 1); | |||||
TextAnnotation inboundAnnotation = new TextAnnotation(); | |||||
TextAnnotation outboundAnnotation = new TextAnnotation(); | |||||
#endregion | |||||
public LogForm(ShadowsocksController controller, string filename) | public LogForm(ShadowsocksController controller, string filename) | ||||
{ | { | ||||
this.controller = controller; | this.controller = controller; | ||||
@@ -30,7 +42,8 @@ namespace Shadowsocks.View | |||||
{ | { | ||||
config = new LogViewerConfig(); | config = new LogViewerConfig(); | ||||
} | } | ||||
else { | |||||
else | |||||
{ | |||||
topMostTrigger = config.topMost; | topMostTrigger = config.topMost; | ||||
wrapTextTrigger = config.wrapText; | wrapTextTrigger = config.wrapText; | ||||
toolbarTrigger = config.toolbarShown; | toolbarTrigger = config.toolbarShown; | ||||
@@ -39,9 +52,54 @@ namespace Shadowsocks.View | |||||
LogMessageTextBox.Font = config.GetFont(); | LogMessageTextBox.Font = config.GetFont(); | ||||
} | } | ||||
controller.TrafficChanged += controller_TrafficChanged; | |||||
UpdateTexts(); | UpdateTexts(); | ||||
} | } | ||||
private void controller_TrafficChanged(object sender, EventArgs e) | |||||
{ | |||||
inboundPoints.Clear(); | |||||
outboundPoints.Clear(); | |||||
foreach (var trafficPerSecond in controller.traffic) | |||||
{ | |||||
inboundPoints.Add(trafficPerSecond.inboundIncreasement); | |||||
outboundPoints.Add(trafficPerSecond.outboundIncreasement); | |||||
maxSpeed = Math.Max(maxSpeed, Math.Max(trafficPerSecond.inboundIncreasement, trafficPerSecond.outboundIncreasement)); | |||||
} | |||||
bandwidthScale = Utils.GetBandwidthScale(maxSpeed); | |||||
//rescale the original data points, since it is List<float>, .ForEach does not work | |||||
inboundPoints = inboundPoints.Select(p => p / bandwidthScale.Item3).ToList(); | |||||
outboundPoints = outboundPoints.Select(p => p / bandwidthScale.Item3).ToList(); | |||||
try | |||||
{ | |||||
if (trafficChart.InvokeRequired) | |||||
{ | |||||
trafficChart.Invoke(new Action(() => | |||||
{ | |||||
trafficChart.Series["Inbound"].Points.DataBindY(inboundPoints); | |||||
trafficChart.Series["Outbound"].Points.DataBindY(outboundPoints); | |||||
trafficChart.ChartAreas[0].AxisY.LabelStyle.Format = "{0:0.##} " + bandwidthScale.Item2; | |||||
inboundAnnotation.AnchorDataPoint = trafficChart.Series["Inbound"].Points.Last(); | |||||
inboundAnnotation.Text = Utils.FormatBandwidth(controller.traffic.Last.inboundIncreasement); | |||||
outboundAnnotation.AnchorDataPoint = trafficChart.Series["Outbound"].Points.Last(); | |||||
outboundAnnotation.Text = Utils.FormatBandwidth(controller.traffic.Last.outboundIncreasement); | |||||
trafficChart.Annotations.Clear(); | |||||
trafficChart.Annotations.Add(inboundAnnotation); | |||||
trafficChart.Annotations.Add(outboundAnnotation); | |||||
})); | |||||
} | |||||
} | |||||
catch (ObjectDisposedException ex) | |||||
{ | |||||
// suppress the thread race error: | |||||
// when closing the form but the Invoked Action is still working and cause 'Chart is Disposed' exception | |||||
} | |||||
} | |||||
private void UpdateTexts() | private void UpdateTexts() | ||||
{ | { | ||||
FileMenuItem.Text = I18N.GetString("&File"); | FileMenuItem.Text = I18N.GetString("&File"); | ||||
@@ -58,6 +116,9 @@ namespace Shadowsocks.View | |||||
TopMostMenuItem.Text = I18N.GetString("&Top Most"); | TopMostMenuItem.Text = I18N.GetString("&Top Most"); | ||||
ShowToolbarMenuItem.Text = I18N.GetString("&Show Toolbar"); | ShowToolbarMenuItem.Text = I18N.GetString("&Show Toolbar"); | ||||
Text = I18N.GetString("Log Viewer"); | Text = I18N.GetString("Log Viewer"); | ||||
// traffic chart | |||||
trafficChart.Series["Inbound"].LegendText = I18N.GetString("Inbound"); | |||||
trafficChart.Series["Outbound"].LegendText = I18N.GetString("Outbound"); | |||||
} | } | ||||
private void Timer_Tick(object sender, EventArgs e) | private void Timer_Tick(object sender, EventArgs e) | ||||
@@ -144,6 +205,7 @@ namespace Shadowsocks.View | |||||
private void LogForm_FormClosing(object sender, FormClosingEventArgs e) | private void LogForm_FormClosing(object sender, FormClosingEventArgs e) | ||||
{ | { | ||||
timer.Stop(); | timer.Stop(); | ||||
controller.TrafficChanged -= controller_TrafficChanged; | |||||
LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | LogViewerConfig config = controller.GetConfigurationCopy().logViewer; | ||||
if (config == null) | if (config == null) | ||||
config = new LogViewerConfig(); | config = new LogViewerConfig(); | ||||
@@ -144,4 +144,7 @@ | |||||
<metadata name="$this.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | <metadata name="$this.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | ||||
<value>True</value> | <value>True</value> | ||||
</metadata> | </metadata> | ||||
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> | |||||
<value>39</value> | |||||
</metadata> | |||||
</root> | </root> |
@@ -2,6 +2,7 @@ | |||||
using System.Collections.Generic; | using System.Collections.Generic; | ||||
using System.Diagnostics; | using System.Diagnostics; | ||||
using System.Drawing; | using System.Drawing; | ||||
using System.Drawing.Imaging; | |||||
using System.Windows.Forms; | using System.Windows.Forms; | ||||
using ZXing; | using ZXing; | ||||
@@ -25,6 +26,8 @@ namespace Shadowsocks.View | |||||
private UpdateChecker updateChecker; | private UpdateChecker updateChecker; | ||||
private NotifyIcon _notifyIcon; | private NotifyIcon _notifyIcon; | ||||
private Bitmap icon_baseBitmap; | |||||
private Icon icon_base, icon_in, icon_out, icon_both, targetIcon; | |||||
private ContextMenu contextMenu1; | private ContextMenu contextMenu1; | ||||
private bool _isFirstRun; | private bool _isFirstRun; | ||||
@@ -46,6 +49,7 @@ namespace Shadowsocks.View | |||||
private MenuItem editOnlinePACItem; | private MenuItem editOnlinePACItem; | ||||
private MenuItem autoCheckUpdatesToggleItem; | private MenuItem autoCheckUpdatesToggleItem; | ||||
private MenuItem proxyItem; | private MenuItem proxyItem; | ||||
private MenuItem VerboseLoggingToggleItem; | |||||
private ConfigForm configForm; | private ConfigForm configForm; | ||||
private ProxyForm proxyForm; | private ProxyForm proxyForm; | ||||
private List<LogForm> logForms = new List<LogForm>(); | private List<LogForm> logForms = new List<LogForm>(); | ||||
@@ -63,6 +67,7 @@ namespace Shadowsocks.View | |||||
controller.PACFileReadyToOpen += controller_FileReadyToOpen; | controller.PACFileReadyToOpen += controller_FileReadyToOpen; | ||||
controller.UserRuleFileReadyToOpen += controller_FileReadyToOpen; | controller.UserRuleFileReadyToOpen += controller_FileReadyToOpen; | ||||
controller.ShareOverLANStatusChanged += controller_ShareOverLANStatusChanged; | controller.ShareOverLANStatusChanged += controller_ShareOverLANStatusChanged; | ||||
controller.VerboseLoggingStatusChanged += controller_VerboseLoggingStatusChanged; | |||||
controller.EnableGlobalChanged += controller_EnableGlobalChanged; | controller.EnableGlobalChanged += controller_EnableGlobalChanged; | ||||
controller.Errored += controller_Errored; | controller.Errored += controller_Errored; | ||||
controller.UpdatePACFromGFWListCompleted += controller_UpdatePACFromGFWListCompleted; | controller.UpdatePACFromGFWListCompleted += controller_UpdatePACFromGFWListCompleted; | ||||
@@ -76,6 +81,7 @@ namespace Shadowsocks.View | |||||
_notifyIcon.MouseClick += notifyIcon1_Click; | _notifyIcon.MouseClick += notifyIcon1_Click; | ||||
_notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick; | _notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick; | ||||
_notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed; | _notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed; | ||||
controller.TrafficChanged += controller_TrafficChanged; | |||||
this.updateChecker = new UpdateChecker(); | this.updateChecker = new UpdateChecker(); | ||||
updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted; | updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted; | ||||
@@ -96,6 +102,32 @@ namespace Shadowsocks.View | |||||
} | } | ||||
} | } | ||||
private void controller_TrafficChanged(object sender, EventArgs e) | |||||
{ | |||||
if (icon_baseBitmap == null) | |||||
return; | |||||
Icon newIcon; | |||||
bool hasInbound = controller.traffic.Last.inboundIncreasement > 0; | |||||
bool hasOutbound = controller.traffic.Last.outboundIncreasement > 0; | |||||
if (hasInbound && hasOutbound) | |||||
newIcon = icon_both; | |||||
else if (hasInbound) | |||||
newIcon = icon_in; | |||||
else if (hasOutbound) | |||||
newIcon = icon_out; | |||||
else | |||||
newIcon = icon_base; | |||||
if (newIcon != this.targetIcon) | |||||
{ | |||||
this.targetIcon = newIcon; | |||||
_notifyIcon.Icon = newIcon; | |||||
} | |||||
} | |||||
void controller_Errored(object sender, System.IO.ErrorEventArgs e) | void controller_Errored(object sender, System.IO.ErrorEventArgs e) | ||||
{ | { | ||||
MessageBox.Show(e.GetException().ToString(), String.Format(I18N.GetString("Shadowsocks Error: {0}"), e.GetException().Message)); | MessageBox.Show(e.GetException().ToString(), String.Format(I18N.GetString("Shadowsocks Error: {0}"), e.GetException().Message)); | ||||
@@ -107,26 +139,32 @@ namespace Shadowsocks.View | |||||
Graphics graphics = Graphics.FromHwnd(IntPtr.Zero); | Graphics graphics = Graphics.FromHwnd(IntPtr.Zero); | ||||
dpi = (int)graphics.DpiX; | dpi = (int)graphics.DpiX; | ||||
graphics.Dispose(); | graphics.Dispose(); | ||||
Bitmap icon = null; | |||||
icon_baseBitmap = null; | |||||
if (dpi < 97) | if (dpi < 97) | ||||
{ | { | ||||
// dpi = 96; | // dpi = 96; | ||||
icon = Resources.ss16; | |||||
icon_baseBitmap = Resources.ss16; | |||||
} | } | ||||
else if (dpi < 121) | else if (dpi < 121) | ||||
{ | { | ||||
// dpi = 120; | // dpi = 120; | ||||
icon = Resources.ss20; | |||||
icon_baseBitmap = Resources.ss20; | |||||
} | } | ||||
else | else | ||||
{ | { | ||||
icon = Resources.ss24; | |||||
icon_baseBitmap = Resources.ss24; | |||||
} | } | ||||
Configuration config = controller.GetConfigurationCopy(); | Configuration config = controller.GetConfigurationCopy(); | ||||
bool enabled = config.enabled; | bool enabled = config.enabled; | ||||
bool global = config.global; | bool global = config.global; | ||||
icon = getTrayIconByState(icon, enabled, global); | |||||
_notifyIcon.Icon = Icon.FromHandle(icon.GetHicon()); | |||||
icon_baseBitmap = getTrayIconByState(icon_baseBitmap, enabled, global); | |||||
icon_base = Icon.FromHandle(icon_baseBitmap.GetHicon()); | |||||
targetIcon = icon_base; | |||||
icon_in = Icon.FromHandle(AddBitmapOverlay(icon_baseBitmap, Resources.ssIn24).GetHicon()); | |||||
icon_out = Icon.FromHandle(AddBitmapOverlay(icon_baseBitmap, Resources.ssOut24).GetHicon()); | |||||
icon_both = Icon.FromHandle(AddBitmapOverlay(icon_baseBitmap, Resources.ssIn24, Resources.ssOut24).GetHicon()); | |||||
_notifyIcon.Icon = targetIcon; | |||||
string serverInfo = null; | string serverInfo = null; | ||||
if (controller.GetCurrentStrategy() != null) | if (controller.GetCurrentStrategy() != null) | ||||
@@ -179,6 +217,19 @@ namespace Shadowsocks.View | |||||
return iconCopy; | return iconCopy; | ||||
} | } | ||||
private Bitmap AddBitmapOverlay(Bitmap original, params Bitmap[] overlays) | |||||
{ | |||||
Bitmap bitmap = new Bitmap(original.Width, original.Height, PixelFormat.Format64bppArgb); | |||||
Graphics canvas = Graphics.FromImage(bitmap); | |||||
canvas.DrawImage(original, new Point(0, 0)); | |||||
foreach (Bitmap overlay in overlays) | |||||
{ | |||||
canvas.DrawImage(new Bitmap(overlay, original.Size), new Point(0, 0)); | |||||
} | |||||
canvas.Save(); | |||||
return bitmap; | |||||
} | |||||
private MenuItem CreateMenuItem(string text, EventHandler click) | private MenuItem CreateMenuItem(string text, EventHandler click) | ||||
{ | { | ||||
return new MenuItem(I18N.GetString(text), click); | return new MenuItem(I18N.GetString(text), click); | ||||
@@ -219,6 +270,7 @@ namespace Shadowsocks.View | |||||
this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)), | this.ShareOverLANItem = CreateMenuItem("Allow Clients from LAN", new EventHandler(this.ShareOverLANItem_Click)), | ||||
new MenuItem("-"), | new MenuItem("-"), | ||||
CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), | CreateMenuItem("Show Logs...", new EventHandler(this.ShowLogItem_Click)), | ||||
this.VerboseLoggingToggleItem = CreateMenuItem( "Verbose Logging", new EventHandler(this.VerboseLoggingToggleItem_Click) ), | |||||
CreateMenuGroup("Updates...", new MenuItem[] { | CreateMenuGroup("Updates...", new MenuItem[] { | ||||
CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)), | CreateMenuItem("Check for Updates...", new EventHandler(this.checkUpdatesItem_Click)), | ||||
new MenuItem("-"), | new MenuItem("-"), | ||||
@@ -247,6 +299,10 @@ namespace Shadowsocks.View | |||||
ShareOverLANItem.Checked = controller.GetConfigurationCopy().shareOverLan; | ShareOverLANItem.Checked = controller.GetConfigurationCopy().shareOverLan; | ||||
} | } | ||||
void controller_VerboseLoggingStatusChanged(object sender, EventArgs e) { | |||||
VerboseLoggingToggleItem.Checked = controller.GetConfigurationCopy().isVerboseLogging; | |||||
} | |||||
void controller_EnableGlobalChanged(object sender, EventArgs e) | void controller_EnableGlobalChanged(object sender, EventArgs e) | ||||
{ | { | ||||
globalModeItem.Checked = controller.GetConfigurationCopy().global; | globalModeItem.Checked = controller.GetConfigurationCopy().global; | ||||
@@ -323,6 +379,7 @@ namespace Shadowsocks.View | |||||
globalModeItem.Checked = config.global; | globalModeItem.Checked = config.global; | ||||
PACModeItem.Checked = !config.global; | PACModeItem.Checked = !config.global; | ||||
ShareOverLANItem.Checked = config.shareOverLan; | ShareOverLANItem.Checked = config.shareOverLan; | ||||
VerboseLoggingToggleItem.Checked = config.isVerboseLogging; | |||||
AutoStartupItem.Checked = AutoStartup.Check(); | AutoStartupItem.Checked = AutoStartup.Check(); | ||||
onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | onlinePACItem.Checked = onlinePACItem.Enabled && config.useOnlinePac; | ||||
localPACItem.Checked = !onlinePACItem.Checked; | localPACItem.Checked = !onlinePACItem.Checked; | ||||
@@ -550,6 +607,11 @@ namespace Shadowsocks.View | |||||
logForms.Add(f); | logForms.Add(f); | ||||
} | } | ||||
private void VerboseLoggingToggleItem_Click( object sender, EventArgs e ) { | |||||
VerboseLoggingToggleItem.Checked = ! VerboseLoggingToggleItem.Checked; | |||||
controller.ToggleVerboseLogging( VerboseLoggingToggleItem.Checked ); | |||||
} | |||||
private void StatisticsConfigItem_Click(object sender, EventArgs e) | private void StatisticsConfigItem_Click(object sender, EventArgs e) | ||||
{ | { | ||||
StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller); | StatisticsStrategyConfigurationForm form = new StatisticsStrategyConfigurationForm(controller); | ||||
@@ -3,11 +3,6 @@ | |||||
<package id="Caseless.Fody" version="1.4.1" targetFramework="net40-client" developmentDependency="true" /> | <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="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="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="Newtonsoft.Json" version="8.0.2" targetFramework="net40-client" /> | ||||
<package id="StringEx.CS" version="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> | </packages> |
@@ -66,18 +66,6 @@ | |||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
<Reference Include="Microsoft.CSharp" /> | <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="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> | <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> | <HintPath>3rd\Newtonsoft.Json.8.0.2\lib\net40\Newtonsoft.Json.dll</HintPath> | ||||
@@ -88,27 +76,7 @@ | |||||
<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.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" /> | ||||
<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" /> | ||||
<Reference Include="System.Windows.Forms.DataVisualization" /> | <Reference Include="System.Windows.Forms.DataVisualization" /> | ||||
<Reference Include="System.Xaml" /> | <Reference Include="System.Xaml" /> | ||||
@@ -303,6 +271,8 @@ | |||||
<Content Include="FodyWeavers.xml"> | <Content Include="FodyWeavers.xml"> | ||||
<SubType>Designer</SubType> | <SubType>Designer</SubType> | ||||
</Content> | </Content> | ||||
<Content Include="Resources\ssIn24.png" /> | |||||
<Content Include="Resources\ssOut24.png" /> | |||||
<Content Include="shadowsocks.ico" /> | <Content Include="shadowsocks.ico" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | <ItemGroup> | ||||
@@ -342,10 +312,8 @@ | |||||
<PropertyGroup> | <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> | <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> | </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'))" /> | <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> | </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')" /> | <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"> | <UsingTask TaskName="CosturaCleanup" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" TaskFactory="CodeTaskFactory"> | ||||
<ParameterGroup> | <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 /> | <StartupObject /> | ||||
</PropertyGroup> | </PropertyGroup> | ||||
<ItemGroup> | <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" /> | ||||
<Reference Include="System.Net" /> | <Reference Include="System.Net" /> | ||||
<Reference Include="System.Net.Http" /> | <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" /> | <Reference Include="System.Net.Http.WebRequest" /> | ||||
</ItemGroup> | </ItemGroup> | ||||
<Choose> | <Choose> | ||||
@@ -82,9 +62,6 @@ | |||||
<Name>shadowsocks-csharp</Name> | <Name>shadowsocks-csharp</Name> | ||||
</ProjectReference> | </ProjectReference> | ||||
</ItemGroup> | </ItemGroup> | ||||
<ItemGroup> | |||||
<None Include="packages.config" /> | |||||
</ItemGroup> | |||||
<Choose> | <Choose> | ||||
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'"> | <When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'"> | ||||
<ItemGroup> | <ItemGroup> | ||||
@@ -105,13 +82,6 @@ | |||||
</Choose> | </Choose> | ||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> | <Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" /> | ||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.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. | <!-- 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"> | ||||