在这种情况下,正确的方法是避免ObjectDisposedException?

时间:2018-01-23 12:40:56

标签: c# .net error-handling dispose

我目前在下一行收到ObjectDisposedException。

var client = ((Socket) asyncResult.AsyncState).EndAccept(asyncResult);
  

System.ObjectDisposedException:'无法访问已处置的对象。   对象名:'System.Net.Sockets.Socket'。'

我只是想知道,避免这样的错误的正确方法(就最佳实践而言)是什么?我不确定如何处理它,我如何检查它是否在手边处理,但这是我应该做的?或者检查别的东西。

我是自学C#所以我从来没有学到这样的东西,有人可以提供一些见解吗?

以下是完整的课程:

internal sealed class SocketHandler : IDisposable
{
    private static readonly ILogger Logger = LogManager.GetCurrentClassLogger();

    private readonly Socket _serverSocket;

    public SocketHandler()
    {
        _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        Load();
    }

    public void Dispose()
    {
        _serverSocket?.Close(); // close also calls dispose...
    }

    private void Load()
    {
        var config = Program.Server.ConfigHandler;

        _serverSocket.Bind(new IPEndPoint(IPAddress.Any, config.GetConfigValueByKey("network.sockets.port").ToInt()));
        _serverSocket.Listen(int.Parse(config.GetConfigValueByKey("network.sockets.backlog")));
        _serverSocket.BeginAccept(OnAcceptConnection, _serverSocket);
    }

    private void OnAcceptConnection(IAsyncResult asyncResult)
    {
        try
        {
            if (_serverSocket == null)
            {
                return;
            }

            var client = ((Socket) asyncResult.AsyncState).EndAccept(asyncResult);

            var playerHandler = Program.Server.BaseHandler.PlayerHandler;
            var players = playerHandler.Players;

            var config = Program.Server.ConfigHandler;

            var maxConnections = int.Parse(config.GetConfigValueByKey("game.players.limit"));
            var maxConnectionsPerIp = int.Parse(config.GetConfigValueByKey("game.players.ip_limit"));

            if (players.Count >= maxConnections)
            {
                Logger.Warn("Incoming connection was refused because the player limit was exceeded.");

                client.Shutdown(SocketShutdown.Both);
                client.Close();

                return;
            }

            if (players.Values.Count(x => x != null && !x._disconnected && x.getIp() == client.RemoteEndPoint.ToString().Split(':')[0]) > maxConnectionsPerIp)
            {
                Logger.Warn("Incoming connection was refused because the IP limit was exceeded.");

                client.Shutdown(SocketShutdown.Both);
                client.Close();

                return;
            }

            var clientId = Randomizer.Next(1, 10000);

            Program.Server.BaseHandler.PlayerHandler.TryAddPlayer(clientId, new Player(clientId, client, new InitialPacketParser()));
        }
        catch (SocketException socketException)
        {
            Logger.Fatal(socketException, "Failed to accept socket connection.");
        }
        finally
        {
            _serverSocket?.BeginAccept(OnAcceptConnection, _serverSocket);
        }
    }
}

2 个答案:

答案 0 :(得分:0)

据我所知,reference source Dispose并非_serverSocket本身。因此,由于您的privateOnAcceptConnection(),因此您是唯一一个在处置时控制的人。

您的Dispose()方法已经开始尝试检查,但不是完全。

Close()方法(或Dispose()_serverSocket _serverSocket}的任何其他位置,您还需要将null设置为{{1} }}。您可以通过线程安全的方式执行此操作:

public class SocketHandler
{
    private Socket _serverSocket; // not read-only

    /* ... */

    public void Dispose()
    {
        Socket tmp = _serverSocket; // save instance
        _serverSocket = null; // set field to null
        tmp?.Close();
    }

现在,OnAcceptConnection()在检查if (_serverSocket == null)时会返回,并且您可以避免异常。

答案 1 :(得分:-1)

问题也可能来自你如何使用SocketHandler。我可以看到你的类中如何实现IDisposable模式没有错。在处理Disposable类之后避免访问它的常规方法是将它包装在using语句中,该语句将在离开块后自动处理该类:

using( SocketHandler handler = new SocketHandler())
{
    (...)
} //handler will be disposed and not accessible after here

有关此Microsoft Docs的详细信息,请article explaining IDispose

修改: 感谢您指出我完全错误地理解了这个问题。

我至少根据我的知识重新创建了场景:

class Program
{
    static void Main(string[] args)
    {
        using (C1 instance = new C1())
        {
            Task.Factory.StartNew(() =>
            {
                Task.Delay(1000);

                bool disposed = (bool)typeof(C1).GetField("disposed", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(instance);

                if (disposed)
                {
                    Console.WriteLine("Already disposed will not call DoSomething()");
                }
                else
                {
                    instance.DoSomething();
                }

            });
        }

        Console.ReadKey(true);
    }
}

class C1 : IDisposable
{
    bool disposed = false;
    public C1()
    {

    }

    public void DoSomething()
    {
        if (disposed)
            throw new ObjectDisposedException("C1");

        Console.WriteLine("Still existing!");
    }


    public void Dispose()
    {
        Dispose(true);

        Console.WriteLine("Disposed!");
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposed)
            return;
        disposed = true;
    }
}

并设法通过反射解决异常问题。假设microsoft将根据其框架设计指南使用相同的模式+ namings,此解决方案至少可以用作解决方法

但我非常怀疑这是最好的方式。