diff --git a/shadowsocks-csharp/Controller/Listener.cs b/shadowsocks-csharp/Controller/Listener.cs index 1cf7cb97..a1f316b8 100755 --- a/shadowsocks-csharp/Controller/Listener.cs +++ b/shadowsocks-csharp/Controller/Listener.cs @@ -18,7 +18,7 @@ namespace Shadowsocks.Controller public class UDPState { public byte[] buffer = new byte[4096]; - public EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + public EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 1); } Configuration _config; diff --git a/shadowsocks-csharp/Controller/UDPRelay.cs b/shadowsocks-csharp/Controller/UDPRelay.cs index afa25aa2..bad2e41b 100644 --- a/shadowsocks-csharp/Controller/UDPRelay.cs +++ b/shadowsocks-csharp/Controller/UDPRelay.cs @@ -5,15 +5,18 @@ using Shadowsocks.Encryption; using Shadowsocks.Model; using System.Net.Sockets; using System.Net; +using System.Runtime.CompilerServices; namespace Shadowsocks.Controller { class UDPRelay : Listener.Service { private Configuration _config; + private LRUCache _cache; public UDPRelay(Configuration config) { this._config = config; + this._cache = new LRUCache(512); // todo: choose a smart number } public bool Handle(byte[] firstPacket, int length, Socket socket, object state) @@ -27,14 +30,19 @@ namespace Shadowsocks.Controller return false; } Listener.UDPState udpState = (Listener.UDPState)state; - // TODO add cache - UDPHandler handler = new UDPHandler(socket, _config.GetCurrentServer(), (IPEndPoint)udpState.remoteEndPoint); + IPEndPoint remoteEndPoint = (IPEndPoint)udpState.remoteEndPoint; + UDPHandler handler = _cache.get(remoteEndPoint); + if (handler == null) + { + handler = new UDPHandler(socket, _config.GetCurrentServer(), remoteEndPoint); + _cache.add(remoteEndPoint, handler); + } handler.Send(firstPacket, length); handler.Receive(); return true; } - class UDPHandler + public class UDPHandler { private Socket _local; private Socket _remote; @@ -47,7 +55,6 @@ namespace Shadowsocks.Controller public UDPHandler(Socket local, Server server, IPEndPoint localEndPoint) { - // TODO add timeout _local = local; _server = server; _localEndPoint = localEndPoint; @@ -108,6 +115,85 @@ namespace Shadowsocks.Controller { } } + public void Close() + { + try + { + _remote.Close(); + } + catch (ObjectDisposedException) + { + } + catch (Exception e) + { + } + finally + { + } + } + } + } + // cc by-sa 3.0 http://stackoverflow.com/a/3719378/1124054 + class LRUCache where V : UDPRelay.UDPHandler + { + private int capacity; + private Dictionary>> cacheMap = new Dictionary>>(); + private LinkedList> lruList = new LinkedList>(); + + public LRUCache(int capacity) + { + this.capacity = capacity; + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public V get(K key) + { + LinkedListNode> node; + if (cacheMap.TryGetValue(key, out node)) + { + V value = node.Value.value; + lruList.Remove(node); + lruList.AddLast(node); + return value; + } + return default(V); + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public void add(K key, V val) + { + if (cacheMap.Count >= capacity) + { + RemoveFirst(); + } + + LRUCacheItem cacheItem = new LRUCacheItem(key, val); + LinkedListNode> node = new LinkedListNode>(cacheItem); + lruList.AddLast(node); + cacheMap.Add(key, node); } + + private void RemoveFirst() + { + // Remove from LRUPriority + LinkedListNode> node = lruList.First; + lruList.RemoveFirst(); + + // Remove from cache + cacheMap.Remove(node.Value.key); + node.Value.value.Close(); + } + } + + class LRUCacheItem + { + public LRUCacheItem(K k, V v) + { + key = k; + value = v; + } + public K key; + public V value; } + }