Почему я получаю данные, когда мой сокет отключен? - PullRequest
0 голосов
/ 15 марта 2020

У меня проблема с сокетами. В основном у меня есть несколько слушателей, которые прослушивают все доступные порты на моем p c. Когда сокет подключается к одному из слушателей, я открываю новое Socket-соединение для обработки этого соединения, и мой слушатель возвращается к прослушиванию.

Однако, когда клиент отключается, я продолжаю получать пустые данные. Как видно из кода ниже, я добавил Console.WriteLine, чтобы проверить длину полученного сообщения. Когда соединение закрывается, оно показывает мне более 100 строк с charLen: 0. Это значит, что он что-то получает, но я не знаю, откуда он приходит.

И тогда я также получу ошибку System.ObjectDisposedException в WaitData -методе

        ClientSocketClass tmpClient = (ClientSocketClass)asyn.AsyncState;
        try
        {

            // END THE BeginReceive() ASYNCHRONOUS CALL BY CALLING THE EndReceive() METHOD FOR THAT SOCKET
            // THIS WILL RETURN THE NUMBER OF CHARACTER WHICH HAS BEEN RECEIVED BY THE CLIENT
            int byteMessage = tmpClient.Socket.EndReceive(asyn);

            char[] chars = new char[byteMessage + 1];

            // EXTRACT THE CHARACTERS INTO A BUFFER
            System.Text.Decoder d = System.Text.Encoding.UTF8.GetDecoder();
            int charLen = d.GetChars(tmpClient.BufferSize, 0, byteMessage, chars, 0);

            Console.WriteLine("charlen: " + charLen.ToString());

            // START WAITING AGAIN FOR NEW DATA FROM THE CLIENT
            WaitForData(tmpClient);

            Array.Resize(ref chars, charLen);

            // PROCESS THE CURRENT MESSAGE
            string tempData = new string(chars).Replace("\0", string.Empty);

            // LOG THE RECEPTION OF NEW DATA
            string log = string.Format("{0}{1}Received: {2}", DateTime.Now.ToString("HH:mm:ss.fff"), "\t", tempData);
            Log(LoggingType.Data, LoggingLevel.Debug, log);


            // ADD THE MESSAGE TO THE MESSAGE QUEUE
            if (MessageQueue != null)
            {
                if (tempData != null && tempData != string.Empty)
                {
                    MessageQueue.Add(tempData);
                }
            }
        }
        catch (ObjectDisposedException)
        {
            // THIS CODE WILL BE EXECUTED IF THE SOCKET WAS DISCONNECTED
            if (tmpClient != null)
            {
                // GET THE ID OF THE CLIENT
                int clientId = tmpClient.Id;

                // REMOVE THE CLIENT FROM THE CONNECTED CLIENTS LISTS
                removeClient(clientId);

                string log = string.Format("{0}{1}Client {2} disconnected", DateTime.Now.ToString("HH:mm:ss.fff"), "\t", clientId);
                Log(LoggingType.Status, LoggingLevel.Notification, log);
            }

            return;
        }

Ниже вас см. метод WaitForData:

    public void WaitForData(ClientSocketClass selectedClient)
    {
        try
        {
            if (pfnWorkerCallBack == null)
            {
                // SPECIFY THE CALL BACK FUNCTION WHICH SHOULD BE RUN
                // WHEN DATA IS RECEIVED FROM THE CLIENT
                pfnWorkerCallBack = new AsyncCallback(OnDataReceived);
            }

            // START RECEIVING THE MESSAGE INTO THE DATA BUFFER
            selectedClient.Socket.BeginReceive(selectedClient.BufferSize, 0, selectedClient.BufferSize.Length, SocketFlags.None, pfnWorkerCallBack, selectedClient);
        }
        catch (SocketException ex)
        {
            string log = string.Format("{0}\t<WaitForData>\tAn error occured while waiting for data: {1}{2}", DateTime.Now.ToString("HH:mm:ss.fff"), ex.Message, Environment.NewLine);
            Log(LoggingType.Error, LoggingLevel.Debug, log);
        }
    }

Теперь я начинаю думать, что что-то не так в работе с данными в OnDataReceived. Я комбинировал различные учебники, которые нашел в Интернете, чтобы получить этот код, который сам по себе работает, только когда клиент отключается, я получаю эту ошибку.

Я надеюсь, что кто-то знает, почему.

Чтобы завершить, ниже вы найдете ClientSocketClass, который я использую для организации своих подключенных клиентов.

    public class ClientSocketClass
    {
        private int tmpId;
        private string tmpIp;
        private byte[] tmpBuffer;
        public event PropertyChangedEventHandler PropertyChanged;

        public int Id
        {
            get { return tmpId; }
            set
            {
                tmpId = value;
                this.NotifyPropertyChanged("Id");
            }
        }


        public string Ip
        {
            get { return tmpIp; }
            set
            {
                tmpIp = value;
                this.NotifyPropertyChanged("Ip");
            }
        }

        [Browsable(false)]
        public Socket Socket { get; set; }

        [Browsable(false)]
        public byte[] BufferSize
        {
            get { return tmpBuffer; }
            set
            {
                tmpBuffer = value;
                this.NotifyPropertyChanged("BufferSize");
            }
        }

        private void NotifyPropertyChanged(string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }
    }

Этот класс используется в BindingList, который отображается в DataGridView, чтобы показать подключенные клиенты. Но я показываю только Id и IP-адрес, поэтому [Browsable(false)]

1 Ответ

0 голосов
/ 16 марта 2020

Я смог найти ответ здесь в StackOverflow ({ ссылка })

Что я не понял, так это то, что WaitForData фактически сгенерировал al oop, и мне нужно было сбежать l oop когда я получал NULL.

Итак, просто добавив приведенный ниже код, проблема была решена.

    if (byteMessage <= 0)
    {
        // LEAVE THE LOOP
        return;
    }

После некоторой очистки новый код выглядит следующим образом:

    public void OnDataReceived(IAsyncResult asyn)
    {
        ClientSocketClass tmpClient = (ClientSocketClass)asyn.AsyncState;
        try
        {
            // END THE BeginReceive() ASYNCHRONOUS CALL BY CALLING THE EndReceive() METHOD FOR THAT SOCKET
            // THIS WILL RETURN THE NUMBER OF CHARACTER WHICH HAS BEEN RECEIVED BY THE CLIENT
            int byteMessage = tmpClient.Socket.EndReceive(asyn);

            // READ THE CONTENT
            string tmpContent = Encoding.UTF8.GetString(tmpClient.BufferSize, 0, byteMessage);

            // CHECK IF WE HAVE REACHED THE END OF THE RECEIVED MESSAGE
            if (byteMessage <= 0)
            {
                // LEAVE THE LOOP
                return;
            }

            // START WAITING AGAIN FOR NEW DATA FROM THE CLIENT
            WaitForData(tmpClient);


            // PROCESS THE CURRENT MESSAGE
            string tempData = tmpContent.Replace("\0", string.Empty);

            // LOG THE RECEPTION OF NEW DATA
            string log = string.Format("{0}{1}Received: {2}", DateTime.Now.ToString("HH:mm:ss.fff"), "\t", tempData);
            Log(LoggingType.Data, LoggingLevel.Debug, log);


            // ADD THE MESSAGE TO THE MESSAGE QUEUE
            if (MessageQueue != null)
            {
                if (tempData != null && tempData != string.Empty)
                {
                    MessageQueue.Add(tempData);
                }
            }
        }
        catch (ObjectDisposedException)
        {
            // THIS CODE WILL BE EXECUTED IF THE SOCKET WAS DISCONNECTED
            if (tmpClient != null)
            {
                // GET THE ID OF THE CLIENT
                int clientId = tmpClient.Id;

                // REMOVE THE CLIENT FROM THE CONNECTED CLIENTS LISTS
                removeClient(clientId);

                string log = string.Format("{0}{1}Client {2} disconnected", DateTime.Now.ToString("HH:mm:ss.fff"), "\t", clientId);
                Log(LoggingType.Status, LoggingLevel.Notification, log);
            }

            return;
        }            
    }

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

Пока проблема решена, потому что я сделал свой буфер очень большим, но если кто-то хочет его исправить, пожалуйста, не стесняйтесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...