我目前在下一行收到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);
}
}
}
答案 0 :(得分:0)
据我所知,reference source Dispose
并非_serverSocket
本身。因此,由于您的private
是OnAcceptConnection()
,因此您是唯一一个在处置时控制的人。
您的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,此解决方案至少可以用作解决方法。
但我非常怀疑这是最好的方式。