我目前正在开发一个网络项目,在那里我制定了一个二进制协议。我的数据包看起来像这样: [1字节类型] [2字节INDEX] [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
}
答案 0 :(得分:3)
你在这里犯了很多错事......这么多错误......哪里开始......
首次网络投票?真?这只是在这个时代进行网络活动的一种天真的方式..但我不会对此有所帮助。
其次,使用这种类型的协议,很容易“不同步”,一旦你这样做,你就无法恢复同步。这通常是通过某种“成帧协议”来完成的,它提供了一个独特的字节序列,您可以用它来指示帧的开始和结束,这样如果您发现自己不同步,您可以读取数据直到获得回到同步。是的,您将丢失数据,但如果您不同步,则已经丢失了数据。
第三,你真的没有在这里做任何大事,所以我从这里无耻地偷走了“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;
}
}
答案 1 :(得分:2)
我认为问题出在这些方面
int inforead = clientStream.Read(infobuffer, 0, 5);
if (inforead < 5) { continue; }
如果长度低于5个字节,您之前读取的数据会发生什么变化?您应该保存到目前为止已读取的字节并附加下一个字节,以便您可以完全使用标题
答案 2 :(得分:1)
你读5 - totalRead。
让totalRead等于5或更多。当发生这种情况时,你什么都不读,在1到4的情况下,你会读到许多任意字节。不是5.然后你也丢弃少于5的结果。
您也可以在偏移1或其他偏移处复制而不知道偏移。
BitConverter.ToInt16(信息缓冲,3);
这是一个例子,什么是关闭2?
因此,如果它不是(解码错误)而不是数据的结构,那么除非你改变你的循环结构,否则你丢失字节而不是NetworkStream。
当你收到时,以justRead的增量计算totalRead,这样你就可以处理任何大小的数据,并以正确的偏移量接收它。