【问题标题】:C# TCP client read occasionally receives no data - 'nothing'C# TCP 客户端读取偶尔接收不到数据 - '什么都没有'
【发布时间】:2016-01-26 11:00:44
【问题描述】:

我编写了一个与服务器通信的 TCP 客户端。在一个专门的“监听”线程中,我有如下代码。它应该只在有一些数据时读取数据。 (if (stream.DataAvailable))

奇怪的是,有时我的程序会崩溃,因为流绝对不会读取任何数据。它将返回一个空的string。更奇怪的是,如果我尝试在 handleResponse(string s) 函数中“捕获”一个空字符串,它不会被捕获。

    public void listenForResponses()
    {
        Console.WriteLine ("Listening...");
        while (isConnected == true)
        {
            Thread.Sleep (updateRate);
            String responseData = String.Empty;

            if (stream.DataAvailable) {
                Int32 bytes = stream.Read (data, 0, data.Length);
                Console.WriteLine (" >> Data size = "+data.Length);
                responseData = System.Text.Encoding.ASCII.GetString (data, 0, bytes);
                output = responseData+"";
                handleResponse (output);
            }
            if (isConnected == false) {
                closeConnection ();
            }
        }
    }

public void handleResponse(string msg)
{
    Console.WriteLine ("Received: "+msg); 
    iterateThroughEachCharInString (msg);
    if ((msg != "")&&(msg != null)&&(msg != " ")) {
        JSONDataObject desrlzdResp = JsonConvert.DeserializeObject<JSONDataObject>(msg);

        if ((desrlzdResp.instruction != null)) {
            if (desrlzdResp.instruction == "TestConn") {
                handleTestConn (desrlzdResp);
            } else if (desrlzdResp.instruction == "SceneOver") {
                handleSceneFinished (desrlzdResp);
            }
        }
    }
}

抛出的异常是handleResponse函数的if ((desrlzdResp.instruction != null))行上的System.NullReferenceException

【问题讨论】:

  • 您假设一个 Send() 对应一个 Receive(),但这不是套接字的工作方式。 responseData 可以包含一条消息、多条消息或部分消息。您需要一个成帧协议来区分这些情况,例如通过在您的数据前面加上一个数字,指示后面有多少字节。尝试搜索。
  • 嗯,看来desrlzdResp 为空,所以在JSONDataObject desrlzdResp = JsonConvert.DeserializeObject&lt;JSONDataObject&gt;(msg); 之后添加if (desrlzdResp != null).. 应该可以避免空引用异常
  • @Pikoh 不,那么您将丢失数据。 handleResponse() 可以接收部分或多条消息,导致JsonConvert.DeserializeObject() 失败。 OP 需要确保他们只使用完整的消息调用handleResponse()
  • @CodeCaster 当然你是绝对正确的,你的第一条评论就是要走的路。我的评论只是为了避免NullReference Exception,显然不是解决背后的逻辑:)
  • 您的 json 反序列化工作不正常。在没有看到您的数据的情况下,进一步评论很棘手。我认为成帧协议是由包含长度的消息的前几个字节处理的——尽管可能表明了一种不太基本的成帧机制。您还需要处理少于完整消息的帧。

标签: c# string tcp tcpclient tcp-ip


【解决方案1】:

Network Streams 有一种习惯,即使它们不活跃,也可以发布可用数据。此外,除非发送方事先通告,否则接收方无法知道传入流的长度。

        /// <summary>
        /// Method designed to allow the sending of Byte[] data to the Peer
        /// Because this is using NetworkStreams - the first 4 bytes sent is the data length
        /// </summary>
        /// <param name="TheMessage"></param>
        public void SendBytesToPeer(byte[] TheMessage)
        {

            try
            {
                long len = TheMessage.Length;

                byte[] Bytelen = BitConverter.GetBytes(len);

                PeerStream.Write(Bytelen, 0, Bytelen.Length);
                PeerStream.Flush();
                PeerStream.Write(TheMessage, 0, TheMessage.Length);
                PeerStream.Flush();
            }
            catch (Exception e)
            {
                //System.Windows.Forms.MessageBox.Show(e.ToString());
            }
        }

注意 - 发送方的刷新可能不需要,但我添加它因为它没有害处 - 微软表示刷新对网络流没有任何作用。

因此,此代码将确定您发送的消息的大小,然后在您的“实际”消息之前将其发送给接收者。

        /// <summary>
        /// Incoming bytes are retrieved in this method
        /// </summary>
        /// <param name="disconnected"></param>
        /// <returns></returns>
        private byte[] ReceivedBytes(ref bool disconnected)
        {
            try
            {
                //byte[] myReadBuffer = new byte[1024];
                int receivedDataLength = 0;
                byte[] data = { };
                int len = 0;
                int i = 0;
                PeerStream.ReadTimeout = 15000;


                if (PeerStream.CanRead)
                {
                    //networkStream.Read(byteLen, 0, 8)
                    byte[] byteLen = new byte[8];
                    if (_client.Client.IsConnected() == false)
                    {
                        //Fire Disconnect event
                        if (OnDisconnect != null)
                        {
                            disconnected = true;
                            OnDisconnect(this);
                            return null;
                        }
                    }
                    while (len == 0)
                    {
                        PeerStream.Read(byteLen, 0, 8);

                        len = BitConverter.ToInt32(byteLen, 0);
                    }
                    data = new byte[len];

                    PeerStream.Read(data, receivedDataLength, len);

                    return data;
                }



            }
            catch (Exception E)
            {

                //System.Windows.Forms.MessageBox.Show("Exception:" + E.ToString());
            }
            return null;
        }

此代码将等到接收器检测到传入流长度,然后它会尝试读取该确切长度。 不要担心 OnDisconnect 位 - 这只是我在做的项目中留下的一些代码。您可能需要考虑在 while(len == 0) 循环中添加 Thread.Sleep,以节省 CPU 周期。

【讨论】:

    猜你喜欢
    • 2022-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-06
    • 1970-01-01
    • 1970-01-01
    • 2015-12-21
    相关资源
    最近更新 更多