【问题标题】:Why am I receiving data when my Socket has disconnected? [duplicate]当我的 Socket 断开连接时,为什么我会收到数据? [复制]
【发布时间】:2020-03-15 17:16:25
【问题描述】:

我的套接字有问题。 基本上我有几个监听器监听我电脑上所有可用的端口。当一个套接字连接到其中一个侦听器时,我会打开一个新的 Socket 连接来处理该连接,然后我的侦听器会重新开始侦听。

但是,当客户端断开连接时,我会继续接收空数据。 正如您在下面的代码中看到的,我放置了一个 Console.WriteLine 来检查接收到的消息的长度。 当连接关闭时,它向我显示了超过 100 行 charLen: 0。这意味着它正在接收一些东西,但我不知道它来自哪里。

然后我还会在WaitData-方法中收到System.ObjectDisposedException 错误

        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)]

【问题讨论】:

  • 接收到零字节意味着传输结束。在那之后你不应该尝试接收更多。 OnDataReceived 的代码在哪里?
  • 使用 cmd.exe 检查 >Netstat -a 客户端和服务器上的连接状态,以查看是否已断开连接。仅应从客户端(而不是服务器)关闭连接如果客户端和服务器都尝试同时关闭,则当连接可能最终处于半开半闭状态时,存在竞争条件。连接是 TCP,它需要对每条消息(包括打开和关闭)进行确认。如果客户端和服务器同时关闭,则不会发生其中一个 ACK​​,因为连接的一侧已经关闭。
  • 显示的第一个代码是 onDataReceived。而且我只是从客户端关闭。服务器不会断开连接,它只会在之后清理并将客户端从 connectedClients-list 中删除。
  • 与此同时,我在 SO (stackoverflow.com/questions/9545527/…) 上发现了一个类似的问题,但我不确定如何在我的代码中实现它。

标签: c# sockets asynchronous


【解决方案1】:

我可以在 StackOverflow (https://stackoverflow.com/a/9546493/4425684) 上找到答案

我没有意识到 WaitForData 实际上生成了一个循环,当我收到 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;
        }            
    }

我确实认为这段代码仍然存在问题,因为我认为当接收到大于我的缓冲区的消息时,该消息将不会被完全解析,因为我只处理 @987654324 之后的消息@,但我不完全确定。

现在问题已经解决了,因为我的缓冲区很大,但是如果有人想更正它,请随时。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-01-20
    • 1970-01-01
    • 2019-11-22
    • 1970-01-01
    • 1970-01-01
    • 2020-02-21
    • 2021-04-20
    相关资源
    最近更新 更多