Погода: -12°C
Samara24.Форум /Компьютеры Интернет Связь / Программирование /

При закрытии сокета срабатывает коллбэк. C#

  • Здравствуйте.
    У меня есть вопрос по сокетам в .NET (старая история:улыб:
    В общем, я создаю на сервере сокет, начинаю им "слушать". Когда клиент цепляется к этому сокету - срабатывает коллбэк, указанный в BeginAccept; мы достаем с помощью EndAccept рабочий сокет и обмениваемся данными с клиентом.
    Теперь, если серверу вдруг нужно закрыть слушающий сокет (неважно уже, подсоединен клиент или нет), то при вызове Close() снова срабатывает коллбэк, однако, сокет уже разрушен, и я получаю ObjectDisposedException при попытке EndAccept, что не удивительно.
    Вопрос. Как закрывать нормально сокет?
    Shutdown и Disconnect падают с ошибкой "10057 WSAENOTCONN". Это тоже меня не удивляет :). Ведь сокет только слушает и выдает другие сокеты для обмена данными с клиентами, а сам никуда не коннектился. Конечно, можно отлавливать эти исключения или при входе в коллбэк проверять значение некоего маркера и выходить из него если маркер говорит об отключенном сокете.
    В общем, посоветуйте, как правильно разделаться с закрытием?

    Правило №2: не смотреть в телескоп на солнце оставшимся глазом.

  • Я завожу свое свойство disconnected, при вызове клоуза выставляю это свойство как флаг, чтобы не обращаться к этому сокету

  • Значит, все-таки маркером обойтись. Я так и поступал, но думал, может все-таки есть "красивый" способ этого избежать.

    Правило №2: не смотреть в телескоп на солнце оставшимся глазом.

  • Кстати, про птичек.
    Читал на днях книгу по системному программированию под Win32, сетевое программирование в том числе. Автор забугорный, описывает функции WinAPI с точки зрения юниксоида, но это так, к слову. Так вот, там утверждается, что прослушиваемый сокет закрывать (который listen(Socket,...)) низзя до завершения работы программы, и во всех примерах этот сокет закрывается при WSACleanup(). Я пробовал под дебаггером Visual C++ 2005 вручную закрыть прослушивающий сокет и отловить на нем событие FD_CLOSE (при асинхронной работе, на оконных сообщениях), управление туда не передавалось ни про shutdown(), ни при closesocket()

  • Странно. Если я вызываю "слушателю" Close(), то (неважно как обойдя все эти колбэки) сокет действительно закрывается и можно создать новый объект и снова заставить его "слушать". Например привязать это к нажатию кнопок "Start server" и "Stop server". И в сетевом экране пропадает "прослушка" порта. Т.е. вроде как сокет действительно разрушается.

    Правило №2: не смотреть в телескоп на солнце оставшимся глазом.

  • [offtopic]
    В ответ на: ... Автор забугорный, описывает функции WinAPI с точки зрения юниксоида, но это так, к слову. ...
    А вы думаете откуда у мелкомягких появился стек протоколов TCP/IP?
    [/offtopic]

    Non solum oportet, sed etiam necessese est

  • В ответ на: Shutdown и Disconnect падают с ошибкой "10057 WSAENOTCONN"
    не уверен, что это так пишется в C#, но в дельфях была конструкция:
    try /* попробовать */
    закрыть/открыть что-то - сделать критичную системную операцию
    except /* исключение */
    а вот тут мы ловим ошибку, и начинаем анализировать, почему нормально операция не произвелась, возможно определяем другой способ освобождения ресурсов, открытия/закрытия устройств, прочей чешуе, памятую что объектная модель на уровнях абстракции типа "сокет" не может преугадать все ответы ОС и оборудования.
    end; /* except */
    Кстати, обработка исключений - то, чем программисты-одиночки-любители часто пренебрегают, и очень зря.

    Думая похожая конструкция должна быть в C#

    Non solum oportet, sed etiam necessese est

  • Спасибо, вы все правильно сказали, в C# действительно есть обработка исключений try/catch и я ей пользуюсь :). И, конечно же, у меня работа с сокетом заключена в try, но отлавливаю я SocketException. Конечно, я могу после поставить и catch(ObjectDisposedException ex) и обрабатывать это исключение, но мой вопрос заключался в том, чтобы исключить срабатывание колбэка и, как следствие, обращения в колбэке к уничтоженному сокету. Обычно, после отлова всех ожидаемых исключений, я ставлю catch(Exception ex) и записываю его в лог (эта конструкция перехватывает абсолютно все исключения). Просто мне казалось, что возможно есть способ закрыть сокет таким образом, чтобы он не генерил колбэк.

    Правило №2: не смотреть в телескоп на солнце оставшимся глазом.

  • В ответ на: Т.е. вроде как сокет действительно разрушается.
    Только что попробовал прилепить кнопку и жестоко заткнуть слушающий сокет. Действительно, закрывается и сообщение о закрытии не поступает в обработчик. Вопрос только, насколько эта операция корректна и не будет ли проблем типа "утечки ресурсов" при долгой работе программы

  • В ответ на: А вы думаете откуда у мелкомягких появился стек протоколов TCP/IP?
    А я вроде и не утверждал, что это именно они его придумали:улыб: В книге честно написано UNIX -> Bercley (вроде так пишется, если нет, извиняюсь :o) sockets -> Windows sockets

  • В ответ на: сообщение о закрытии не поступает в обработчик. Вопрос только, насколько эта операция корректна и не будет ли проблем типа "утечки ресурсов" при долгой работе программы
    Это как? Т.е. коллбэк у вас не запускается при закрытии? Или вы BeginAccept не делали?
    Про "утечку ресурсов", в .NET есть сборщик мусора, может он и не "само совершенство", но благодаря ему, по идее, не должно возникать утечек.

    Правило №2: не смотреть в телескоп на солнце оставшимся глазом.

  • На С++
    Пишу портмаппер.
    Упрощенно так:
    SOCKET lstsock;
    lstsock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    bind(lstsock,...);
    listen(lstsock,SOMAXCON);
    WSAAsyncSelect(lstsock,hMainWnd,WM_ASYNC_CLIENTEVENT,FD_ACCEPT|FD_READ|FD_CLOSE);
    в обработчике сообщения WM_ASYNC_CLIENTEVENT, когда FD_ACCEPT:
    SOCKET newsock = accept(lstsock,NULL,NULL);
    При этом newsock наследует параметры lstsock, в том числе обработчик сообщения WM_ASYNC_CLIENTEVENT.
    Так вот, в этот самый обработчик управление прекрасно заходит, когда к программе "стучатся" со стороны в слущающий сокет, во второй, в третий раз и т.д. Заходит, когда закрывается newsock, программно через shutdown() или со стороны. Естественно, еще когда данные приходят. А вот когда closesocket(lstsock) или shutdown(lstsock) - управление туда не передается :шок:

  • Ну так по идее это правильно. Делаем Shutdown - объект не уничтожается. Close - уничтожается. Заходить вроде бы не должен. А у меня - заходит :(. А если я "слушателю" делаю сначала Shutdown - он падает (выше написано). Поэтому приходится либо отлавливать в обработчике исключение, связанное с обращением к уничтоженному объекту, либо пользоваться флагом.

    Правило №2: не смотреть в телескоп на солнце оставшимся глазом.

  • Видимо, тонкости C#, возможно, даже связанные с тем самым "уборщиком мусора". Я бы посоветовал сходить на www.rsdn.ru, там много квалифицированного народа общается.

  • Я видел подобный вопрос на форуме www.gotdotnet.ru, но внятно топикстартеру никто ответить не смог. Попробую покопать на RDSN, хотя это скорее вопрос эстетики кода, чем реальной проблемы:улыб:

    Правило №2: не смотреть в телескоп на солнце оставшимся глазом.

  • Я бы пользовался try/except со флагами...
    Уборка мусора, чем так славятся компиляторы "высокого уровня" решает тривиальные задачи из условий общности эксепшнов стандартных объектов.
    С точки зрения обычной ОСи открытие сокета и привязка к нему процедуры обработки - естественное логичное событие, причин для сохранения саллбэк-методов после уничтожения (закрытия сокетов) в С дот Нет да и во многих языках нету, но алгоритмизм Си дот Нет позволяет иметь с точки зрения ОСи закрытый сокет и не освобожденный код/память. Научите изучать Perl, там после привязки процедуры к сокету нет варианта его отвязать без закрытия сокета, что у вас и получается. Вывод - не все мягкое что не твердое. Если я на Perl'е открываю сокет и привязываюсь (bind) к нему - отвязатся я могу лишь после его закрытия, а если отвязался закрыв сам сокет - мой код, привязанный к обработке событий сокета никогда больше не выполнится. Почему так происходит в дот-нете - есть только предположение, что сей продукт был собарн в очень большой степени абстракции.
    В большинстве случаев операция "открытия сокета" и его привязки интерпретируется компиляторами более высокого уровня как одна операция. Если вы закрыли сокет, то - сборщик мусора еще не получил извещения что код не нужен (потому что это неизвестно по умолчанию - если есть код, то никто кроме автора не решит нужен он или нет), либо в целях сборки мусора собирал код и обрашение с памятью произошла неотработка "возврата указателя", перехваченного осборщиком.
    Но это сложно, из простого - используйте флаг и обработку исключений, из сложного не используйте дот-нет /* имхо, не пинать =)) */
    в общем много и сумбурно, попрошу не пинать и воспринимать это как поток сознания, который в печатной форме не могу объяснить =))

    Non solum oportet, sed etiam necessese est

  • Спасибо вам, в очередной раз:улыб:В принципе, я так и делал, но, повторюсь, думал, что есть корректный способ закрывания, без лишнего "дергания" колбэка.
    Оффтоп: в .NET сборщик мусора работает тогда, когда считает нужным :). Конечно, можно явно вызвать сборку мусора, но даже тогда я не могу быть уверенным, что мусор будет убран. Но, тем не менее, несмотря на это, .NET весьма и весьма крут:улыб:

    Правило №2: не смотреть в телескоп на солнце оставшимся глазом.

  • =)) оффтопик:

    php программисы настолько суровы, что их уверенность в выполнении кода в одной придуманной автором локали (цп1251, кои8р, 866-я) не подразумевает выполнения в мускуле set names в принципе и явный вызов деструкторов объектов в частности =))
    Не скажу что я опытный кодописатель, но практика показывает, что для тривиальных задач "сборщики" высокого уровня работают процентов на 80, позволяя экономить процентов 200 времени написания кода, но в нетривиальных ситуациях нужно смотреть лично, "чтобы код не тёк" =))

    Non solum oportet, sed etiam necessese est

  • А почему это при вызове Close срабатывает калбэк, на BeginAccept ? Может быть сразу по месту Close надо позаботиться, чтобы не вызывать BeginAccept ? Скорее всего вы закрыли сокет в каком то потоке, но в этот момент произошел вызов BeginAccept в другом потоке и ему был передан уже дохлый сокет.

  • Нет, к сожалению:улыб:Колбэк действительно вызывается, причем не только в случае с BeginAccept, но и с BeginReceive, например.
    приведу код, который использовался для теста. Это простейшая форма с тремя кнопками button1, button2, button3. Первая инициирует прослушивание порта (создаю "слушающий" сокет", вторая - подсоединяет новый сокет к этому порту (метод Connect), третья закрывает "слушателя".
    public partial class Form1 : Form
    {

    Socket acceptor = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    Socket connector = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    Socket sender;

    public Form1()
    {
    InitializeComponent();
    acceptor.Bind(new IPEndPoint(IPAddress.Loopback, 12000));
    acceptor.Listen(1);
    }

    private void button1_Click(object sender, EventArgs e)
    {
    //Начинаем слушать
    acceptor.BeginAccept(new AsyncCallback(OnAccepted), null);
    }

    private void button2_Click(object sender, EventArgs e)
    {
    connector.Connect(new IPEndPoint(IPAddress.Loopback, 12000));
    }

    private void OnAccepted(IAsyncResult result)
    {
    //Соединение установлено, сокет для обмена данными создан
    sender = acceptor.EndAccept(result);//Вот здесь возникает исключение, т.к. уже был вызван Close().
    //BeginAccept не вызывался больше ни в одном потоке, т.к. их нет:улыб:


    sender.Close();
    //Сокет для обмена данными уничтожен, продолжаем слушать дальше
    acceptor.BeginAccept(new AsyncCallback(OnAccepted), null);
    }


    void Button3Click(object sender, EventArgs e)
    {
    //Закрываем слушателя
    acceptor.Close();
    }
    }
    Прошу прощения за плохую читабельность - видимо парсер обрезает табы

    Правило №2: не смотреть в телескоп на солнце оставшимся глазом.

Записей на странице:

Перейти в форум

Модератор: