Как закрыть клиент соединение сокета в объекте после того как он потерялся в куче?
Такой вот вопрос. У меня есть класс, допустим, Device. Этот класс предоставляет функционал взаимодействия с IoT-устройством("умная" вещь) и реализует его API. Каждый экземпляр этого класса представляет отдельное устройство. Удаленное устройство имеет два режима:
1) когда оно сервер - ограниченный режим, плохой режим, но устройство к нему автоматически переходит при выключении или перехвате его другим приложением.
2) когда оно клиент. То что мне нужно. Но приходится ловить соединения. И вот тут проблема.
Мой класс Device имеет статический Socket Server, который (после отправки "прямого" запроса устройству на подключение) делает Accept() в его экземпляре Device и инициализирует его поле Socket Client.
А вот и сама проблема. У меня есть класс Discoverer который в другом потоке каждые 5 сек. возвращает ссылку на новую коллекцию обнаруженных устройств. Старая коллекция уходит в небытие, а до Dispose() в котором я убиваю соединение, видимо, дело не доходит пока сборщик мусора не проснется. Таким образом при создании коллекции новых Device каждые 5 сек. я имею у них Socket Client == null, а соединения, видимо, открыты, т. к. следующий Accept() приводит программу в ступор.
Я могу, конечно, обновлять коллекцию выборочно, не создавая новую и сохраняя актуальные экземпляры Device, будучи уверенным что отвалившиеся устройства точно отсоеденены. Но ведь это решение проблемы извне. И это не reusable по отношению к слассу Device. Можно ли решить этот вопрос внутри Device? Ведь иначе могут всплыть внезапные ситуации в будущем. Код для наглядности:
В Discoverer создается коллекция
FoundDevices = DiscoverDevices(); //Создается коллекция обнаруженных устройств
if(CheckDevicesOnlineChanged()) //Сравнивает коллекции. Косячный метод(всегда true) помог увидеть проблему
{
DeviceCollectionChenged?.Invoke(FoundDevices); //классу-подписчику возвращается новая коллекция
}
В Device каждый раз при создании
private Socket Client = null;
При каждой отправке команды устройству форсится TCP подключение (2-й режим устройства)
private void SendCommand(string command)
{
lock (CommandLocker)
{
byte[] comandBuffer = Encoding.UTF8.GetBytes(command);
{
try
{
Client.Send(comandBuffer); //попытка не пытка
}
catch (Exception)
{
Connect(); //пересоединим обратно
Client.Send(comandBuffer); //попробуем снова
}
}
}
}
И тут на Accept() приходит осознание, что поциент скорее жив, чем мертв. Он просто чем-то занят.
public void Connect()
{
if (Client == null) // а он каждые 5 сек. "null", т.к. объект все время создется заново
{
SendRequestForConnection(); //просим устройство к нам подключится
Client = TcpServer.Accept(); //Ждем тепловой смерти вселенной при повторном вызове
}
IsConnected = true;
}
Я уже не знаю что писать в Dispose(). Он, видимо, просто не вызывается. Удаленное устройство должно отключаться само при разрыве соединения. Если бы это вызывалось, проблем бы не было.
public void Dispose()
{
Client.Close();
Client.Dispose();
}