简体   繁体   English

C#NetworkStream数据丢失

[英]C# NetworkStream data loss

I am currently working on a networking project where I worked out a binary protocol. 我目前正在从事一个联网项目,在那里我制定了一个二进制协议。 My packets look like this: [1 byte TYPE][2 bytes INDEX][2 bytes LENGTH][LENGTH bytes DATA] 我的数据包看起来像这样:[1字节类型] [2字节索引] [2字节长度] [长度字节数据]

And here's the code where I am receiving the packets: 这是我接收数据包的代码:

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;
    }
}

Well, and everything works fine... as long as I run it on 127.0.0.1. 好吧,一切正常……只要我在127.0.0.1上运行它。 As soon as I try testing it over long distance, packets somehow get lost, and I am getting an overflow-exception on the line where I convert the first byte to PacketType. 一旦我尝试对其进行长距离测试,数据包就会以某种方式丢失,并且在将第一个字节转换为PacketType的那一行上,我得到了溢出异常。 Also, if I try to convert the other values to int16, I get very strange values. 另外,如果我尝试将其他值转换为int16,则会得到非常奇怪的值。

I assume the stream somehow looses some bytes on its way to the server, but can this be? 我假设流以某种方式在到达服务器的途中丢失了一些字节,但这可以吗? Or is it just a little mistake of mine somewhere in the code? 还是这只是我在代码中某个地方的一个小错误?

edit: I now edited the code, now it reads till it gets its 5 bytes. 编辑:我现在编辑了代码,现在读取直到得到5个字节。 But I still get the same exception over long distance... 但是我在长距离上仍然遇到同样的异常...

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 is an enum: PacketType是一个枚举:

public enum PacketType
{
    AddressSocks5 = 0,
    Status = 1,
    Data = 2,
    Disconnect = 3,
    AddressSocks4 = 4
}

So many things you're doing wrong here... so many bugs... where to even start... 您在这里做错的事情太多...很多错误...甚至从哪里开始...

First Network polling? 第一次网络轮询? Really? 真? That's just a naïve way of doing network activity in this day and age.. but I won't harp on that. 在当今时代,这只是进行网络活动的一种幼稚方式..但我不会对此rp之以鼻。

Second, with this type of protocol, it's pretty easy to get "out of sync" and once you do, you have no way to get back in sync. 其次,使用这种类型的协议,很容易实现“不同步”,一旦执行,便无法恢复同步。 This is typically accomplished with some kind of "framing protocol" which provides a unique sequence of bytes that you can use to indicate the start and end of a frame, so that if you ever find yourself out of sync you can read data until you get back in sync. 通常,这是通过某种“成帧协议”完成的,该协议提供了唯一的字节序列,可用于指示帧的开始和结束,因此,如果您发现自己不同步,则可以读取数据,直到获得重新同步。 Yes, you will lose data, but you've already lost it if you're out of sync. 是的,您将丢失数据,但是如果不同步,则已经丢失了数据。

Third, you're not really doing anything huge here, so I shamelessly stole the "ReadWholeArray" code from here, it's not the most efficient, but it works and there is other code there that might help: 第三,您实际上并没有在这里做任何大的事情,因此我从这里偷偷地偷了“ ReadWholeArray”代码,它不是最有效的,但是它可以工作,并且还有其他代码可能会有所帮助:

http://www.yoda.arachsys.com/csharp/readbinary.html http://www.yoda.arachsys.com/csharp/readbinary.html

Note: you don't mention how you are serializing the length, type and index values on the other side. 注意:您不会在另一侧提到如何序列化长度,类型和索引值。 So using the BitConverter may be the wrong thing depending on how that was done. 因此,根据操作方式的不同,使用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;
    }
}

I think the problem is in these lines 我认为问题出在这些方面

int inforead = clientStream.Read(infobuffer, 0, 5);
if (inforead < 5) { continue; }

what happen to your previously read data if the length is under 5 byte? 如果长度小于5个字节,您以前读取的数据会怎样? you should save the bytes you have read so far and append next bytes so you can have the header completely 您应该保存到目前为止已读取的字节,并追加下一个字节,以便完全拥有标题

You Read 5 - totalRead. 您阅读5-totalRead。

let totalRead equal 5 or more. 让totalRead等于5或更大。 When that happens you read nothing, and in cases of 1 - 4 you read that many arbitrary bytes. 发生这种情况时,您什么也没读,在1-4的情况下,您读了那么多任意字节。 Not 5. You also then discard any result of less then 5. 不是5.您还丢弃小于5的任何结果。

You also copy at a offset 1 or another offset without really knowing the offset. 您还以一个偏移量1或另一个偏移量进行复制,而实际上并不知道该偏移量。

BitConverter.ToInt16(infobuffer, 3); BitConverter.ToInt16(信息缓冲区,3);

Is an example of this, what is at off 2? 这是一个例子,关闭2是什么?

So if it's not that (decoding error) and and not the structure of your data then unless you change the structure of your loop its you who's losing the bytes not the NetworkStream. 因此,如果不是那样的话(解码错误),而不是数据的结构,那么除非您更改循环的结构,否则丢失字节的不是网络流。

Calculate totalRead by increments of justRead when you recieve so you can handle any size of data as well as receiving it at the correct offset. 收到时以justRead的增量计算totalRead,以便您可以处理任何大小的数据以及以正确的偏移量接收它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM