jchristn / SuperSimpleTcp

Simple wrapper for TCP client and server in C# with SSL support
MIT License
459 stars 95 forks source link

Why doesn't Dispose call Stop? #208

Open basprins opened 8 months ago

basprins commented 8 months ago

Hi, We just migrated from SimpleTcp to SuperSimpleTcp and as a result a bunch of unit tests started to fail. They fail because we're trying to bind to a socket which is already used.

System.Net.Sockets.SocketException : Only one usage of each socket address (protocol/network address/port) is normally permitted.
   at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, Boolean disconnectOnFailure, String callerName)
   at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
   at System.Net.Sockets.Socket.Bind(EndPoint localEP)
   at System.Net.Sockets.TcpListener.Start(Int32 backlog)
   at SuperSimpleTcp.SimpleTcpServer.Start()

I can workaround the problem by adding server.Stop() to the Dispose method.

I wonder why this is not done implicitly by your library. Doesn't this leave sockets open when user software starts a server, and disposes the instance and close the app? Wouldn't you expect that when a server instance is disposed it cleans up everything it used?

jchristn commented 6 months ago

Hi, the server-side Dispose does indeed call Stop() against the TcpListener if it is not null. Is there somewhere else?

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                try
                {
                    if (_clients != null && _clients.Count > 0)
                    {
                        foreach (KeyValuePair<string, ClientMetadata> curr in _clients)
                        {
                            curr.Value.Dispose();
                            Logger?.Invoke($"{_header}disconnected client: {curr.Key}");
                        } 
                    }

                    if (_tokenSource != null)
                    {
                        if (!_tokenSource.IsCancellationRequested)
                        {
                            _tokenSource.Cancel();
                        }

                        _tokenSource.Dispose();
                    }

                    if (_listener != null && _listener.Server != null)
                    {
                        _listener.Server.Close();
                        _listener.Server.Dispose();
                    }

                    if (_listener != null)
                    {
                        _listener.Stop();
                    }
                }
                catch (Exception e)
                {
                    Logger?.Invoke($"{_header}dispose exception:{Environment.NewLine}{e}{Environment.NewLine}");
                }

                _isListening = false;

                Logger?.Invoke($"{_header}disposed");
            }
        }