【问题标题】:Stream.Read is combining two different readsStream.Read 结合了两种不同的读取
【发布时间】:2013-02-23 03:40:18
【问题描述】:

我有一个用 C# 编写的简单文件服务器\客户端应用程序。但我经常遇到我的流将两个不同的读取写入单个缓冲区的问题。我有一个同步的流,仍然没有帮助。有什么建议么?谢谢!

System.Threading.Thread.Sleep(25);
receive_fspos = new byte[30];
int bytesread = stream_1.Read(receive_fspos, 0, receive_fspos.Length);//this is where it gets combined
if (bytesread == 0) 
{ 
    finished = true; 
    System.Threading.Thread.Sleep(25); 
}
string string_1 = utf.GetString(receive_fspos).TrimEnd(new char[] { (char)0 });
int fsposition = (int)Convert.ToInt64(string_1);
bytestosend = fsposition;
filestream.Position = fsposition;
byte[] buffer_1 = new byte[bufsize];
int bytesreadfromfs = filestream.Read(buffer_1, 0, buffer_1.Length);
stream_1.Write(buffer_1, 0, buffer_1.Length);
Console.Write("\rSent " + fsposition + " / " + length + " bytes");
finished = true;

【问题讨论】:

  • 您是说,例如,您将两条 10 字节的消息写入一个流并在另一端接收 20 字节?这就是为什么它被称为流。一端进入的字节以相同的顺序从另一端出来,没有重复或丢失。没有大于字节的实体的概念,即消息记录。读取中接收的字节数取决于整个网络的缓冲。如果你总共写了 100 个字节,你可以从第一次读取得到 43 个字节,从下一个读取得到 56 个字节,然后是一个单独的字节。
  • 不完全是。我已将每次读取的长度设置为哦,比如说 30 字节,每条消息实际上是 8 字节,它在一次调用 stream.read() 方法时将服务器的两个 stream.write() 调用写入缓冲区。它不应该这样做,它应该将 8 字节消息和 22 个空白字节写入一个 30 字节缓冲区。对吗?
  • 抱歉,读取将检索所有可用数据,直到缓冲区大小。缓冲区中的任何剩余字节都不会受到影响,即被清除。如果您想将字节流解释为消息,那么需要创建一种方法来确定每条消息的边界。我使用了一个标头,其中前 2 或 4 个字节包含消息的长度。附加标头字段可能包含消息类型、子类型、id、...。由于字节是按任何大小的组接收的,因此您必须准备好从片段中重新组装消息。连长度字段都可以碎片化!
  • 哈哈我害怕这个,使用UDP的唯一原因是未指定的接收长度。我会想到一些东西谢谢伙计。

标签: c# client client-server filestream


【解决方案1】:

如果您不完全理解,我不建议您编写自己的流方法。

您遇到的问题是因为传入的数据是字节流,无法让您知道消息的长度是多少字节。

在下面的代码中,您声明要读取流的“receive_fspos.Length”字节。由于“receive_fspos.Length”为 30,因此将读取的字节数将在 0 到 30 之间。

如果连接只接收到 15 个字节。它会给你15个字节。如果消息是 20 字节长。然后消息现在被分成不同的段。

如果第一条消息是 4 字节,第二条消息是 12 字节。现在您有 2 条消息和一组 16 个空白字节。更糟糕的是,这 16 个“空白”字节可能是进入流的第三条消息的开始。

如果消息是 50 字节长。那么您将只收到一半的消息。现在您需要将读取的字节添加到单独的缓冲区中。再次从流中读取。然后重复此操作,直到您确定已读取完成整个消息所需的确切字节数。然后将所有读取的字节连接回单个字节[]。

     receive_fspos = new byte[30];
     int bytesread = stream_1.Read(receive_fspos, 0, receive_fspos.Length);//this is where it gets combined

请使用 BCL 方法,而不是滚动您自己的循环。听起来您正在使用字符串,所以这将是首选方法。我建议以下方法。

using(NetworkStream networkStream = tcpClient.GetStream())
using(StreamReader streamReader = new StreamReader(networkStream))
using(StreamWriter streamWriter = new StreamWriter(networkStream))
{
     networkStream.ReadTimeout = timeout; //Set a timeout to stop the stream from reading indefinately           

     //To receive a string
     string incomingString = stream.ReadLine();

     //To send a string
     stream.WriteLine(messageToSend);
     stream.Flush();
}

您的回答表明您正在尝试发送文件。为此,我建议发送一个字节数组 []。使用此方法,您可以发送任何可以序列化的内容。这包括一个文件。请注意,文件的大小是有限的,因为它必须保存在内存中。要写入更大的文件,您需要在数据流式传输时将数据保存在块中。

//Please note that if the file size is large enough. It may be preferred to use a stream instead of holding the entire file in memory.
byte[] fileAsBytes = File.ReadAllBytes(fileName);

using(NetworkStream networkStream = tcpClient.GetStream())
using(BinaryReader binaryReader = new BinaryReader(networkStream))
using(BinaryWriter binaryWriter = new BinaryWriter(networkStream))
{
     networkStream.ReadTimeout = timeout; //Set a timeout to stop the stream from reading indefinately           

     //To receive a byte array
     int incomingBytesLength = BinaryReader.ReadInt32(); //The header is 4 bytes that lets us know how large the incoming byte[] is.
     byte[] incomingBytes = BinaryReader.ReadBytes(incomingBytesLength);

     //To send a byte array
     BinaryWriter.Write(fileAsBytes.Length); //Send a header of 4 bytes that lets the listener know how large the incoming byte[] is.
     BinaryWriter.Write(fileAsBytes);
}

【讨论】:

  • 在我的代码中,我已经设置了它,以便从服务器读取的字节在收到后立即写入文件。感谢您的回答,这种方法看起来比我目前使用的方法稳定得多。我会尽快尝试这个并与您联系。谢谢。
  • hmmmm...似乎没有发生任何事情,我正在使用您发布的发送和接收方法。什么都没有发送。
  • 你创建了 TcpClient 和 TcpListener 吗?在使用此方法之前,请确保您有 Tcp 连接。
  • int length3 = 30 - fsposition.ToString().Length;字符串 s1 = fsposition.ToString() + 新字符串('x', length3); fsposbyte = utf.GetBytes(s1); stream_1.Write(fsposbyte, 0, fsposbyte.Length);
  • 服务器接收 30 个字节,并简单地将“x”替换为“”。然后将其转换为 int。它没有解决问题,只是修补它。
【解决方案2】:

得到它的工作,代码> 30000个字符:\ 它有点乱,但嘿,它很实用。

服务器:https://www.dropbox.com/s/2wyccxpjbja10z3/Program.cs?m

客户:https://www.dropbox.com/s/yp78nx4ubacsz6f/Program.cs?m

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-28
    • 2022-08-03
    • 1970-01-01
    • 2014-10-20
    相关资源
    最近更新 更多