From 4b3ec386911cf7ec1a7be941688cf640a626961f Mon Sep 17 00:00:00 2001 From: kimw Date: Sun, 15 May 2016 01:34:28 -0400 Subject: [PATCH 01/11] featured print "connect to hostname:port" to log --- shadowsocks-csharp/Controller/Service/TCPRelay.cs | 37 +++++++++++++++-------- shadowsocks-csharp/Util/Util.cs | 6 ++-- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 9c162780..808f1f6f 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net; using System.Net.Sockets; using System.Timers; @@ -15,10 +16,7 @@ namespace Shadowsocks.Controller private ShadowsocksController _controller; private DateTime _lastSweepTime; - public ISet Handlers - { - get; set; - } + public ISet Handlers { get; set; } public TCPRelay(ShadowsocksController controller) { @@ -389,10 +387,7 @@ namespace Shadowsocks.Controller private class ServerTimer : Timer { public Server Server; - - public ServerTimer(int p) : base(p) - { - } + public ServerTimer(int p) : base(p) { } } private void StartConnect() @@ -484,8 +479,6 @@ namespace Shadowsocks.Controller connected = true; - Logging.Debug($"Socket connected to {remote.RemoteEndPoint}"); - var latency = DateTime.Now - _startConnectTime; IStrategy strategy = controller.GetCurrentStrategy(); strategy?.UpdateLatency(server, latency); @@ -554,7 +547,6 @@ namespace Shadowsocks.Controller } encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend); } - Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeRemoteReceiveCallback() (download)"); connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null); IStrategy strategy = controller.GetCurrentStrategy(); @@ -597,6 +589,28 @@ namespace Shadowsocks.Controller 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]; + Logging.Debug($"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]; + Logging.Debug($"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]; + Logging.Debug($"connect to [{dst_addr}]:{dst_port}"); + break; + } int bytesToSend; lock (encryptionLock) { @@ -606,7 +620,6 @@ namespace Shadowsocks.Controller } encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); } - Logging.Debug(remote, bytesToSend, "TCP Relay", "@PipeConnectionReceiveCallback() (upload)"); tcprelay.UpdateOutboundCounter(server, bytesToSend); _startSendingTime = DateTime.Now; _bytesToSend = bytesToSend; diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs index 178c994b..3d09db7a 100755 --- a/shadowsocks-csharp/Util/Util.cs +++ b/shadowsocks-csharp/Util/Util.cs @@ -76,7 +76,8 @@ namespace Shadowsocks.Util // // just kidding 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 (GZipStream input = new GZipStream(new MemoryStream(buf), - CompressionMode.Decompress, false)) + CompressionMode.Decompress, + false)) { while ((n = input.Read(buffer, 0, buffer.Length)) > 0) { From 0cee110e5916979428a61b14e246340f66070722 Mon Sep 17 00:00:00 2001 From: kimw Date: Sun, 15 May 2016 01:44:03 -0400 Subject: [PATCH 02/11] renamed private properties with prefix underline --- shadowsocks-csharp/Controller/Service/TCPRelay.cs | 170 ++++++++++------------ 1 file changed, 80 insertions(+), 90 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 808f1f6f..888a2dd5 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -39,7 +39,7 @@ namespace Shadowsocks.Controller TCPHandler handler = new TCPHandler(this); handler.connection = socket; handler.controller = _controller; - handler.relay = this; + handler.tcprelay = this; handler.Start(firstPacket, length); IList handlersToClose = new List(); @@ -85,6 +85,11 @@ namespace Shadowsocks.Controller class TCPHandler { + // Size of receive buffer. + static public readonly int RecvSize = 8192; + static public readonly int RecvReserveSize = IVEncryptor.ONETIMEAUTH_BYTES + IVEncryptor.AUTH_BYTES; // reserve for one-time auth + static public readonly int BufferSize = RecvSize + RecvReserveSize + 32; + // public Encryptor encryptor; public IEncryptor encryptor; public Server server; @@ -92,51 +97,36 @@ namespace Shadowsocks.Controller public Socket remote; public Socket connection; public ShadowsocksController controller; - public TCPRelay relay; + public TCPRelay tcprelay; public DateTime lastActivity; - private const int maxRetry = 4; - private int retryCount = 0; - private bool connected; - - private byte command; + private const int _maxRetry = 4; + private int _retryCount = 0; + private bool _connected; + private byte _command; private byte[] _firstPacket; 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 _startReceivingTime; private DateTime _startSendingTime; private int _bytesToSend; - private TCPRelay tcprelay; // TODO: tcprelay ?= relay + private TCPRelay _tcprelay; // TODO: is _tcprelay equals tcprelay declared above? public TCPHandler(TCPRelay tcprelay) { - this.tcprelay = tcprelay; + this._tcprelay = tcprelay; } public void CreateRemote() @@ -160,7 +150,7 @@ namespace Shadowsocks.Controller private void CheckClose() { - if (connectionShutdown && remoteShutdown) + if (_connectionShutdown && _remoteShutdown) { Close(); } @@ -168,17 +158,17 @@ namespace Shadowsocks.Controller public void Close() { - lock (relay.Handlers) + lock (tcprelay.Handlers) { - relay.Handlers.Remove(this); + tcprelay.Handlers.Remove(this); } lock (this) { - if (closed) + if (_closed) { return; } - closed = true; + _closed = true; } if (connection != null) { @@ -204,9 +194,9 @@ namespace Shadowsocks.Controller Logging.LogUsefulException(e); } } - lock (encryptionLock) + lock (_encryptionLock) { - lock (decryptionLock) + lock (_decryptionLock) { if (encryptor != null) { @@ -218,7 +208,7 @@ namespace Shadowsocks.Controller private void HandshakeReceive() { - if (closed) + if (_closed) { return; } @@ -251,7 +241,7 @@ namespace Shadowsocks.Controller private void HandshakeSendCallback(IAsyncResult ar) { - if (closed) + if (_closed) { return; } @@ -266,7 +256,7 @@ namespace Shadowsocks.Controller // +-----+-----+-------+------+----------+----------+ // Skip first 3 bytes // TODO validate - connection.BeginReceive(connetionRecvBuffer, 0, 3, 0, new AsyncCallback(handshakeReceive2Callback), null); + connection.BeginReceive(_connetionRecvBuffer, 0, 3, 0, new AsyncCallback(handshakeReceive2Callback), null); } catch (Exception e) { @@ -277,7 +267,7 @@ namespace Shadowsocks.Controller private void handshakeReceive2Callback(IAsyncResult ar) { - if (closed) + if (_closed) { return; } @@ -287,13 +277,13 @@ namespace Shadowsocks.Controller 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 }; connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ResponseCallback), null); } - else if (command == 3) + else if (_command == 3) { HandleUDPAssociate(); } @@ -334,7 +324,7 @@ namespace Shadowsocks.Controller private void ReadAll(IAsyncResult ar) { - if (closed) + if (_closed) { return; } @@ -344,7 +334,7 @@ namespace Shadowsocks.Controller { connection.EndSend(ar); Logging.Debug(remote, RecvSize, "TCP Relay"); - connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); + connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); } else { @@ -352,7 +342,7 @@ namespace Shadowsocks.Controller if (bytesRead > 0) { Logging.Debug(remote, RecvSize, "TCP Relay"); - connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); + connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); } else { @@ -417,7 +407,7 @@ namespace Shadowsocks.Controller connectTimer.Enabled = true; connectTimer.Server = server; - connected = false; + _connected = false; // Connect to the remote endpoint. remote.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), connectTimer); } @@ -430,7 +420,7 @@ namespace Shadowsocks.Controller private void connectTimer_Elapsed(object sender, ElapsedEventArgs e) { - if (connected) + if (_connected) { return; } @@ -447,11 +437,11 @@ namespace Shadowsocks.Controller private void RetryConnect() { - if (retryCount < maxRetry) + if (_retryCount < _maxRetry) { - Logging.Debug($"Connection failed, retry ({retryCount})"); + Logging.Debug($"Connection failed, retry ({_retryCount})"); StartConnect(); - retryCount++; + _retryCount++; } else { @@ -462,7 +452,7 @@ namespace Shadowsocks.Controller private void ConnectCallback(IAsyncResult ar) { Server server = null; - if (closed) + if (_closed) { return; } @@ -477,12 +467,12 @@ namespace Shadowsocks.Controller // Complete the connection. remote.EndConnect(ar); - connected = true; + _connected = true; var latency = DateTime.Now - _startConnectTime; IStrategy strategy = controller.GetCurrentStrategy(); strategy?.UpdateLatency(server, latency); - tcprelay.UpdateLatency(server, latency); + _tcprelay.UpdateLatency(server, latency); StartPipe(); } @@ -506,15 +496,15 @@ namespace Shadowsocks.Controller private void StartPipe() { - if (closed) + if (_closed) { return; } try { _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, 0, new AsyncCallback(PipeRemoteReceiveCallback), null); + connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null); } catch (Exception e) { @@ -525,29 +515,29 @@ namespace Shadowsocks.Controller private void PipeRemoteReceiveCallback(IAsyncResult ar) { - if (closed) + if (_closed) { return; } try { int bytesRead = remote.EndReceive(ar); - totalRead += bytesRead; - tcprelay.UpdateInboundCounter(server, bytesRead); + _totalRead += bytesRead; + _tcprelay.UpdateInboundCounter(server, bytesRead); if (bytesRead > 0) { lastActivity = DateTime.Now; int bytesToSend; - lock (decryptionLock) + lock (_decryptionLock) { - if (closed) + if (_closed) { return; } - encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend); + encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); } - connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null); + connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null); IStrategy strategy = controller.GetCurrentStrategy(); if (strategy != null) @@ -558,7 +548,7 @@ namespace Shadowsocks.Controller else { connection.Shutdown(SocketShutdown.Send); - connectionShutdown = true; + _connectionShutdown = true; CheckClose(); //if (totalRead == 0) @@ -578,52 +568,52 @@ namespace Shadowsocks.Controller private void PipeConnectionReceiveCallback(IAsyncResult ar) { - if (closed) + if (_closed) { return; } try { int bytesRead = connection.EndReceive(ar); - totalWrite += bytesRead; + _totalWrite += bytesRead; if (bytesRead > 0) { - int atyp = connetionRecvBuffer[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]; + dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(4).ToArray()).ToString(); + dst_port = (_connetionRecvBuffer[5] << 8) + _connetionRecvBuffer[6]; Logging.Debug($"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]; + int len = _connetionRecvBuffer[1]; + dst_addr = System.Text.Encoding.UTF8.GetString(_connetionRecvBuffer, 2, len); + dst_port = (_connetionRecvBuffer[len + 2] << 8) + _connetionRecvBuffer[len + 3]; Logging.Debug($"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]; + dst_addr = new IPAddress(_connetionRecvBuffer.Skip(1).Take(16).ToArray()).ToString(); + dst_port = (_connetionRecvBuffer[17] << 8) + _connetionRecvBuffer[18]; Logging.Debug($"connect to [{dst_addr}]:{dst_port}"); break; } int bytesToSend; - lock (encryptionLock) + lock (_encryptionLock) { - if (closed) + if (_closed) { return; } - encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend); + encryptor.Encrypt(_connetionRecvBuffer, bytesRead, _connetionSendBuffer, out bytesToSend); } - tcprelay.UpdateOutboundCounter(server, bytesToSend); + _tcprelay.UpdateOutboundCounter(server, bytesToSend); _startSendingTime = DateTime.Now; _bytesToSend = bytesToSend; - remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); + remote.BeginSend(_connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null); IStrategy strategy = controller.GetCurrentStrategy(); strategy?.UpdateLastWrite(server); @@ -631,7 +621,7 @@ namespace Shadowsocks.Controller else { remote.Shutdown(SocketShutdown.Send); - remoteShutdown = true; + _remoteShutdown = true; CheckClose(); } } @@ -644,14 +634,14 @@ namespace Shadowsocks.Controller private void PipeRemoteSendCallback(IAsyncResult ar) { - if (closed) + if (_closed) { return; } try { remote.EndSend(ar); - connection.BeginReceive(connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null); + connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeConnectionReceiveCallback), null); } catch (Exception e) { @@ -662,14 +652,14 @@ namespace Shadowsocks.Controller private void PipeConnectionSendCallback(IAsyncResult ar) { - if (closed) + if (_closed) { return; } try { connection.EndSend(ar); - remote.BeginReceive(remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null); + remote.BeginReceive(_remoteRecvBuffer, 0, RecvSize, 0, new AsyncCallback(PipeRemoteReceiveCallback), null); } catch (Exception e) { From ff04a2a18af99a936854581c7262c84fcbe3a843 Mon Sep 17 00:00:00 2001 From: kimw Date: Sun, 15 May 2016 02:04:04 -0400 Subject: [PATCH 03/11] clean up code in C# 6 --- shadowsocks-csharp/Controller/Service/TCPRelay.cs | 222 +++++++--------------- 1 file changed, 67 insertions(+), 155 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 888a2dd5..0e0c78fd 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -27,44 +27,38 @@ namespace Shadowsocks.Controller 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; - } - if (length < 2 || firstPacket[0] != 5) + else { - return false; - } - socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); - TCPHandler handler = new TCPHandler(this); - handler.connection = socket; - handler.controller = _controller; - handler.tcprelay = this; + socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); + TCPHandler handler = new TCPHandler(this); + handler.connection = socket; + handler.controller = _controller; + handler.tcprelay = this; - handler.Start(firstPacket, length); - IList handlersToClose = new List(); - lock (Handlers) - { - Handlers.Add(handler); - DateTime now = DateTime.Now; - if (now - _lastSweepTime > TimeSpan.FromSeconds(1)) + handler.Start(firstPacket, length); + IList handlersToClose = new List(); + lock (Handlers) { - _lastSweepTime = now; - foreach (TCPHandler handler1 in Handlers) + Handlers.Add(handler); + DateTime now = DateTime.Now; + if (now - _lastSweepTime > TimeSpan.FromSeconds(1)) { - if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) - { - handlersToClose.Add(handler1); - } + _lastSweepTime = now; + foreach (TCPHandler handler1 in Handlers) + if (now - handler1.lastActivity > TimeSpan.FromSeconds(900)) + handlersToClose.Add(handler1); } } + foreach (TCPHandler handler1 in handlersToClose) + { + Logging.Debug("Closing timed out TCP connection."); + handler1.Close(); + } + return true; } - foreach (TCPHandler handler1 in handlersToClose) - { - Logging.Debug("Closing timed out TCP connection."); - handler1.Close(); - } - return true; } public void UpdateInboundCounter(Server server, long n) @@ -126,16 +120,14 @@ namespace Shadowsocks.Controller public TCPHandler(TCPRelay tcprelay) { - this._tcprelay = tcprelay; + _tcprelay = tcprelay; } public void CreateRemote() { Server server = controller.GetAServer(IStrategyCallerType.TCP, (IPEndPoint)connection.RemoteEndPoint); if (server == null || server.server == "") - { throw new ArgumentException("No server configured"); - } encryptor = EncryptorFactory.GetEncryptor(server.method, server.password, server.auth, false); this.server = server; } @@ -151,9 +143,7 @@ namespace Shadowsocks.Controller private void CheckClose() { if (_connectionShutdown && _remoteShutdown) - { Close(); - } } public void Close() @@ -165,57 +155,43 @@ namespace Shadowsocks.Controller lock (this) { if (_closed) - { return; - } - _closed = true; + else + _closed = true; } - if (connection != null) + try { - try - { - connection.Shutdown(SocketShutdown.Both); - connection.Close(); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - } + connection?.Shutdown(SocketShutdown.Both); + connection?.Close(); } - if (remote != null) + catch (Exception e) { - try - { - remote.Shutdown(SocketShutdown.Both); - remote.Close(); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - } + Logging.LogUsefulException(e); + } + try + { + remote?.Shutdown(SocketShutdown.Both); + remote?.Close(); + } + catch (Exception e) + { + Logging.LogUsefulException(e); } lock (_encryptionLock) { lock (_decryptionLock) { - if (encryptor != null) - { - ((IDisposable)encryptor).Dispose(); - } + encryptor?.Dispose(); } } } private void HandshakeReceive() { - if (_closed) - { - return; - } + if (_closed) return; try { int bytesRead = _firstPacketLength; - if (bytesRead > 1) { byte[] response = { 5, 0 }; @@ -228,9 +204,7 @@ namespace Shadowsocks.Controller connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(HandshakeSendCallback), null); } else - { Close(); - } } catch (Exception e) { @@ -241,10 +215,7 @@ namespace Shadowsocks.Controller private void HandshakeSendCallback(IAsyncResult ar) { - if (_closed) - { - return; - } + if (_closed) return; try { connection.EndSend(ar); @@ -267,14 +238,10 @@ namespace Shadowsocks.Controller private void handshakeReceive2Callback(IAsyncResult ar) { - if (_closed) - { - return; - } + if (_closed) return; try { int bytesRead = connection.EndReceive(ar); - if (bytesRead >= 3) { _command = _connetionRecvBuffer[1]; @@ -284,9 +251,7 @@ namespace Shadowsocks.Controller connection.BeginSend(response, 0, response.Length, 0, new AsyncCallback(ResponseCallback), null); } else if (_command == 3) - { HandleUDPAssociate(); - } } else { @@ -308,13 +273,14 @@ namespace Shadowsocks.Controller int port = endPoint.Port; byte[] response = new byte[4 + address.Length + 2]; response[0] = 5; - if (endPoint.AddressFamily == AddressFamily.InterNetwork) - { - response[3] = 1; - } - else if (endPoint.AddressFamily == AddressFamily.InterNetworkV6) + switch (endPoint.AddressFamily) { - response[3] = 4; + case AddressFamily.InterNetwork: + response[3] = 1; + break; + case AddressFamily.InterNetworkV6: + response[3] = 4; + break; } address.CopyTo(response, 4); response[response.Length - 1] = (byte)(port & 0xFF); @@ -324,10 +290,7 @@ namespace Shadowsocks.Controller private void ReadAll(IAsyncResult ar) { - if (_closed) - { - return; - } + if (_closed) return; try { if (ar.AsyncState != null) @@ -345,9 +308,7 @@ namespace Shadowsocks.Controller connection.BeginReceive(_connetionRecvBuffer, 0, RecvSize, 0, new AsyncCallback(ReadAll), null); } else - { Close(); - } } } catch (Exception e) @@ -362,10 +323,8 @@ namespace Shadowsocks.Controller try { connection.EndSend(ar); - StartConnect(); } - catch (Exception e) { Logging.LogUsefulException(e); @@ -396,8 +355,7 @@ namespace Shadowsocks.Controller } IPEndPoint remoteEP = new IPEndPoint(ipAddress, server.server_port); - remote = new Socket(ipAddress.AddressFamily, - SocketType.Stream, ProtocolType.Tcp); + remote = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp); remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); _startConnectTime = DateTime.Now; @@ -420,16 +378,10 @@ namespace Shadowsocks.Controller private void connectTimer_Elapsed(object sender, ElapsedEventArgs e) { - if (_connected) - { - return; - } + if (_connected) return; Server server = ((ServerTimer)sender).Server; IStrategy strategy = controller.GetCurrentStrategy(); - if (strategy != null) - { - strategy.SetFailure(server); - } + strategy?.SetFailure(server); Logging.Info($"{server.FriendlyName()} timed out"); remote.Close(); RetryConnect(); @@ -444,22 +396,16 @@ namespace Shadowsocks.Controller _retryCount++; } else - { Close(); - } } private void ConnectCallback(IAsyncResult ar) { - Server server = null; - if (_closed) - { - return; - } + if (_closed) return; try { ServerTimer timer = (ServerTimer)ar.AsyncState; - server = timer.Server; + Server server = timer.Server; timer.Elapsed -= connectTimer_Elapsed; timer.Enabled = false; timer.Dispose(); @@ -484,10 +430,7 @@ namespace Shadowsocks.Controller if (server != null) { IStrategy strategy = controller.GetCurrentStrategy(); - if (strategy != null) - { - strategy.SetFailure(server); - } + strategy?.SetFailure(server); } Logging.LogUsefulException(e); RetryConnect(); @@ -496,10 +439,7 @@ namespace Shadowsocks.Controller private void StartPipe() { - if (_closed) - { - return; - } + if (_closed) return; try { _startReceivingTime = DateTime.Now; @@ -515,10 +455,7 @@ namespace Shadowsocks.Controller private void PipeRemoteReceiveCallback(IAsyncResult ar) { - if (_closed) - { - return; - } + if (_closed) return; try { int bytesRead = remote.EndReceive(ar); @@ -531,32 +468,19 @@ namespace Shadowsocks.Controller int bytesToSend; lock (_decryptionLock) { - if (_closed) - { - return; - } + if (_closed) return; encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); } connection.BeginSend(_remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null); IStrategy strategy = controller.GetCurrentStrategy(); - if (strategy != null) - { - strategy.UpdateLastRead(server); - } + strategy?.UpdateLastRead(server); } else { connection.Shutdown(SocketShutdown.Send); _connectionShutdown = true; CheckClose(); - - //if (totalRead == 0) - //{ - // // closed before anything received, reports as failure - // // disable this feature - // controller.GetCurrentStrategy().SetFailure(this.server); - //} } } catch (Exception e) @@ -568,10 +492,7 @@ namespace Shadowsocks.Controller private void PipeConnectionReceiveCallback(IAsyncResult ar) { - if (_closed) - { - return; - } + if (_closed) return; try { int bytesRead = connection.EndReceive(ar); @@ -604,10 +525,7 @@ namespace Shadowsocks.Controller int bytesToSend; lock (_encryptionLock) { - if (_closed) - { - return; - } + if (_closed) return; encryptor.Encrypt(_connetionRecvBuffer, bytesRead, _connetionSendBuffer, out bytesToSend); } _tcprelay.UpdateOutboundCounter(server, bytesToSend); @@ -634,10 +552,7 @@ namespace Shadowsocks.Controller private void PipeRemoteSendCallback(IAsyncResult ar) { - if (_closed) - { - return; - } + if (_closed) return; try { remote.EndSend(ar); @@ -652,10 +567,7 @@ namespace Shadowsocks.Controller private void PipeConnectionSendCallback(IAsyncResult ar) { - if (_closed) - { - return; - } + if (_closed) return; try { connection.EndSend(ar); From f9296b49b12cbfe945e643c05aa2dd71869bbc44 Mon Sep 17 00:00:00 2001 From: kimw Date: Sun, 15 May 2016 02:17:05 -0400 Subject: [PATCH 04/11] clean up buggy parameter. Replaced SocketFlags.None (an enum which euqals 0) from zero. --- shadowsocks-csharp/Controller/Service/TCPRelay.cs | 29 ++++++++++------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/shadowsocks-csharp/Controller/Service/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs index 0e0c78fd..28d7ee8b 100644 --- a/shadowsocks-csharp/Controller/Service/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -37,7 +37,6 @@ namespace Shadowsocks.Controller handler.connection = socket; handler.controller = _controller; handler.tcprelay = this; - handler.Start(firstPacket, length); IList handlersToClose = new List(); lock (Handlers) @@ -201,7 +200,7 @@ namespace Shadowsocks.Controller response = new byte[] { 0, 91 }; 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 Close(); @@ -227,7 +226,7 @@ namespace Shadowsocks.Controller // +-----+-----+-------+------+----------+----------+ // Skip first 3 bytes // 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) { @@ -248,7 +247,7 @@ namespace Shadowsocks.Controller if (_command == 1) { 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) HandleUDPAssociate(); @@ -285,7 +284,7 @@ namespace Shadowsocks.Controller address.CopyTo(response, 4); response[response.Length - 1] = (byte)(port & 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) @@ -297,7 +296,7 @@ namespace Shadowsocks.Controller { connection.EndSend(ar); Logging.Debug(remote, 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 { @@ -305,7 +304,7 @@ namespace Shadowsocks.Controller if (bytesRead > 0) { Logging.Debug(remote, 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 Close(); @@ -443,8 +442,8 @@ namespace Shadowsocks.Controller try { _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) { @@ -461,7 +460,6 @@ namespace Shadowsocks.Controller int bytesRead = remote.EndReceive(ar); _totalRead += bytesRead; _tcprelay.UpdateInboundCounter(server, bytesRead); - if (bytesRead > 0) { lastActivity = DateTime.Now; @@ -471,8 +469,7 @@ namespace Shadowsocks.Controller if (_closed) return; encryptor.Decrypt(_remoteRecvBuffer, bytesRead, _remoteSendBuffer, out bytesToSend); } - 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(); strategy?.UpdateLastRead(server); } @@ -497,7 +494,6 @@ namespace Shadowsocks.Controller { int bytesRead = connection.EndReceive(ar); _totalWrite += bytesRead; - if (bytesRead > 0) { int atyp = _connetionRecvBuffer[0]; @@ -531,8 +527,7 @@ namespace Shadowsocks.Controller _tcprelay.UpdateOutboundCounter(server, bytesToSend); _startSendingTime = DateTime.Now; _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(); strategy?.UpdateLastWrite(server); } @@ -556,7 +551,7 @@ namespace Shadowsocks.Controller try { 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) { @@ -571,7 +566,7 @@ namespace Shadowsocks.Controller try { 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) { From b0f71c43f5bf11d3cb3b3e9fbbd2e71df630a6d5 Mon Sep 17 00:00:00 2001 From: Allen Zhu Date: Thu, 4 Aug 2016 17:32:56 +0800 Subject: [PATCH 05/11] Add traffic chart and traffic icon support (#595) * Implement the line chart on Log View window for inbound/outbound traffic per second * Implement dynamic tray icon to indicate Inbound and Outbound * Refine linechart * Bugfix for dynamic icon * Refine chart presentation * Change the data points' type for more accurate calculation * Tweak inbound/outbound indicator image for tray icon --- .../Controller/ShadowsocksController.cs | 60 +++++++++++- .../Properties/Resources.Designer.cs | 22 ++++- shadowsocks-csharp/Properties/Resources.resx | 6 ++ shadowsocks-csharp/Resources/ssIn24.png | Bin 0 -> 224 bytes shadowsocks-csharp/Resources/ssOut24.png | Bin 0 -> 211 bytes shadowsocks-csharp/Util/Util.cs | 22 ++++- shadowsocks-csharp/View/LogForm.Designer.cs | 103 ++++++++++++++++++--- shadowsocks-csharp/View/LogForm.cs | 53 ++++++++++- shadowsocks-csharp/View/LogForm.resx | 3 + shadowsocks-csharp/View/MenuViewController.cs | 61 ++++++++++-- shadowsocks-csharp/shadowsocks-csharp.csproj | 2 + 11 files changed, 308 insertions(+), 24 deletions(-) create mode 100644 shadowsocks-csharp/Resources/ssIn24.png create mode 100644 shadowsocks-csharp/Resources/ssOut24.png diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 643d68ab..b6f64ed5 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -23,6 +23,7 @@ namespace Shadowsocks.Controller // interacts with low level logic private Thread _ramThread; + private Thread _trafficThread; private Listener _listener; private PACServer _pacServer; @@ -35,6 +36,7 @@ namespace Shadowsocks.Controller public long inboundCounter = 0; public long outboundCounter = 0; + public QueueLast traffic; private bool stopped = false; @@ -45,10 +47,29 @@ namespace Shadowsocks.Controller public string Path; } + public class QueueLast : Queue + { + 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 EnableStatusChanged; public event EventHandler EnableGlobalChanged; public event EventHandler ShareOverLANStatusChanged; + public event EventHandler TrafficChanged; // when user clicked Edit PAC, and PAC file has already created public event EventHandler PACFileReadyToOpen; @@ -66,6 +87,7 @@ namespace Shadowsocks.Controller StatisticsConfiguration = StatisticsStrategyConfiguration.Load(); _strategyManager = new StrategyManager(this); StartReleasingMemory(); + StartTrafficStatistics(60); } public void Start() @@ -313,7 +335,7 @@ namespace Shadowsocks.Controller { if (_config.availabilityStatistics) { - new Task(() => availabilityStatistics.UpdateLatency(server, (int) latency.TotalMilliseconds)).Start(); + new Task(() => availabilityStatistics.UpdateLatency(server, (int)latency.TotalMilliseconds)).Start(); } } @@ -516,5 +538,41 @@ namespace Shadowsocks.Controller } } + private void StartTrafficStatistics(int queueMaxSize) + { + traffic = new QueueLast(); + 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); + } + } + } } diff --git a/shadowsocks-csharp/Properties/Resources.Designer.cs b/shadowsocks-csharp/Properties/Resources.Designer.cs index 1205845f..49e2151b 100644 --- a/shadowsocks-csharp/Properties/Resources.Designer.cs +++ b/shadowsocks-csharp/Properties/Resources.Designer.cs @@ -182,7 +182,27 @@ namespace Shadowsocks.Properties { return ((System.Drawing.Bitmap)(obj)); } } - + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ssIn24 { + get { + object obj = ResourceManager.GetObject("ssIn24", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap ssOut24 { + get { + object obj = ResourceManager.GetObject("ssOut24", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Looks up a localized resource of type System.Drawing.Bitmap. /// diff --git a/shadowsocks-csharp/Properties/Resources.resx b/shadowsocks-csharp/Properties/Resources.resx index aa0c2f63..12e04f8d 100755 --- a/shadowsocks-csharp/Properties/Resources.resx +++ b/shadowsocks-csharp/Properties/Resources.resx @@ -148,6 +148,12 @@ ..\Resources\ss24.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\Resources\ssIn24.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\ssOut24.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\Resources\ssw128.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/shadowsocks-csharp/Resources/ssIn24.png b/shadowsocks-csharp/Resources/ssIn24.png new file mode 100644 index 0000000000000000000000000000000000000000..8e60412c53c4bb45ac208395145ff13a01cd6c61 GIT binary patch literal 224 zcmV<603ZK}P)N2bZe?^J zG%heMIczh2P5=M^UP(kjR7gv;%)tr3Fbo7ix8nYbRPqVrF$4^o$ADNkt(o~1V~MfE zSYj+OmKaNnCB_nCiLu03Vk|M17)y*L#u8(RvBX$nJh5iq#}|xF_~{!)C;apcqZ5Am a*mD7$L!H}12;4*f0000N2bZe?^J zG%heMIczh2P5=M^QAtEWR7gwh&A|zP01O1d^k0q*Mhh4eVg30S0t-iGcR$5gVk|M1 z7)y*L#u8(RvBX$nEHRcCON=GP5@U(6#8_f1F_vbJ>)yfWjsL-eofF#{uloi-ws8Oe N002ovPDHLkV1l2>LNNdU literal 0 KcmV+b0RR6000031 diff --git a/shadowsocks-csharp/Util/Util.cs b/shadowsocks-csharp/Util/Util.cs index 178c994b..207e5289 100755 --- a/shadowsocks-csharp/Util/Util.cs +++ b/shadowsocks-csharp/Util/Util.cs @@ -100,29 +100,49 @@ namespace Shadowsocks.Util public static string FormatBandwidth(long n) { + var result = GetBandwidthScale(n); + return $"{result.Item1:0.##}{result.Item2}"; + } + + /// + /// Return scaled bandwidth + /// + /// Raw bandwidth + /// + /// Item1: float, bandwidth with suitable scale (eg. 56) + /// Item2: string, scale unit name (eg. KiB) + /// Item3: long, scale unit (eg. 1024) + /// + public static Tuple GetBandwidthScale(long n) + { + long scale = 1; float f = n; string unit = "B"; if (f > 1024) { f = f / 1024; + scale <<= 10; unit = "KiB"; } if (f > 1024) { f = f / 1024; + scale <<= 10; unit = "MiB"; } if (f > 1024) { f = f / 1024; + scale <<= 10; unit = "GiB"; } if (f > 1024) { f = f / 1024; + scale <<= 10; unit = "TiB"; } - return $"{f:0.##}{unit}"; + return new Tuple(f, unit, scale); } [DllImport("kernel32.dll")] diff --git a/shadowsocks-csharp/View/LogForm.Designer.cs b/shadowsocks-csharp/View/LogForm.Designer.cs index 3ac114ba..1add4487 100644 --- a/shadowsocks-csharp/View/LogForm.Designer.cs +++ b/shadowsocks-csharp/View/LogForm.Designer.cs @@ -29,6 +29,10 @@ private void InitializeComponent() { 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.MainMenu = new System.Windows.Forms.MainMenu(this.components); this.FileMenuItem = new System.Windows.Forms.MenuItem(); @@ -47,8 +51,15 @@ this.WrapTextCheckBox = new System.Windows.Forms.CheckBox(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); 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.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(); // // LogMessageTextBox @@ -57,13 +68,13 @@ 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.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.Multiline = true; this.LogMessageTextBox.Name = "LogMessageTextBox"; this.LogMessageTextBox.ReadOnly = true; 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; // // MainMenu @@ -144,9 +155,9 @@ this.TopMostCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); 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.Size = new System.Drawing.Size(71, 25); + this.TopMostCheckBox.Size = new System.Drawing.Size(72, 23); this.TopMostCheckBox.TabIndex = 3; this.TopMostCheckBox.Text = "&Top Most"; this.TopMostCheckBox.UseVisualStyleBackColor = true; @@ -157,7 +168,7 @@ this.ChangeFontButton.AutoSize = true; this.ChangeFontButton.Location = new System.Drawing.Point(84, 3); 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.Text = "&Font"; this.ChangeFontButton.UseVisualStyleBackColor = true; @@ -168,7 +179,7 @@ this.CleanLogsButton.AutoSize = true; this.CleanLogsButton.Location = new System.Drawing.Point(3, 3); 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.Text = "&Clean Logs"; this.CleanLogsButton.UseVisualStyleBackColor = true; @@ -181,7 +192,7 @@ this.WrapTextCheckBox.AutoSize = true; this.WrapTextCheckBox.Location = new System.Drawing.Point(165, 3); 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.Text = "&Wrap Text"; this.WrapTextCheckBox.UseVisualStyleBackColor = true; @@ -191,15 +202,15 @@ // this.tableLayoutPanel1.ColumnCount = 1; 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.splitContainer1, 0, 1); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.RowCount = 2; 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; // // ToolbarFlowLayoutPanel @@ -212,17 +223,73 @@ this.ToolbarFlowLayoutPanel.Dock = System.Windows.Forms.DockStyle.Fill; this.ToolbarFlowLayoutPanel.Location = new System.Drawing.Point(3, 3); 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; // + // 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 // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); 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.Menu = this.MainMenu; - this.MinimumSize = new System.Drawing.Size(400, 213); + this.MinimumSize = new System.Drawing.Size(400, 200); this.Name = "LogForm"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Log Viewer"; @@ -233,6 +300,12 @@ this.tableLayoutPanel1.PerformLayout(); this.ToolbarFlowLayoutPanel.ResumeLayout(false); 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); } @@ -257,5 +330,7 @@ private System.Windows.Forms.FlowLayoutPanel ToolbarFlowLayoutPanel; private System.Windows.Forms.MenuItem MenuItemSeparater; private System.Windows.Forms.MenuItem ShowToolbarMenuItem; + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.DataVisualization.Charting.Chart trafficChart; } } \ No newline at end of file diff --git a/shadowsocks-csharp/View/LogForm.cs b/shadowsocks-csharp/View/LogForm.cs index d49cd1af..5a83d96e 100644 --- a/shadowsocks-csharp/View/LogForm.cs +++ b/shadowsocks-csharp/View/LogForm.cs @@ -2,6 +2,9 @@ using System.Drawing; using System.IO; using System.Windows.Forms; +using System.Windows.Forms.DataVisualization.Charting; +using System.Collections.Generic; +using System.Linq; using Shadowsocks.Controller; using Shadowsocks.Properties; @@ -18,6 +21,15 @@ namespace Shadowsocks.View const int BACK_OFFSET = 65536; ShadowsocksController controller; + #region chart + List inboundPoints = new List(); + List outboundPoints = new List(); + long maxSpeed = 0; + Tuple bandwidthScale = new Tuple(0, "B", 1); + TextAnnotation inboundAnnotation = new TextAnnotation(); + TextAnnotation outboundAnnotation = new TextAnnotation(); + #endregion + public LogForm(ShadowsocksController controller, string filename) { this.controller = controller; @@ -30,7 +42,8 @@ namespace Shadowsocks.View { config = new LogViewerConfig(); } - else { + else + { topMostTrigger = config.topMost; wrapTextTrigger = config.wrapText; toolbarTrigger = config.toolbarShown; @@ -39,9 +52,46 @@ namespace Shadowsocks.View LogMessageTextBox.Font = config.GetFont(); } + controller.TrafficChanged += controller_TrafficChanged; + 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, .ForEach does not work + inboundPoints = inboundPoints.Select(p => p / bandwidthScale.Item3).ToList(); + outboundPoints = outboundPoints.Select(p => p / bandwidthScale.Item3).ToList(); + + 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); + })); + } + } + private void UpdateTexts() { FileMenuItem.Text = I18N.GetString("&File"); @@ -144,6 +194,7 @@ namespace Shadowsocks.View private void LogForm_FormClosing(object sender, FormClosingEventArgs e) { timer.Stop(); + controller.TrafficChanged -= controller_TrafficChanged; LogViewerConfig config = controller.GetConfigurationCopy().logViewer; if (config == null) config = new LogViewerConfig(); diff --git a/shadowsocks-csharp/View/LogForm.resx b/shadowsocks-csharp/View/LogForm.resx index c921ecfb..8f20a9f7 100644 --- a/shadowsocks-csharp/View/LogForm.resx +++ b/shadowsocks-csharp/View/LogForm.resx @@ -144,4 +144,7 @@ True + + 39 + \ No newline at end of file diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index fab3d729..3a5e5367 100755 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Drawing; +using System.Drawing.Imaging; using System.Windows.Forms; using ZXing; @@ -25,6 +26,8 @@ namespace Shadowsocks.View private UpdateChecker updateChecker; private NotifyIcon _notifyIcon; + private Bitmap icon_baseBitmap; + private Icon icon_base, icon_in, icon_out, icon_both, targetIcon; private ContextMenu contextMenu1; private bool _isFirstRun; @@ -74,6 +77,7 @@ namespace Shadowsocks.View _notifyIcon.MouseClick += notifyIcon1_Click; _notifyIcon.MouseDoubleClick += notifyIcon1_DoubleClick; _notifyIcon.BalloonTipClosed += _notifyIcon_BalloonTipClosed; + controller.TrafficChanged += controller_TrafficChanged; this.updateChecker = new UpdateChecker(); updateChecker.CheckUpdateCompleted += updateChecker_CheckUpdateCompleted; @@ -94,6 +98,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) { MessageBox.Show(e.GetException().ToString(), String.Format(I18N.GetString("Shadowsocks Error: {0}"), e.GetException().Message)); @@ -105,26 +135,32 @@ namespace Shadowsocks.View Graphics graphics = Graphics.FromHwnd(IntPtr.Zero); dpi = (int)graphics.DpiX; graphics.Dispose(); - Bitmap icon = null; + icon_baseBitmap = null; if (dpi < 97) { // dpi = 96; - icon = Resources.ss16; + icon_baseBitmap = Resources.ss16; } else if (dpi < 121) { // dpi = 120; - icon = Resources.ss20; + icon_baseBitmap = Resources.ss20; } else { - icon = Resources.ss24; + icon_baseBitmap = Resources.ss24; } Configuration config = controller.GetConfigurationCopy(); bool enabled = config.enabled; 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; if (controller.GetCurrentStrategy() != null) @@ -177,6 +213,19 @@ namespace Shadowsocks.View 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) { return new MenuItem(I18N.GetString(text), click); diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 008134ae..15046bb9 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -291,6 +291,8 @@ Designer + + From 6a8a09983e22e5e8fb529a8f32af969e7f7f2e7d Mon Sep 17 00:00:00 2001 From: Syrone Wong Date: Fri, 5 Aug 2016 11:05:16 +0800 Subject: [PATCH 06/11] Merge fixes from @GangZhuo, close #478 (#638) Squashed commit of the following: commit e280c2385c234f68e3b653dbd9efc26ed9df002f Merge: 579039f 87aa9eb Author: Gang Zhuo Date: Wed Mar 16 04:02:27 2016 -0400 Merge remote-tracking branch 'origin/master' commit 579039fe40d2ac66b97fd9cbfe6bda695d4e92ec Author: Gang Zhuo Date: Fri Mar 4 21:45:23 2016 +0800 log unhandle exception commit 8d863f1d5f484bf636a0780f5e69d944b1810ae2 Author: Gang Zhuo Date: Fri Mar 4 21:44:54 2016 +0800 fix Availability Statistics commit b3ce1c698a7518e76f521c411ca0d4240fc2f34a Author: Gang Zhuo Date: Fri Mar 4 21:07:54 2016 +0800 tiny refactor commit 0f7d39e27ef524bfa2d75034066bb074c91f91e6 Merge: bd7078a b01aced Author: Gang Zhuo Date: Fri Mar 4 20:02:58 2016 +0800 Merge remote-tracking branch 'origin/master' commit bd7078aa4f1198c5e94dc4b89b44632b42ed1982 Author: Gang Zhuo Date: Fri Mar 4 20:01:58 2016 +0800 tiny refactor commit 49adb95b6ec957a3a162399b846aca9ad1b65fdc Author: Gang Zhuo Date: Fri Mar 4 03:44:26 2016 -0500 log more Ping information for debug commit 2aa0620ddda3cb98e0db741529debd3a58b1e9c3 Author: Gang Zhuo Date: Fri Mar 4 03:04:10 2016 -0500 reduce 3rd packages [solve conflicts and squash to make history clean] Signed-off-by: Syrone Wong --- .../Controller/Service/AvailabilityStatistics.cs | 332 ++++++++++++++------- .../Controller/Service/PolipoRunner.cs | 43 ++- .../Controller/ShadowsocksController.cs | 7 +- shadowsocks-csharp/Data/cn.txt | 2 + shadowsocks-csharp/Program.cs | 15 + shadowsocks-csharp/packages.config | 5 - shadowsocks-csharp/shadowsocks-csharp.csproj | 34 --- test/packages.config | 8 - test/test.csproj | 30 -- 9 files changed, 261 insertions(+), 215 deletions(-) delete mode 100644 test/packages.config diff --git a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs index af039b18..a43c4780 100644 --- a/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs +++ b/shadowsocks-csharp/Controller/Service/AvailabilityStatistics.cs @@ -107,67 +107,30 @@ namespace Shadowsocks.Controller var bytes = inbound - lastInbound; _lastInboundCounter[id] = inbound; var inboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds); - _inboundSpeedRecords.GetOrAdd(id, new List {inboundSpeed}).Add(inboundSpeed); + _inboundSpeedRecords.GetOrAdd(id, (k) => + { + List records = new List(); + records.Add(inboundSpeed); + return records; + }); var lastOutbound = _lastOutboundCounter[id]; var outbound = _outboundCounter[id]; bytes = outbound - lastOutbound; _lastOutboundCounter[id] = outbound; var outboundSpeed = GetSpeedInKiBPerSecond(bytes, _monitorInterval.TotalSeconds); - _outboundSpeedRecords.GetOrAdd(id, new List {outboundSpeed}).Add(outboundSpeed); + _outboundSpeedRecords.GetOrAdd(id, (k) => + { + List records = new List(); + records.Add(outboundSpeed); + return records; + }); Logging.Debug( $"{id}: current/max inbound {inboundSpeed}/{_inboundSpeedRecords[id].Max()} KiB/s, current/max outbound {outboundSpeed}/{_outboundSpeedRecords[id].Max()} KiB/s"); } } - private async Task 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() { _inboundSpeedRecords.Clear(); @@ -178,15 +141,14 @@ namespace Shadowsocks.Controller private void Run(object _) { UpdateRecords(); - Save(); Reset(); - FilterRawStatistics(); } - private async void UpdateRecords() + private void UpdateRecords() { var records = new Dictionary(); - + UpdateRecordsState state = new UpdateRecordsState(); + state.counter = _controller.GetCurrentConfiguration().configs.Count; foreach (var server in _controller.GetCurrentConfiguration().configs) { var id = server.Identifier(); @@ -202,43 +164,80 @@ namespace Shadowsocks.Controller records[id] = record; else 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) { - List records; - if (!RawStatistics.TryGetValue(serverIdentifier, out records)) + try + { + List records; + lock (RawStatistics) + { + if (!RawStatistics.TryGetValue(serverIdentifier, out records)) + { + records = new List(); + RawStatistics[serverIdentifier] = records; + } + } + records.Add(record); + } + catch (Exception e) { - records = new List(); + Logging.LogUsefulException(e); } - records.Add(record); - RawStatistics[serverIdentifier] = records; } private void Save() { + Logging.Debug($"save statistics to {AvailabilityStatisticsFile}"); if (RawStatistics.Count == 0) { return; } 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); } catch (IOException e) @@ -258,17 +257,25 @@ namespace Shadowsocks.Controller 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) { - var result = (int) (bytes/seconds)/1024; + var result = (int)(bytes / seconds) / 1024; return result; } - private class ICMPResult - { - internal readonly List RoundtripTime = new List(); - internal readonly Server Server; - - internal ICMPResult(Server server) - { - Server = server; - } - } - public void Dispose() { _recorder.Dispose(); @@ -321,44 +317,158 @@ namespace Shadowsocks.Controller public void UpdateLatency(Server server, int latency) { - List records; - _latencyRecords.TryGetValue(server.Identifier(), out records); - if (records == null) + _latencyRecords.GetOrAdd(server.Identifier(), (k) => { - records = new List(); - } - records.Add(latency); - _latencyRecords[server.Identifier()] = records; + List records = new List(); + records.Add(latency); + return records; + }); } 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) { - 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 Completed; + private Server server; + + private int repeat; + private IPAddress ip; + private Ping ping; + private List RoundtripTime; + + public MyPing(Server server, int repeat) { - count += n; + this.server = server; + this.repeat = repeat; + RoundtripTime = new List(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 RoundtripTime; + public object UserState; } - _outboundCounter[server.Identifier()] = count; } + } } diff --git a/shadowsocks-csharp/Controller/Service/PolipoRunner.cs b/shadowsocks-csharp/Controller/Service/PolipoRunner.cs index 1c0efcdd..a8b1e2d2 100644 --- a/shadowsocks-csharp/Controller/Service/PolipoRunner.cs +++ b/shadowsocks-csharp/Controller/Service/PolipoRunner.cs @@ -47,20 +47,7 @@ namespace Shadowsocks.Controller Process[] existingPolipo = Process.GetProcessesByName("ss_privoxy"); 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; _runningPort = this.GetFreePort(); @@ -86,20 +73,30 @@ namespace Shadowsocks.Controller { if (_process != null) { - try - { - _process.Kill(); - _process.WaitForExit(); - } - catch (Exception e) - { - Logging.LogUsefulException(e); - } + KillProcess(_process); _process = null; } 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() { int defaultPort = 8123; diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index b6f64ed5..8023f51f 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -5,7 +5,6 @@ using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; -using System.Threading.Tasks; using Newtonsoft.Json; using Shadowsocks.Controller.Strategy; @@ -335,7 +334,7 @@ namespace Shadowsocks.Controller { if (_config.availabilityStatistics) { - new Task(() => availabilityStatistics.UpdateLatency(server, (int)latency.TotalMilliseconds)).Start(); + availabilityStatistics.UpdateLatency(server, (int)latency.TotalMilliseconds); } } @@ -344,7 +343,7 @@ namespace Shadowsocks.Controller Interlocked.Add(ref inboundCounter, n); if (_config.availabilityStatistics) { - new Task(() => availabilityStatistics.UpdateInboundCounter(server, n)).Start(); + availabilityStatistics.UpdateInboundCounter(server, n); } } @@ -353,7 +352,7 @@ namespace Shadowsocks.Controller Interlocked.Add(ref outboundCounter, n); if (_config.availabilityStatistics) { - new Task(() => availabilityStatistics.UpdateOutboundCounter(server, n)).Start(); + availabilityStatistics.UpdateOutboundCounter(server, n); } } diff --git a/shadowsocks-csharp/Data/cn.txt b/shadowsocks-csharp/Data/cn.txt index a1211fb4..30f51f20 100644 --- a/shadowsocks-csharp/Data/cn.txt +++ b/shadowsocks-csharp/Data/cn.txt @@ -101,3 +101,5 @@ Failed to decode QRCode=无法解析二维码 Failed to update registry=无法修改注册表 System Proxy On: =系统代理已启用: Running: Port {0}=正在运行:端口 {0} +Unexpect error, shadowsocks will be exit. Please report to=非预期错误,Shadowsocks将退出。请提交此错误到 + \ No newline at end of file diff --git a/shadowsocks-csharp/Program.cs b/shadowsocks-csharp/Program.cs index 977ebffd..f277de6f 100755 --- a/shadowsocks-csharp/Program.cs +++ b/shadowsocks-csharp/Program.cs @@ -21,6 +21,7 @@ namespace Shadowsocks Utils.ReleaseMemory(true); using (Mutex mutex = new Mutex(false, "Global\\Shadowsocks_" + Application.StartupPath.GetHashCode())) { + AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); @@ -53,5 +54,19 @@ namespace Shadowsocks 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(); + } + } } } diff --git a/shadowsocks-csharp/packages.config b/shadowsocks-csharp/packages.config index 6f3efda8..b80520d3 100644 --- a/shadowsocks-csharp/packages.config +++ b/shadowsocks-csharp/packages.config @@ -3,11 +3,6 @@ - - - - - \ No newline at end of file diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index 15046bb9..2bd9fa88 100644 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -66,18 +66,6 @@ - - 3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll - True - - - 3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll - True - - - 3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll - True - 3rd\Newtonsoft.Json.8.0.2\lib\net40\Newtonsoft.Json.dll @@ -88,27 +76,7 @@ - - 3rd\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll - True - - - 3rd\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.dll - True - - - 3rd\Microsoft.Net.Http.2.0.20710.0\lib\net40\System.Net.Http.WebRequest.dll - True - - - 3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll - True - - - 3rd\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll - True - @@ -332,10 +300,8 @@ 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}. - - diff --git a/test/packages.config b/test/packages.config deleted file mode 100644 index 7d90c6e3..00000000 --- a/test/packages.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/test/test.csproj b/test/test.csproj index 777a10b8..d1c3fa2e 100755 --- a/test/test.csproj +++ b/test/test.csproj @@ -35,29 +35,9 @@ - - ..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.dll - True - - - ..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.dll - True - - - ..\shadowsocks-csharp\3rd\Microsoft.Bcl.Async.1.0.168\lib\net40\Microsoft.Threading.Tasks.Extensions.Desktop.dll - True - - - ..\shadowsocks-csharp\3rd\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll - True - - - ..\shadowsocks-csharp\3rd\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll - True - @@ -82,9 +62,6 @@ shadowsocks-csharp - - - @@ -105,13 +82,6 @@ - - - - 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}. - - -