diff --git a/Source/RemObjects.InternetPack/Bindings.cs b/Source/RemObjects.InternetPack/Bindings.cs index 801df4a..e8e887f 100644 --- a/Source/RemObjects.InternetPack/Bindings.cs +++ b/Source/RemObjects.InternetPack/Bindings.cs @@ -131,6 +131,8 @@ public Boolean ShouldSerializePort() public class ServerBinding : Binding { + ManualResetEvent _accepted; + public ServerBinding() { this.fListenerThreadCount = 1; @@ -235,12 +237,16 @@ public virtual void Unbind(Boolean block) if (this.fListeningSocket == null) return; - this.fListeningSocket.Close(); + using (this.fListeningSocket) + { + this.fListeningSocket.Close(); #if FULLFRAMEWORK - if (block && this.fListenThreads != null) - for (Int32 i = 0; i < this.fListenThreads.Length; i++) - this.fListenThreads[i].Join(); + if (block && this.fListenThreads != null) + for (Int32 i = 0; i < this.fListenThreads.Length; i++) + this.fListenThreads[i].Join(); #endif + this.fListeningSocket = null; + } } public virtual void BindUnthreaded() @@ -249,12 +255,45 @@ public virtual void BindUnthreaded() this.fListeningSocket = new Socket(this.AddressFamily, this.SocketType, this.Protocol); if (!this.EnableNagle) this.fListeningSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, 1); - this.fListeningSocket.Bind(this.fEndPoint); + + try + { + this.fListeningSocket.Bind(this.fEndPoint); + } + catch (SocketException) + { + using (this.fListeningSocket) + { + this.fListeningSocket = null; + } + this.fEndPoint = null; + throw; + } } public virtual Connection Accept() { - return new Connection(this.fListeningSocket.Accept()); + using (_accepted = new ManualResetEvent(false)) + { + var cancelThread = new Thread(CancelAccept); + cancelThread.Start(); + + var socket = this.fListeningSocket.Accept(); + + _accepted.Set(); + cancelThread.Join(); + _accepted = null; + + return new Connection(socket); + } + } + + void CancelAccept() + { + if (_accepted.WaitOne(TimeSpan.FromSeconds(20))) + return; + + fListeningSocket.Close(); } } diff --git a/Source/RemObjects.InternetPack/FtpServer.cs b/Source/RemObjects.InternetPack/FtpServer.cs index 0908639..42ea5ed 100644 --- a/Source/RemObjects.InternetPack/FtpServer.cs +++ b/Source/RemObjects.InternetPack/FtpServer.cs @@ -1161,16 +1161,20 @@ public static void Cmd_PASV(Object sender, CommandEventArgs e) lSession.PassiveServer = new SimpleServer(); lSession.PassiveServer.Binding.Address = ((IPEndPoint)e.Connection.LocalEndPoint).Address; - lSession.PassiveServer.Open(); + if (!lSession.PassiveServer.Open()) + { + e.Connection.WriteLine("421 Can't create socket"); + return; + } } lSession.Passive = true; Byte[] lAddress; #if FULLFRAMEWORK - lAddress = ((IPEndPoint)lSession.PassiveServer.Binding.ListeningSocket.LocalEndPoint).Address.GetAddressBytes(); + lAddress = lSession.PassiveServer.GetLocalEndpointAddress().GetAddressBytes(); #endif #if COMPACTFRAMEWORK - IPAddress lIPAddress = ((IPEndPoint)lSession.PassiveServer.Binding.ListeningSocket.LocalEndPoint).Address; + IPAddress lIPAddress = lSession.PassiveServer.GetLocalEndpointAddress(); String[] lIPAddressstr = lIPAddress.ToString().Split(new Char[] {'.'}); lAddress = new Byte[lIPAddressstr.Length]; for (Int32 i = 0; i < lIPAddressstr.Length; i++) diff --git a/Source/RemObjects.InternetPack/SimpleServer.cs b/Source/RemObjects.InternetPack/SimpleServer.cs index 84cbcf7..6f71df7 100644 --- a/Source/RemObjects.InternetPack/SimpleServer.cs +++ b/Source/RemObjects.InternetPack/SimpleServer.cs @@ -6,12 +6,17 @@ Using this code requires a valid license of the RemObjects Internet Pack which can be obtained at http://www.remobjects.com?ip. ---------------------------------------------------------------------------*/ +using System; using System.Net; +using System.Net.Sockets; +using System.Runtime.CompilerServices; namespace RemObjects.InternetPack { public class SimpleServer { + public static readonly SimplePortPool Pool = new SimplePortPool(); + public static IPAddress LocalEndpointIpAddress; public SimpleServer() { this.fBinding = new ServerBinding(); @@ -29,10 +34,27 @@ public ServerBinding Binding private ServerBinding fBinding; #endregion - public void Open() + public bool Open() { - this.Binding.BindUnthreaded(); - this.Binding.ListeningSocket.Listen(this.Binding.MaxWaitConnections); + var retries = 3; + while (retries > 0) + { + try + { + this.Binding.Port = Pool.GetNextSocketPort(); + this.Binding.BindUnthreaded(); + this.Binding.ListeningSocket.Listen(this.Binding.MaxWaitConnections); + break; + } + catch (SocketException) + { + this.Binding.Unbind(); + } + + --retries; + } + + return retries > 0; } public void Close() @@ -44,5 +66,45 @@ public Connection WaitForConnection() { return this.Binding.Accept(); } + + public IPAddress GetLocalEndpointAddress() + { + return LocalEndpointIpAddress ?? ((IPEndPoint)Binding.ListeningSocket.LocalEndPoint).Address; + } + + public Int32 GetLocalEndpointPort() + { + return ((IPEndPoint) Binding.ListeningSocket.LocalEndPoint).Port; + } + } + + public class SimplePortPool + { + int _minPort = 5000; + int _maxPort = 5010; + int _nextPort; + + public void SetPortRange(int min, int max) + { + if (min >= max) + throw new InvalidOperationException(); + + _minPort = min; + _maxPort = max; + + } + + [MethodImpl(MethodImplOptions.Synchronized)] + public int GetNextSocketPort() + { + if (_minPort > _maxPort) + throw new InvalidOperationException("Max port should be greater than min port"); + + if (_nextPort < _minPort || _maxPort < _nextPort) + _nextPort = _minPort; + var port = _nextPort; + ++_nextPort; + return port; + } } } \ No newline at end of file