Хобрук: Ваш путь к мастерству в программировании

Отключение входящего сокета приводит к повторному чтению старых данных

Я ищу помощь в отключении сокета для прослушивания, пожалуйста. У меня проблема в том, что когда я вызываю Shutdown() непосредственно перед Close(), один из этих двух вызовов, кажется, вызывает фиктивное чтение в сокете последних полученных данных. Как я могу остановить это, пожалуйста?

Мое приложение имеет две пары сокетов, как FTP. Один — это клиент, который подключается к удаленному серверу, а другой — сервер, который прослушивает и принимает второе соединение с удаленного хоста. Это входящее соединение подключено к асинхронному устройству OnReceived для обработки нежелательных данных, поступающих с удаленного хоста.

Все это прекрасно работает, и обе розетки могут оставаться подключенными в течение нескольких дней или недель. Но если что-то пойдет не так, я реагирую тем, что закрываю все и начинаю заново. Во время вызова либо inboundSocket.Shutdown(), либо inbounSocket.Close() (не уверен, что это сложно отладить во втором потоке), я как будто перечитываю последний входящий пакет во входящем сокете. Тогда это вызывает еще больше проблем.

Как я могу отключиться. закрыть, отключить и т.д. и т.п., не заставляя это перечитывать?

Пример кода ниже, урезанный, чтобы показать мельчайшие детали.

Заранее спасибо.

Даниэль

public class TcpIpSenderReceiver
{ 
    /// <summary>
    /// Main thread, main entry point 
    /// </summary>
    public void ConnectAndLogonAndStartReceivingInboundMessages()
    {   
        CreateListenerAndStartListening();
        AcceptInboundSocketAndHookUpDataReceiptCallBack();          
    }

    /// <summary>
    /// Main thread
    /// </summary>
    int CreateListenerAndStartListening()
    { 
        tcpListener = new TcpListener(LocalBoundIpAddress, listeningPort);
        tcpListener.Start();        
    }


    /// <summary>
    /// SECOND thread
    /// </summary>
    void AcceptInboundSocketAndHookUpDataReceiptCallBack()
    {
        int i = 0;
        while (i < 100 && !tcpListener.Pending())
        {
            i++;
            Thread.Sleep(100);
        }
        inboundSocket = tcpListener.AcceptSocket();
        bufferForReceive = new byte[bufferSize];
        WireUpCallbackForAsynchReceive();
    }

    /// <summary>
    /// SECOND thread
    /// </summary>
    void WireUpCallbackForAsynchReceive()
    {
        if (asynchCallbackFunctionForReceive == null)
        {
            asynchCallbackFunctionForReceive = new AsyncCallback(OnDataReceived);
        }
        if (inboundSocket.Connected)
        {
            try
            {
                asyncResultForReceive = inboundSocket.BeginReceive(bufferForReceive, 0, bufferForReceive.Length, SocketFlags.None, asynchCallbackFunctionForReceive, null);
            }
            catch (Exception)
            {
                //...
            }
        }
    }


    /// <summary>
    /// SECOND thread
    /// </summary>
    void OnDataReceived(IAsyncResult asyn)
    {
        // Read data call goes here.....

        if (asyncResultForReceive != null)
        {
            inboundSocket.EndReceive(asyncResultForReceive);  
        }
        WireUpCallbackForAsynchReceive();  // listen again for next inbound message
    } 


    void Shutdown()
    {
        shouldAbortThread = true;
        asyncResultForReceive = null;
        asynchCallbackFunctionForReceive = null;
        if (outboundStreamWriter != null)
        {
            try
            {
                outboundStreamWriter.Close();
                outboundStreamWriter.Dispose();
                outboundStreamWriter = null;
            }
            finally { }
        }
        if (outboundNetworkStream != null)
        {
            try
            {
                outboundNetworkStream.Close();
                outboundNetworkStream.Dispose();
                outboundNetworkStream = null;
            }
            finally { }
        }
        if (tcpClient != null)
        {
            try
            {
                tcpClient.Close();
                tcpClient = null;
            }
            catch (SocketException)
            {
                // ...
            }
        }
        if (inboundSocket != null)
        {
            try
            {
                // I think this is where it's puking
                inboundSocket.Shutdown(SocketShutdown.Both);
                inboundSocket.Close();
                inboundSocket = null;
            }
            catch (SocketException)
            {
                //...
            }
        }
        if (tcpListener != null)
        {
            try
            {
                tcpListener.Stop();
                tcpListener = null;
            }
            catch (SocketException)
            {
                //...
            }
        }
    }


    #region Local variables

    volatile bool shouldAbortThread;
    TcpListener tcpListener;
    TcpClient tcpClient;
    StreamWriter outboundStreamWriter;
    NetworkStream outboundNetworkStream;
    Socket inboundSocket = null;
    IAsyncResult asyncResultForReceive;
    public AsyncCallback asynchCallbackFunctionForReceive;
    byte[] bufferForReceive;
    static string HostnameShared;
    #endregion
}
19.03.2013

  • Предполагать, что сокеты содержат ошибки для базовой функциональности, обычно неправильно... Ваш код неисправен, а не их. 19.03.2013

Ответы:


1

В OnDataReceived выглядит (из комментария и того факта, что вы игнорируете возвращаемое значение EndReceive), что вы обрабатываете данные в буфере до вызова EndReceive в inboundSocket. Если это так, у вас нет указаний на количество байтов, которые были фактически прочитаны из сокета (которое, вероятно, будет равно 0, если сокет закрывается), и поэтому вы обрабатываете данные, все еще находящиеся в буфере из предыдущее чтение, отсюда и появление повторного чтения старых данных.

Вам необходимо вызвать EndReceive перед попыткой обработки каких-либо данных. Что-то вроде:

void OnDataReceived(IAsyncResult asyn)
{
    var bytesRead = inboundSocket.EndReceive(asyn);
    if (bytesRead == 0) return; // Socket is closed

    // Process the data here

    WireUpCallbackForAsynchReceive();  // listen again for next inbound message
} 

Кроме того, тот факт, что вы не проверяли возвращаемое значение EndReceive, предполагает, что вы ожидаете, что каждый прием вернет либо данные всего буфера, либо, возможно, то же количество байтов, которое было отправлено сервером при вызове Send/BeginSend, что бывает не всегда.

19.03.2013
  • Иридиум, да, все верно, я считываю и обрабатываю данные до EndReceive. Большое спасибо за четкое объяснение, попробую. 22.03.2013

  • 2

    Я обошел это следующим образом. Вместо того, чтобы предоставлять функцию обратного вызова и позволять фреймворку обрабатывать второй поток, я явно запустил свой собственный рабочий поток и позволил ему заблокироваться до тех пор, пока не будут получены данные. Если получено ноль байтов, я знаю, что другой конец отключается, и могу красиво завершить мой конец. Единственная проблема, которой я хотел поделиться, заключалась в том, что мне нужно было попытаться обойти блокирующую функцию socket.Receive() и обработать исключение SocketError.Interrupted. Это происходит, когда основная программа завершается.

    Код выглядит примерно так:

            Thread dataReceivedWorkerThread;
    
        void AcceptInboundSocketAndStartWorkerThread()
        {            
            inboundSocket = tcpListener.AcceptSocket();
            dataReceivedWorkerThread = new Thread(new ThreadStart(ListenForAndWaitToReceiveInboundData));
            dataReceivedWorkerThread.Start();
        }
    
        void ListenForAndWaitToReceiveInboundData()
        {
                var bytesRead = 0;
                try
                {
                    bytesRead = inboundSocket.Receive(bufferForReceive, 0, bufferForReceive.Length, SocketFlags.None); // blocks here                   
                }
                catch (SocketException se)
                {
                    if (se.SocketErrorCode == SocketError.Interrupted) 
                    {
                        // Handle shutdown by (e.g.) parent thread....                      
                    }
                    else
                    {
                        // handle other
                    }
                    return;
                }
                if (bytesRead == 0)
                {
                    // handle shutdown from other end
                    return;
                }
    
                // Do stuff with received data....
            }
        }
    
    03.01.2014
    Новые материалы

    Учебные заметки JavaScript Object Oriented Labs
    Вот моя седьмая неделя обучения программированию. После ruby ​​и его фреймворка rails я начал изучать самый популярный язык интерфейса — javascript. В отличие от ruby, javascript — это более..

    Разбор строк запроса в vue.js
    Иногда вам нужно получить данные из строк запроса, в этой статье показано, как это сделать. В жизни каждого дизайнера/разработчика наступает момент, когда им необходимо беспрепятственно..

    Предсказание моей следующей любимой книги 📚 Благодаря данным Goodreads и машинному обучению 👨‍💻
    «Если вы не любите читать, значит, вы не нашли нужную книгу». - J.K. Роулинг Эта статья сильно отличается от тех, к которым вы, возможно, привыкли . Мне очень понравилось поработать над..

    Основы принципов S.O.L.I.D, Javascript, Git и NoSQL
    каковы принципы S.O.L.I.D? Принципы SOLID призваны помочь разработчикам создавать надежные, удобные в сопровождении приложения. мы видим пять ключевых принципов. Принципы SOLID были разработаны..

    Как настроить Selenium в проекте Angular
    Угловой | Селен Как настроить Selenium в проекте Angular Держите свое приложение Angular и тесты Selenium в одной рабочей области и запускайте их с помощью Mocha. В этой статье мы..

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..