如何从NetworkStream中获取所有数据

17

我正在尝试读取通过 TCP/IP 连接的机器缓冲区中的所有数据,但我不知道为什么没有获取到所有数据,有些数据丢失了。 以下是我正在使用的代码...

using (NetworkStream stream = client.GetStream())
{
    byte[] data = new byte[1024];
    int numBytesRead = stream.Read(data, 0, data.Length);
    if (numBytesRead > 0)
    {
       string str= Encoding.ASCII.GetString(data, 0, numBytesRead);
    }
}

请告诉我我缺少什么以从机器中获取所有数据。 感谢您的提前回复。


1
有没有可能与您明确地最多读取1024字节有关? - decPL
1
到底缺少什么?流的开头,结尾还是随机字节?你能从字节序列中识别出流的结尾吗?你知道数据的预期长度吗? - Aik
1
不,它不能是任何东西。如果你不想等到客户端关闭连接,你需要定义一个协议。否则 - 一直读取直到客户端关闭连接。 - CodeCaster
@CodeCaster 我的机器具有将数据存储到缓冲区的功能,如果没有客户端连接/监听机器。因此,我的要求是,如果我连接到机器,在这种情况下,无论机器缓冲区中有什么/所有数据,我的代码都应该能够读取它... - shubham Hegdey
@shubham Hegdey,你确定数据确实丢失了吗?问题也可能出在Encoding.ASCII.GetString上。你是在传输文本吗?这段文本是否以ASCII编码(仅包含128个字符)? - Aik
显示剩余7条评论
8个回答

28

你的代码问题在于如果数据大小超过缓冲区大小(在你的情况下是1024字节),你将无法获取所有数据,因此你必须在循环内部读取流。然后,你可以在MemoryStream中写入所有数据,直到NetworkStream的结尾。


      string str;
      using (NetworkStream stream = client.GetStream())
      {
            byte[] data = new byte[1024];
            using (MemoryStream ms = new MemoryStream())
            {

                int numBytesRead ;
                while ((numBytesRead = stream.Read(data, 0, data.Length)) > 0)
                {
                    ms.Write(data, 0, numBytesRead);


                }
               str = Encoding.ASCII.GetString(ms.ToArray(), 0, (int)ms.Length);
            }
        }

1
不,你将会得到整个字符串,它是在你读取NetworkStream时存储的。 - George Chondrompilas
你的意思是说缓冲区中所有可用的数据,我理解得对吗? - shubham Hegdey
我在 while (numBytesRead = stream.Read(data, 0, data.Length) > 0) 处遇到错误,无法将 bool 转换为 int,并且 buffer 在当前上下文中不存在。代码的最后一行 Encoding.ASCII.GetString(ms.ToArray(), 0, ms.Length); 也出现了错误。 - shubham Hegdey
好的,谢谢。但是现在在 Encoding.ASCII.GetString(ms.ToArray(), 0, ms.Length); 处仍然出现错误,最佳重载方法有一些无效的参数。请检查。 - shubham Hegdey
7
请注意,这个解决方案会在 using 段结束后关闭连接。 - FoKycHuK
显示剩余14条评论

11

这个示例来自于MSDN: NetworkStream.DataAvailable,展示如何使用该属性实现此功能:

// Examples for CanRead, Read, and DataAvailable. 
// Check to see if this NetworkStream is readable. 
if(myNetworkStream.CanRead)
{
    byte[] myReadBuffer = new byte[1024];
    StringBuilder myCompleteMessage = new StringBuilder();
    int numberOfBytesRead = 0;

    // Incoming message may be larger than the buffer size. 
    do{
         numberOfBytesRead = myNetworkStream.Read(myReadBuffer, 0, myReadBuffer.Length);

         myCompleteMessage.AppendFormat("{0}", Encoding.ASCII.GetString(myReadBuffer, 0, numberOfBytesRead));

    }
    while(myNetworkStream.DataAvailable);

    // Print out the received message to the console.
    Console.WriteLine("You received the following message : " +
                                 myCompleteMessage);
}
else
{
     Console.WriteLine("Sorry.  You cannot read from this NetworkStream.");
}

这个例子是做什么的?NetworkStream.DataAvailable 对 OP 的目的可靠吗?它何时变为 false - CodeCaster
当套接字中没有更多数据时,I 变为 false,但这种情况为什么不可靠? - dariogriffo
1
不,NetworkStream.DataAvailablefalse 只表示接收方的接收缓冲区中没有数据。这并不意味着发送方已经完成发送,因此它不能用于实现协议,也无法解决 OP 的问题。 - CodeCaster
2
读一下自己说的话,你在说我是对的,他要求读取流中的所有数据。你比他更进了一步,我同意你的看法,他需要实现一个协议,但这不是问题所在,他问的是如何读取流中的所有数据。 - dariogriffo
1
我非常怀疑那不是 OP 想要的,但你说得对。 - CodeCaster
我也是,他会提出一个新问题,最好自己尝试理解并努力解决,我们都曾经历过这个过程 :D - dariogriffo

2

尝试使用以下代码:

using (NetworkStream stream = client.GetStream())
    {
         while (!stream.DataAvailable)
         {
             Thread.Sleep(20);
         }
        
         if (stream.DataAvailable && stream.CanRead)
         {
              Byte[] data = new Byte[1024];
              List<byte> allData = new List<byte>();
        
              do
              {
                    int numBytesRead = stream.Read(data,0,data.Length);
    
                    if (numBytesRead == data.Length)
                    {
                         allData.AddRange(data);
                    }
                    else if (numBytesRead > 0)
                    {
                         allData.AddRange(data.Take(numBytesRead));
                    }                                    
               } while (stream.DataAvailable);
          }
    }
      

希望这能帮助到你,它可以防止你错过任何发送给你的数据。

2

试试这个:

 private string GetResponse(NetworkStream stream)
    {
        byte[] data = new byte[1024];
        using (MemoryStream memoryStream = new MemoryStream())
        {
            do
            {
                stream.Read(data, 0, data.Length);
                memoryStream.Write(data, 0, data.Length);
            } while (stream.DataAvailable);

            return Encoding.ASCII.GetString(memoryStream.ToArray(), 0, (int)memoryStream.Length);
        }
    }

1
我会将第8行和第9行替换为var readCount = stream.Read(data, 0, data.Length);memoryStream.Write(data, 0, readCount);。这样就不会出现尾随的0字节,这些字节会转换成输出字符串中的"\0"空字符。 - Petru Zaharia

1
同步方法有时无法显示请求正文。使用异步方法可以稳定地显示请求正文。
string request = default(string);
StringBuilder sb = new StringBuilder();

byte[] buffer = new  byte[client.ReceiveBufferSize];
int bytesCount;

if (client.GetStream().CanRead)
{
    do
    {
        bytesCount = client.GetStream().ReadAsync(buffer, 0, buffer.Length).Result;
        sb.Append(Encoding.UTF8.GetString(buffer, 0, bytesCount));
    }
    while(client.GetStream().DataAvailable);

    request = sb.ToString();
}

0

对于我的情况,消息本身告诉了后续消息的长度。以下是代码:

 int lengthOfMessage=1024;
 string message = "";
 using (MemoryStream ms = new MemoryStream())
 {
      int numBytesRead;
      while ((numBytesRead = memStream.Read(MessageBytes, 0, lengthOfMessage)) > 0)
      {
            lengthOfMessage = lengthOfMessage - numBytesRead;
            ms.Write(MessageBytes, 0, numBytesRead);
      }
      message = Encoding.ASCII.GetString(ms.ToArray(), 0, (int)ms.Length);
 }

0

TCP本身没有任何定义“数据结束”的方式。这是应用层协议的责任。

例如,参见HTTP请求描述:

客户端请求(在本例中仅由请求行和一个标题字段组成)后跟一个空行,以便请求以双重换行符结束

因此,对于请求,数据结束由两个换行符序列确定。而对于响应:

Content-Type指定HTTP消息传递的数据的Internet媒体类型,而Content-Length指示其以字节为单位的长度。

响应内容大小在数据之前的标头中指定。因此,您可以自行编码一次传输的数据量 - 它可以只是在数据开头的前2或4个字节中保持总大小以读取,或者如果需要更复杂的方式。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接