C# 网络流数据丢失
本文关键字:数据 网络 | 更新日期: 2023-09-27 18:34:15
我目前正在做一个网络项目,在那里我制定了一个二进制协议。我的数据包如下所示:[1 字节类型][2 字节索引][2 字节长度][长度字节数据]
这是我接收数据包的代码:
NetworkStream clientStream= Client.GetStream();
while (Client.Connected)
{
Thread.Sleep(10);
try
{
if (clientStream.DataAvailable)
{
byte[] infobuffer = new byte[5];
int inforead = clientStream.Read(infobuffer, 0, 5);
if (inforead < 5) { continue; }
byte[] rawclient = new byte[2];
Array.Copy(infobuffer, 1, rawclient, 0, 2);
PacketType type = (PacketType)Convert.ToSByte(infobuffer[0]);
int clientIndex = BitConverter.ToInt16(rawclient, 0);
int readLength = BitConverter.ToInt16(infobuffer, 3);
byte[] readbuffer = new byte[readLength];
int count_read = clientStream.Read(readbuffer, 0, readLength);
byte[] read_data = new byte[count_read];
Array.Copy(readbuffer, read_data, count_read);
HandleData(read_data, type, clientIndex);
}
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("[E] " + ex.GetType().ToString());
Console.ResetColor();
break;
}
}
好吧,一切正常...只要我在 127.0.0.1 上运行它。一旦我尝试远距离测试它,数据包就会以某种方式丢失,并且在我将第一个字节转换为 PacketType 的行上出现溢出异常。另外,如果我尝试将其他值转换为 int16,我会得到非常奇怪的值。
我假设流在到达服务器的途中以某种方式丢失了一些字节,但可以吗?还是只是我在代码中的某个地方犯了一个小错误?
编辑:我现在编辑了代码,现在它读取直到它得到它的 5 个字节。但是我仍然在长距离上得到同样的异常......
NetworkStream clientStream = Client.GetStream();
while (Client.Connected)
{
Thread.Sleep(10);
try
{
if (clientStream.DataAvailable)
{
int totalread = 0;
byte[] infobuffer = new byte[5];
while (totalread < 5)
{
int inforead = clientStream.Read(infobuffer, totalread, 5 - totalread);
if (inforead == 0)
{ break; }
totalread += inforead;
}
byte[] rawclient = new byte[2];
Array.Copy(infobuffer, 1, rawclient, 0, 2);
PacketType type = (PacketType)Convert.ToSByte(infobuffer[0]);
int clientIndex = BitConverter.ToInt16(rawclient, 0);
int readLength = BitConverter.ToInt16(infobuffer, 3);
byte[] readbuffer = new byte[readLength];
int count_read = clientStream.Read(readbuffer, 0, readLength);
byte[] read_data = new byte[count_read];
Array.Copy(readbuffer, read_data, count_read);
HandleData(read_data, type, clientIndex);
}
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("[E] " + ex.GetType().ToString());
Console.ResetColor();
break;
}
}
PacketType 是一个枚举:
public enum PacketType
{
AddressSocks5 = 0,
Status = 1,
Data = 2,
Disconnect = 3,
AddressSocks4 = 4
}
你在这里做错了很多事情......这么多虫子...甚至从哪里开始...
第一次网络轮询? 真? 这只是当今时代进行网络活动的一种幼稚方式。但我不会对此喋喋不休。
其次,使用这种类型的协议,很容易"不同步",一旦你这样做了,你就没有办法恢复同步。 这通常是通过某种"成帧协议"来实现的,该协议提供了一个独特的字节序列,您可以使用这些字节序列来指示帧的开始和结束,这样,如果您发现自己不同步,您可以读取数据,直到您恢复同步。 是的,您将丢失数据,但如果数据不同步,您已经丢失了数据。
第三,你在这里并没有真正做任何大事,所以我无耻地从这里偷走了"ReadWholeArray"代码,它不是最有效的,但它有效,并且还有其他代码可能会有所帮助:
http://www.yoda.arachsys.com/csharp/readbinary.html
注意:您没有提及如何在另一端序列化长度、类型和索引值。 因此,使用BitConverter可能是错误的事情,具体取决于如何完成。
if (clientStream.DataAvailable)
{
byte[] data = new byte[5];
// if it can't read all 5 bytes, it throws an exception
ReadWholeArray(clientStream, data);
PacketType type = (PacketType)Convert.ToSByte(data[0]);
int clientIndex = BitConverter.ToInt16(data, 1);
int readLength = BitConverter.ToInt16(data, 3);
byte[] rawdata = new byte[readLength];
ReadWholeArray(clientStream, rawdata);
HandleData(rawdata, type, clientIndex);
}
/// <summary>
/// Reads data into a complete array, throwing an EndOfStreamException
/// if the stream runs out of data first, or if an IOException
/// naturally occurs.
/// </summary>
/// <param name="stream">The stream to read data from</param>
/// <param name="data">The array to read bytes into. The array
/// will be completely filled from the stream, so an appropriate
/// size must be given.</param>
public static void ReadWholeArray (Stream stream, byte[] data)
{
int offset=0;
int remaining = data.Length;
while (remaining > 0)
{
int read = stream.Read(data, offset, remaining);
if (read <= 0)
throw new EndOfStreamException
(String.Format("End of stream reached with {0} bytes left to read", remaining));
remaining -= read;
offset += read;
}
}
我认为问题出在这些行中
int inforead = clientStream.Read(infobuffer, 0, 5);
if (inforead < 5) { continue; }
如果长度小于 5 个字节,您之前读取的数据会发生什么情况? 您应该保存到目前为止已读取的字节并附加下一个字节,以便您可以完全拥有标头
5 - totalRead。
让 totalRead 等于 5 或更多。当这种情况发生时,你什么都不读,在 1 - 4 的情况下,你读取了那么多任意字节。不是5。然后,您还将丢弃小于 5 的任何结果。
您也可以在偏移量 1 或另一个偏移量处复制,而实际上不知道偏移量。
BitConverter.ToInt16(infobuffer, 3(;
举个例子,关 2 是什么?
因此,如果不是(解码错误(而不是数据的结构,那么除非您更改循环的结构,否则丢失字节的是您而不是 NetworkStream。
在接收时按 justRead 的增量计算总读取,以便您可以处理任何大小的数据以及以正确的偏移量接收数据。