Browse Source

add HA

tags/2.5
clowwindy 9 years ago
parent
commit
ddd46ed8c6
12 changed files with 225 additions and 15 deletions
  1. +8
    -0
      shadowsocks-csharp/Controller/Logging.cs
  2. +26
    -0
      shadowsocks-csharp/Controller/Service/TCPRelay.cs
  3. +5
    -0
      shadowsocks-csharp/Controller/ShadowsocksController.cs
  4. +9
    -10
      shadowsocks-csharp/Controller/Strategy/BalancingStrategy.cs
  5. +126
    -0
      shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs
  6. +27
    -1
      shadowsocks-csharp/Controller/Strategy/IStrategy.cs
  7. +2
    -0
      shadowsocks-csharp/Controller/Strategy/StrategyManager.cs
  8. +1
    -0
      shadowsocks-csharp/Data/cn.txt
  9. +11
    -0
      shadowsocks-csharp/Model/Server.cs
  10. +5
    -1
      shadowsocks-csharp/View/ConfigForm.cs
  11. +4
    -3
      shadowsocks-csharp/View/MenuViewController.cs
  12. +1
    -0
      shadowsocks-csharp/shadowsocks-csharp.csproj

+ 8
- 0
shadowsocks-csharp/Controller/Logging.cs View File

@@ -31,6 +31,14 @@ namespace Shadowsocks.Controller
}
}
public static void Debug(object o)
{
#if DEBUG
Console.WriteLine(o);
#endif
}
public static void LogUsefulException(Exception e)
{
// just log useful exceptions, not all of them


+ 26
- 0
shadowsocks-csharp/Controller/Service/TCPRelay.cs View File

@@ -34,6 +34,7 @@ namespace Shadowsocks.Controller
Server server = _controller.GetCurrentStrategy().GetAServer(IStrategyCallerType.TCP, (IPEndPoint)socket.RemoteEndPoint);
handler.encryptor = EncryptorFactory.GetEncryptor(server.method, server.password);
handler.server = server;
handler.controller = _controller;
handler.Start(firstPacket, length);
return true;
@@ -48,6 +49,7 @@ namespace Shadowsocks.Controller
// Client socket.
public Socket remote;
public Socket connection;
public ShadowsocksController controller;
private byte command;
private byte[] _firstPacket;
@@ -55,6 +57,10 @@ namespace Shadowsocks.Controller
// Size of receive buffer.
public const int RecvSize = 16384;
public const int BufferSize = RecvSize + 32;
private int totalRead = 0;
private int totalWrite = 0;
// remote receive buffer
private byte[] remoteRecvBuffer = new byte[RecvSize];
// remote send buffer
@@ -72,6 +78,8 @@ namespace Shadowsocks.Controller
private object encryptionLock = new object();
private object decryptionLock = new object();
private DateTime _startConnectTime;
public void Start(byte[] firstPacket, int length)
{
this._firstPacket = firstPacket;
@@ -305,6 +313,8 @@ namespace Shadowsocks.Controller
SocketType.Stream, ProtocolType.Tcp);
remote.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
_startConnectTime = DateTime.Now;
// Connect to the remote endpoint.
remote.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), null);
@@ -330,10 +340,14 @@ namespace Shadowsocks.Controller
//Console.WriteLine("Socket connected to {0}",
// remote.RemoteEndPoint.ToString());
var latency = DateTime.Now - _startConnectTime;
controller.GetCurrentStrategy().UpdateLatency(this.server, latency);
StartPipe();
}
catch (Exception e)
{
controller.GetCurrentStrategy().SetFailure(this.server);
Logging.LogUsefulException(e);
this.Close();
}
@@ -368,6 +382,7 @@ namespace Shadowsocks.Controller
try
{
int bytesRead = remote.EndReceive(ar);
totalRead += bytesRead;
if (bytesRead > 0)
{
@@ -381,6 +396,8 @@ namespace Shadowsocks.Controller
encryptor.Decrypt(remoteRecvBuffer, bytesRead, remoteSendBuffer, out bytesToSend);
}
connection.BeginSend(remoteSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeConnectionSendCallback), null);
controller.GetCurrentStrategy().UpdateLastRead(this.server);
}
else
{
@@ -388,6 +405,12 @@ namespace Shadowsocks.Controller
connection.Shutdown(SocketShutdown.Send);
connectionShutdown = true;
CheckClose();
if (totalRead == 0)
{
// closed before anything received, reports as failure
controller.GetCurrentStrategy().SetFailure(this.server);
}
}
}
catch (Exception e)
@@ -406,6 +429,7 @@ namespace Shadowsocks.Controller
try
{
int bytesRead = connection.EndReceive(ar);
totalWrite += bytesRead;
if (bytesRead > 0)
{
@@ -419,6 +443,8 @@ namespace Shadowsocks.Controller
encryptor.Encrypt(connetionRecvBuffer, bytesRead, connetionSendBuffer, out bytesToSend);
}
remote.BeginSend(connetionSendBuffer, 0, bytesToSend, 0, new AsyncCallback(PipeRemoteSendCallback), null);
controller.GetCurrentStrategy().UpdateLastWrite(this.server);
}
else
{


+ 5
- 0
shadowsocks-csharp/Controller/ShadowsocksController.cs View File

@@ -282,6 +282,11 @@ namespace Shadowsocks.Controller
polipoRunner.Stop();
try
{
foreach (var strategy in GetStrategies())
{
strategy.ReloadServers();
}
polipoRunner.Start(_config);
TCPRelay tcpRelay = new TCPRelay(this);


+ 9
- 10
shadowsocks-csharp/Controller/Strategy/BalancingStrategy.cs View File

@@ -20,20 +20,19 @@ namespace Shadowsocks.Controller.Strategy
public string Name
{
get
{
return "Load Balance";
}
get { return I18N.GetString("Load Balance"); }
}
public string ID
{
get
{
return "com.shadowsocks.strategy.balancing";
}
get { return "com.shadowsocks.strategy.balancing"; }
}
public void ReloadServers()
{
// do nothing
}
public Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint)
{
var configs = _controller.GetCurrentConfiguration().configs;
@@ -49,7 +48,7 @@ namespace Shadowsocks.Controller.Strategy
return configs[index % configs.Count];
}
public void UpdateLatency(Model.Server server)
public void UpdateLatency(Model.Server server, TimeSpan latency)
{
// do nothing
}


+ 126
- 0
shadowsocks-csharp/Controller/Strategy/HighAvailabilityStrategy.cs View File

@@ -0,0 +1,126 @@
using Shadowsocks.Model;
using System;
using System.Collections.Generic;
using System.Text;
namespace Shadowsocks.Controller.Strategy
{
class HighAvailabilityStrategy : IStrategy
{
protected Server _currentServer;
protected Dictionary<Server, ServerStatus> _serverStatus;
ShadowsocksController _controller;
Random _random;
public class ServerStatus
{
// time interval between SYN and SYN+ACK
public TimeSpan latency;
// last time anything received
public DateTime lastRead;
// last time anything sent
public DateTime lastWrite;
// connection refused or closed before anything received
public DateTime lastFailure;
public Server server;
}
/**
* if last failure is > 10 min
* and (last write > last read) and (now - last read < 5s) // means not stuck
* choose the lowest latency
*/
public HighAvailabilityStrategy(ShadowsocksController controller)
{
_controller = controller;
_random = new Random();
_serverStatus = new Dictionary<Server, ServerStatus>();
}
public string Name
{
get { return I18N.GetString("High Availability"); }
}
public string ID
{
get { return "com.shadowsocks.strategy.ha"; }
}
public void ReloadServers()
{
// make a copy to avoid locking
var newServerStatus = new Dictionary<Server, ServerStatus>(_serverStatus);
foreach (var server in _controller.GetCurrentConfiguration().configs)
{
if (!newServerStatus.ContainsKey(server))
{
var status = new ServerStatus();
status.server = server;
newServerStatus[server] = status;
}
}
_serverStatus = newServerStatus;
// just leave removed servers there
// TODO
_currentServer = _controller.GetCurrentConfiguration().configs[0];
}
public Server GetAServer(IStrategyCallerType type, System.Net.IPEndPoint localIPEndPoint)
{
return _currentServer;
}
public void UpdateLatency(Model.Server server, TimeSpan latency)
{
Logging.Debug(String.Format("latency: {0} {1}", server.FriendlyName(), latency));
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))
{
status.latency = latency;
}
}
public void UpdateLastRead(Model.Server server)
{
Logging.Debug(String.Format("last read: {0}", server.FriendlyName()));
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))
{
status.lastRead = DateTime.Now;
}
}
public void UpdateLastWrite(Model.Server server)
{
Logging.Debug(String.Format("last write: {0}", server.FriendlyName()));
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))
{
status.lastWrite = DateTime.Now;
}
}
public void SetFailure(Model.Server server)
{
Logging.Debug(String.Format("failure: {0}", server.FriendlyName()));
ServerStatus status;
if (_serverStatus.TryGetValue(server, out status))
{
status.lastFailure = DateTime.Now;
}
}
}
}

+ 27
- 1
shadowsocks-csharp/Controller/Strategy/IStrategy.cs View File

@@ -12,19 +12,45 @@ namespace Shadowsocks.Controller.Strategy
UDP
}
/*
* IStrategy
*
* Subclasses must be thread-safe
*/
public interface IStrategy
{
string Name { get; }
string ID { get; }
/*
* Called when servers need to be reloaded, i.e. new configuration saved
*/
void ReloadServers();
/*
* Get a new server to use in TCPRelay or UDPRelay
*/
Server GetAServer(IStrategyCallerType type, IPEndPoint localIPEndPoint);
void UpdateLatency(Server server);
/*
* TCPRelay will call this when latency of a server detected
*/
void UpdateLatency(Server server, TimeSpan latency);
/*
* TCPRelay will call this when reading from a server
*/
void UpdateLastRead(Server server);
/*
* TCPRelay will call this when writing to a server
*/
void UpdateLastWrite(Server server);
/*
* TCPRelay will call this when fatal failure detected
*/
void SetFailure(Server server);
}
}

+ 2
- 0
shadowsocks-csharp/Controller/Strategy/StrategyManager.cs View File

@@ -12,6 +12,8 @@ namespace Shadowsocks.Controller.Strategy
{
_strategies = new List<IStrategy>();
_strategies.Add(new BalancingStrategy(controller));
_strategies.Add(new HighAvailabilityStrategy(controller));
// TODO: load DLL plugins
}
public IList<IStrategy> GetStrategies()
{


+ 1
- 0
shadowsocks-csharp/Data/cn.txt View File

@@ -24,6 +24,7 @@ About...=关于...
Quit=退出
Edit Servers=编辑服务器
Load Balance=负载均衡
High Availability=高可用

# Config Form



+ 11
- 0
shadowsocks-csharp/Model/Server.cs View File

@@ -18,6 +18,17 @@ namespace Shadowsocks.Model
public string method;
public string remarks;
public override int GetHashCode()
{
return server.GetHashCode() ^ server_port;
}
public override bool Equals(object obj)
{
Server o2 = (Server)obj;
return this.server == o2.server && this.server_port == o2.server_port;
}
public string FriendlyName()
{
if (string.IsNullOrEmpty(server))


+ 5
- 1
shadowsocks-csharp/View/ConfigForm.cs View File

@@ -136,7 +136,11 @@ namespace Shadowsocks.View
_modifiedConfiguration = controller.GetConfigurationCopy();
LoadConfiguration(_modifiedConfiguration);
_oldSelectedIndex = _modifiedConfiguration.index;
ServersListBox.SelectedIndex = _modifiedConfiguration.index;
if (_oldSelectedIndex < 0)
{
_oldSelectedIndex = 0;
}
ServersListBox.SelectedIndex = _oldSelectedIndex;
LoadSelectedServer();
}


+ 4
- 3
shadowsocks-csharp/View/MenuViewController.cs View File

@@ -127,7 +127,7 @@ namespace Shadowsocks.View
string serverInfo = null;
if (config.strategy != null)
{
serverInfo = I18N.GetString(controller.GetCurrentStrategy().Name);
serverInfo = controller.GetCurrentStrategy().Name;
}
else
{
@@ -275,17 +275,18 @@ namespace Shadowsocks.View
int i = 0;
foreach (var strategy in controller.GetStrategies())
{
MenuItem item = new MenuItem(I18N.GetString(strategy.Name));
MenuItem item = new MenuItem(strategy.Name);
item.Tag = strategy.ID;
item.Click += AStrategyItem_Click;
items.Add(i, item);
i++;
}
int strategyCount = i;
Configuration configuration = controller.GetConfigurationCopy();
foreach (var server in configuration.configs)
{
MenuItem item = new MenuItem(server.FriendlyName());
item.Tag = i;
item.Tag = i - strategyCount;
item.Click += AServerItem_Click;
items.Add(i, item);
i++;


+ 1
- 0
shadowsocks-csharp/shadowsocks-csharp.csproj View File

@@ -124,6 +124,7 @@
<Compile Include="3rd\zxing\ResultPoint.cs" />
<Compile Include="3rd\zxing\ResultPointCallback.cs" />
<Compile Include="3rd\zxing\WriterException.cs" />
<Compile Include="Controller\Strategy\HighAvailabilityStrategy.cs" />
<Compile Include="Controller\System\AutoStartup.cs" />
<Compile Include="Controller\FileManager.cs" />
<Compile Include="Controller\Service\GFWListUpdater.cs" />


Loading…
Cancel
Save