diff --git a/shadowsocks-csharp/Controller/GfwListUpdater.cs b/shadowsocks-csharp/Controller/Service/GfwListUpdater.cs similarity index 100% rename from shadowsocks-csharp/Controller/GfwListUpdater.cs rename to shadowsocks-csharp/Controller/Service/GfwListUpdater.cs diff --git a/shadowsocks-csharp/Controller/Listener.cs b/shadowsocks-csharp/Controller/Service/Listener.cs old mode 100755 new mode 100644 similarity index 100% rename from shadowsocks-csharp/Controller/Listener.cs rename to shadowsocks-csharp/Controller/Service/Listener.cs diff --git a/shadowsocks-csharp/Controller/PACServer.cs b/shadowsocks-csharp/Controller/Service/PACServer.cs old mode 100755 new mode 100644 similarity index 100% rename from shadowsocks-csharp/Controller/PACServer.cs rename to shadowsocks-csharp/Controller/Service/PACServer.cs diff --git a/shadowsocks-csharp/Controller/PolipoRunner.cs b/shadowsocks-csharp/Controller/Service/PolipoRunner.cs old mode 100755 new mode 100644 similarity index 100% rename from shadowsocks-csharp/Controller/PolipoRunner.cs rename to shadowsocks-csharp/Controller/Service/PolipoRunner.cs diff --git a/shadowsocks-csharp/Controller/PortForwarder.cs b/shadowsocks-csharp/Controller/Service/PortForwarder.cs old mode 100755 new mode 100644 similarity index 100% rename from shadowsocks-csharp/Controller/PortForwarder.cs rename to shadowsocks-csharp/Controller/Service/PortForwarder.cs diff --git a/shadowsocks-csharp/Controller/TCPRelay.cs b/shadowsocks-csharp/Controller/Service/TCPRelay.cs old mode 100755 new mode 100644 similarity index 94% rename from shadowsocks-csharp/Controller/TCPRelay.cs rename to shadowsocks-csharp/Controller/Service/TCPRelay.cs index 610d28b7..49abc6db --- a/shadowsocks-csharp/Controller/TCPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/TCPRelay.cs @@ -5,16 +5,17 @@ using System.Net.Sockets; using System.Net; using Shadowsocks.Encryption; using Shadowsocks.Model; +using Shadowsocks.Controller.Strategy; namespace Shadowsocks.Controller { class TCPRelay : Listener.Service { - private Configuration _config; - public TCPRelay(Configuration config) + private ShadowsocksController _controller; + public TCPRelay(ShadowsocksController controller) { - this._config = config; + this._controller = controller; } public bool Handle(byte[] firstPacket, int length, Socket socket, object state) @@ -30,7 +31,7 @@ namespace Shadowsocks.Controller socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true); Handler handler = new Handler(); handler.connection = socket; - Server server = _config.GetCurrentServer(); + Server server = _controller.GetCurrentStrategy().GetAServer(IStrategyCallerType.TCP, (IPEndPoint)socket.RemoteEndPoint); handler.encryptor = EncryptorFactory.GetEncryptor(server.method, server.password); handler.server = server; diff --git a/shadowsocks-csharp/Controller/UDPRelay.cs b/shadowsocks-csharp/Controller/Service/UDPRelay.cs similarity index 92% rename from shadowsocks-csharp/Controller/UDPRelay.cs rename to shadowsocks-csharp/Controller/Service/UDPRelay.cs index beee8d85..53ee32ac 100644 --- a/shadowsocks-csharp/Controller/UDPRelay.cs +++ b/shadowsocks-csharp/Controller/Service/UDPRelay.cs @@ -6,16 +6,17 @@ using Shadowsocks.Model; using System.Net.Sockets; using System.Net; using System.Runtime.CompilerServices; +using Shadowsocks.Controller.Strategy; namespace Shadowsocks.Controller { class UDPRelay : Listener.Service { - private Configuration _config; + private ShadowsocksController _controller; private LRUCache _cache; - public UDPRelay(Configuration config) + public UDPRelay(ShadowsocksController controller) { - this._config = config; + this._controller = controller; this._cache = new LRUCache(512); // todo: choose a smart number } @@ -34,7 +35,7 @@ namespace Shadowsocks.Controller UDPHandler handler = _cache.get(remoteEndPoint); if (handler == null) { - handler = new UDPHandler(socket, _config.GetCurrentServer(), remoteEndPoint); + handler = new UDPHandler(socket, _controller.GetCurrentStrategy().GetAServer(IStrategyCallerType.UDP, remoteEndPoint), remoteEndPoint); _cache.add(remoteEndPoint, handler); } handler.Send(firstPacket, length); diff --git a/shadowsocks-csharp/Controller/UpdateChecker.cs b/shadowsocks-csharp/Controller/Service/UpdateChecker.cs old mode 100755 new mode 100644 similarity index 100% rename from shadowsocks-csharp/Controller/UpdateChecker.cs rename to shadowsocks-csharp/Controller/Service/UpdateChecker.cs diff --git a/shadowsocks-csharp/Controller/ShadowsocksController.cs b/shadowsocks-csharp/Controller/ShadowsocksController.cs index 0eb57c94..a3e56391 100755 --- a/shadowsocks-csharp/Controller/ShadowsocksController.cs +++ b/shadowsocks-csharp/Controller/ShadowsocksController.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Text; using System.Threading; using System.Net.Sockets; +using Shadowsocks.Controller.Strategy; namespace Shadowsocks.Controller { @@ -20,6 +21,7 @@ namespace Shadowsocks.Controller private Listener _listener; private PACServer _pacServer; private Configuration _config; + private StrategyManager _strategyManager; private PolipoRunner polipoRunner; private GFWListUpdater gfwListUpdater; private bool stopped = false; @@ -49,6 +51,7 @@ namespace Shadowsocks.Controller public ShadowsocksController() { _config = Configuration.Load(); + _strategyManager = new StrategyManager(this); } public void Start() @@ -70,11 +73,34 @@ namespace Shadowsocks.Controller } // always return copy - public Configuration GetConfiguration() + public Configuration GetConfigurationCopy() { return Configuration.Load(); } + // always return current instance + public Configuration GetCurrentConfiguration() + { + return _config; + } + + public IList GetStrategies() + { + return _strategyManager.GetStrategies(); + } + + public IStrategy GetCurrentStrategy() + { + foreach (var strategy in _strategyManager.GetStrategies()) + { + if (strategy.ID == this._config.strategy) + { + return strategy; + } + } + return null; + } + public void SaveServers(List servers, int localPort) { _config.configs = servers; @@ -134,6 +160,14 @@ namespace Shadowsocks.Controller public void SelectServerIndex(int index) { _config.index = index; + _config.strategy = null; + SaveConfig(_config); + } + + public void SelectStrategy(string strategyID) + { + _config.index = -1; + _config.strategy = strategyID; SaveConfig(_config); } @@ -250,8 +284,8 @@ namespace Shadowsocks.Controller { polipoRunner.Start(_config); - TCPRelay tcpRelay = new TCPRelay(_config); - UDPRelay udpRelay = new UDPRelay(_config); + TCPRelay tcpRelay = new TCPRelay(this); + UDPRelay udpRelay = new UDPRelay(this); List services = new List(); services.Add(tcpRelay); services.Add(udpRelay); diff --git a/shadowsocks-csharp/Controller/Strategy/BalancingStrategy.cs b/shadowsocks-csharp/Controller/Strategy/BalancingStrategy.cs new file mode 100644 index 00000000..1e4f8a9a --- /dev/null +++ b/shadowsocks-csharp/Controller/Strategy/BalancingStrategy.cs @@ -0,0 +1,72 @@ +using Shadowsocks.Controller; +using Shadowsocks.Model; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace Shadowsocks.Controller.Strategy +{ + class BalancingStrategy : IStrategy + { + ShadowsocksController _controller; + Random _random; + + public BalancingStrategy(ShadowsocksController controller) + { + _controller = controller; + _random = new Random(); + } + + public string Name + { + get + { + return "Load Balance"; + } + } + + public string ID + { + get + { + return "com.shadowsocks.strategy.balancing"; + } + } + + public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint) + { + var configs = _controller.GetCurrentConfiguration().configs; + int index; + if (type == IStrategyCallerType.TCP) + { + index = _random.Next(); + } + else + { + index = localIPEndPoint.GetHashCode(); + } + return configs[index % configs.Count]; + } + + public void UpdateLatency(Model.Server server) + { + // do nothing + } + + public void UpdateLastRead(Model.Server server) + { + // do nothing + } + + public void UpdateLastWrite(Model.Server server) + { + // do nothing + } + + public void SetFailure(Model.Server server) + { + // do nothing + } + } +} diff --git a/shadowsocks-csharp/Controller/Strategy/IStrategy.cs b/shadowsocks-csharp/Controller/Strategy/IStrategy.cs new file mode 100644 index 00000000..8e4f65a0 --- /dev/null +++ b/shadowsocks-csharp/Controller/Strategy/IStrategy.cs @@ -0,0 +1,30 @@ +using Shadowsocks.Model; +using System; +using System.Collections.Generic; +using System.Net; +using System.Text; + +namespace Shadowsocks.Controller.Strategy +{ + public enum IStrategyCallerType + { + TCP, + UDP + } + + public interface IStrategy + { + string Name { get; } + string ID { get; } + + Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint); + + void UpdateLatency(Server server); + + void UpdateLastRead(Server server); + + void UpdateLastWrite(Server server); + + void SetFailure(Server server); + } +} diff --git a/shadowsocks-csharp/Controller/Strategy/StrategyManager.cs b/shadowsocks-csharp/Controller/Strategy/StrategyManager.cs new file mode 100644 index 00000000..254dd636 --- /dev/null +++ b/shadowsocks-csharp/Controller/Strategy/StrategyManager.cs @@ -0,0 +1,21 @@ +using Shadowsocks.Controller; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Shadowsocks.Controller.Strategy +{ + class StrategyManager + { + List _strategies; + public StrategyManager(ShadowsocksController controller) + { + _strategies = new List(); + _strategies.Add(new BalancingStrategy(controller)); + } + public IList GetStrategies() + { + return _strategies; + } + } +} diff --git a/shadowsocks-csharp/Controller/AutoStartup.cs b/shadowsocks-csharp/Controller/System/AutoStartup.cs similarity index 100% rename from shadowsocks-csharp/Controller/AutoStartup.cs rename to shadowsocks-csharp/Controller/System/AutoStartup.cs diff --git a/shadowsocks-csharp/Controller/SystemProxy.cs b/shadowsocks-csharp/Controller/System/SystemProxy.cs old mode 100755 new mode 100644 similarity index 100% rename from shadowsocks-csharp/Controller/SystemProxy.cs rename to shadowsocks-csharp/Controller/System/SystemProxy.cs diff --git a/shadowsocks-csharp/Data/cn.txt b/shadowsocks-csharp/Data/cn.txt index 3f4a3ac2..cf0054f0 100644 --- a/shadowsocks-csharp/Data/cn.txt +++ b/shadowsocks-csharp/Data/cn.txt @@ -23,6 +23,7 @@ Show Logs...=显示日志... About...=关于... Quit=退出 Edit Servers=编辑服务器 +Load Balance=负载均衡 # Config Form diff --git a/shadowsocks-csharp/Model/Configuration.cs b/shadowsocks-csharp/Model/Configuration.cs index 294dd9b2..812883b2 100755 --- a/shadowsocks-csharp/Model/Configuration.cs +++ b/shadowsocks-csharp/Model/Configuration.cs @@ -11,6 +11,9 @@ namespace Shadowsocks.Model public class Configuration { public List configs; + + // when strategy is set, index is ignored + public string strategy; public int index; public bool global; public bool enabled; @@ -79,9 +82,9 @@ namespace Shadowsocks.Model { config.index = config.configs.Count - 1; } - if (config.index < 0) + if (config.index < -1) { - config.index = 0; + config.index = -1; } config.isDefault = false; try diff --git a/shadowsocks-csharp/View/ConfigForm.cs b/shadowsocks-csharp/View/ConfigForm.cs index 574fafd5..d67acec5 100755 --- a/shadowsocks-csharp/View/ConfigForm.cs +++ b/shadowsocks-csharp/View/ConfigForm.cs @@ -133,7 +133,7 @@ namespace Shadowsocks.View private void LoadCurrentConfiguration() { - _modifiedConfiguration = controller.GetConfiguration(); + _modifiedConfiguration = controller.GetConfigurationCopy(); LoadConfiguration(_modifiedConfiguration); _oldSelectedIndex = _modifiedConfiguration.index; ServersListBox.SelectedIndex = _modifiedConfiguration.index; diff --git a/shadowsocks-csharp/View/MenuViewController.cs b/shadowsocks-csharp/View/MenuViewController.cs index ff5a018c..ac144569 100755 --- a/shadowsocks-csharp/View/MenuViewController.cs +++ b/shadowsocks-csharp/View/MenuViewController.cs @@ -71,9 +71,9 @@ namespace Shadowsocks.View LoadCurrentConfiguration(); - updateChecker.CheckUpdate(controller.GetConfiguration()); + updateChecker.CheckUpdate(controller.GetConfigurationCopy()); - if (controller.GetConfiguration().isDefault) + if (controller.GetConfigurationCopy().isDefault) { _isFirstRun = true; ShowConfigForm(); @@ -106,7 +106,7 @@ namespace Shadowsocks.View { icon = Resources.ss24; } - Configuration config = controller.GetConfiguration(); + Configuration config = controller.GetConfigurationCopy(); bool enabled = config.enabled; bool global = config.global; if (!enabled) @@ -124,12 +124,21 @@ namespace Shadowsocks.View } _notifyIcon.Icon = Icon.FromHandle(icon.GetHicon()); + string serverInfo = null; + if (config.strategy != null) + { + serverInfo = I18N.GetString(controller.GetCurrentStrategy().Name); + } + else + { + serverInfo = config.GetCurrentServer().FriendlyName(); + } // we want to show more details but notify icon title is limited to 63 characters string text = I18N.GetString("Shadowsocks") + " " + UpdateChecker.Version + "\n" + (enabled ? I18N.GetString("System Proxy On: ") + (global ? I18N.GetString("Global") : I18N.GetString("PAC")) : String.Format(I18N.GetString("Running: Port {0}"), config.localPort)) // this feedback is very important because they need to know Shadowsocks is running - + "\n" + config.GetCurrentServer().FriendlyName(); + + "\n" + serverInfo; _notifyIcon.Text = text.Substring(0, Math.Min(63, text.Length)); } @@ -185,18 +194,18 @@ namespace Shadowsocks.View private void controller_EnableStatusChanged(object sender, EventArgs e) { - enableItem.Checked = controller.GetConfiguration().enabled; + enableItem.Checked = controller.GetConfigurationCopy().enabled; modeItem.Enabled = enableItem.Checked; } void controller_ShareOverLANStatusChanged(object sender, EventArgs e) { - ShareOverLANItem.Checked = controller.GetConfiguration().shareOverLan; + ShareOverLANItem.Checked = controller.GetConfigurationCopy().shareOverLan; } void controller_EnableGlobalChanged(object sender, EventArgs e) { - globalModeItem.Checked = controller.GetConfiguration().global; + globalModeItem.Checked = controller.GetConfigurationCopy().global; PACModeItem.Checked = !globalModeItem.Checked; } @@ -243,7 +252,7 @@ namespace Shadowsocks.View private void LoadCurrentConfiguration() { - Configuration config = controller.GetConfiguration(); + Configuration config = controller.GetConfigurationCopy(); UpdateServersMenu(); enableItem.Checked = config.enabled; modeItem.Enabled = config.enabled; @@ -263,20 +272,32 @@ namespace Shadowsocks.View { items.RemoveAt(0); } - - Configuration configuration = controller.GetConfiguration(); - for (int i = 0; i < configuration.configs.Count; i++) + int i = 0; + foreach (var strategy in controller.GetStrategies()) + { + MenuItem item = new MenuItem(I18N.GetString(strategy.Name)); + item.Tag = strategy.ID; + item.Click += AStrategyItem_Click; + items.Add(i, item); + i++; + } + Configuration configuration = controller.GetConfigurationCopy(); + foreach (var server in configuration.configs) { - Server server = configuration.configs[i]; MenuItem item = new MenuItem(server.FriendlyName()); item.Tag = i; item.Click += AServerItem_Click; items.Add(i, item); + i++; } - if (configuration.index >= 0 && configuration.index < configuration.configs.Count) + foreach (MenuItem item in items) { - items[configuration.index].Checked = true; + if (item.Tag != null && (item.Tag.ToString() == configuration.index.ToString() || item.Tag.ToString() == configuration.strategy)) + { + item.Checked = true; + } + } } @@ -380,6 +401,12 @@ namespace Shadowsocks.View controller.SelectServerIndex((int)item.Tag); } + private void AStrategyItem_Click(object sender, EventArgs e) + { + MenuItem item = (MenuItem)sender; + controller.SelectStrategy((string)item.Tag); + } + private void ShowLogItem_Click(object sender, EventArgs e) { string argument = Logging.LogFile; @@ -511,11 +538,11 @@ namespace Shadowsocks.View { if (!onlinePACItem.Checked) { - if (String.IsNullOrEmpty(controller.GetConfiguration().pacUrl)) + if (String.IsNullOrEmpty(controller.GetConfigurationCopy().pacUrl)) { UpdateOnlinePACURLItem_Click(sender, e); } - if (!String.IsNullOrEmpty(controller.GetConfiguration().pacUrl)) + if (!String.IsNullOrEmpty(controller.GetConfigurationCopy().pacUrl)) { localPACItem.Checked = false; onlinePACItem.Checked = true; @@ -527,7 +554,7 @@ namespace Shadowsocks.View private void UpdateOnlinePACURLItem_Click(object sender, EventArgs e) { - string origPacUrl = controller.GetConfiguration().pacUrl; + string origPacUrl = controller.GetConfigurationCopy().pacUrl; string pacUrl = Microsoft.VisualBasic.Interaction.InputBox( I18N.GetString("Please input PAC Url"), I18N.GetString("Edit Online PAC URL"), diff --git a/shadowsocks-csharp/shadowsocks-csharp.csproj b/shadowsocks-csharp/shadowsocks-csharp.csproj index fc87e606..7ec2eccf 100755 --- a/shadowsocks-csharp/shadowsocks-csharp.csproj +++ b/shadowsocks-csharp/shadowsocks-csharp.csproj @@ -124,15 +124,15 @@ - + - + - + - - - + + + @@ -142,7 +142,7 @@ - + @@ -150,6 +150,9 @@ True Resources.resx + + + Form @@ -157,12 +160,12 @@ ConfigForm.cs - - + + - + Form