数据丢失TCP IP C#

时间:2011-04-03 07:30:22

标签: c# networking serialization tcp packet

这是我的代码:

private void OnReceive(IAsyncResult result)
{
NetStateObject state = (NetStateObject)result.AsyncState;

Socket client = state.Socket;

int size = client.EndReceive(result);

byte[] data = state.Buffer;

object data = null;

using (MemoryStream stream = new MemoryStream(data))
{
    BinaryFormatter formatter = new BinaryFormatter();

    data = formatter.Deserialize(stream);
}

//todo: something with data

client.BeginReceive(
    state.Buffer,
    0,
    NetStateObject.BUFFER_SIZE,
    SocketFlags.None,
    OnReceive,
    state
    );
}

state.Buffer的最大大小为NetStateObject.BUFFER_SIZE(1024)。首先,这太大还是太小?其次,如果我发送大于那个的东西,我的反序列化会搞砸,因为它试图反序列化的对象没有所有信息(因为并非所有数据都被发送)。在尝试构建数据并对其执行某些操作之前,如何确保已收到所有数据?

已完成的工作代码

        private void OnReceive(IAsyncResult result)
    {
        NetStateObject state = (NetStateObject)result.AsyncState;

        Socket client = state.Socket;

        try
        {
            //get the read data and see how many bytes we received
            int bytesRead = client.EndReceive(result);

            //store the data from the buffer
            byte[] dataReceived = state.Buffer;

            //this will hold the byte data for the number of bytes being received
            byte[] totalBytesData = new byte[4];

            //load the number byte data from the data received
            for (int i = 0; i < 4; i++)
            {
                totalBytesData[i] = dataReceived[i];
            }

            //convert the number byte data to a numan readable integer
            int totalBytes = BitConverter.ToInt32(totalBytesData, 0);

            //create a new array with the length of the total bytes being received
            byte[] data = new byte[totalBytes];

            //load what is in the buffer into the data[]
            for (int i = 0; i < bytesRead - 4; i++)
            {
                data[i] = state.Buffer[i + 4];
            }

            //receive packets from the connection until the number of bytes read is no longer less than we need
            while (bytesRead < totalBytes + 4)
            {
                bytesRead += state.Socket.Receive(data, bytesRead - 4, totalBytes + 4 - bytesRead, SocketFlags.None);
            }

            CommandData commandData;

            using (MemoryStream stream = new MemoryStream(data))
            {
                BinaryFormatter formatter = new BinaryFormatter();

                commandData = (CommandData)formatter.Deserialize(stream);
            }

            ReceivedCommands.Enqueue(commandData);

            client.BeginReceive(
                state.Buffer,
                0,
                NetStateObject.BUFFER_SIZE,
                SocketFlags.None,
                OnReceive,
                state
                );

            dataReceived = null;
            totalBytesData = null;
            data = null;
        }
        catch(Exception e)
        {
            Console.WriteLine("***********************");
            Console.WriteLine(e.Source);
            Console.WriteLine("***********************");
            Console.WriteLine(e.Message);
            Console.WriteLine("***********************");
            Console.WriteLine(e.InnerException);
            Console.WriteLine("***********************");
            Console.WriteLine(e.StackTrace);
        }
    }

2 个答案:

答案 0 :(得分:4)

TCP是一种流协议。它没有数据包的概念。可以在多个数据包中发送单个写入调用,并且可以将多个写入调用放入同一个数据包中。因此,您需要在TCP之上实现自己的打包逻辑。

有两种常见的打包方式:

  1. 分隔符字符,通常用于文本协议,新行是一种常见的选择
  2. 为每个数据包添加前缀,通常是二进制协议的好选择。
  3. 您可以在该数据包的开头存储逻辑数据包的大小。然后你阅读,直到你收到足够的字节来填充数据包并开始反序列化。

答案 1 :(得分:4)

  

在尝试构建数据并使用它做某事之前,如何确保我的所有数据都已收到?

你必须实现一些协议,所以你知道。

虽然TCP是可靠的,但它不能保证套接字一端的单次写入数据在另一端显示为单次读取:重试,数据包碎片和MTU都可以导致数据被接收到不同的接收器的大小单位。您将按正确的顺序获取数据。

因此,您需要在发送时包含一些信息,以便接收方知道何时有完整的消息。我还建议包括什么样的消息和什么版本的数据(这将构成能够一起支持不同客户端和服务器版本的基础)。

所以发件人发送: - 消息类型 - 消息版本 - 消息大小(以字节为单位)

接收器将循环,使用缓冲区执行读取并将其附加到主缓冲区(MemoryStream对此有利)。一旦收到完整的标题,它就知道何时收到了完整的数据。

(另一种方法是将一些模式作为“消息结束”标记包含在内,但是你需要处理内容中出现的相同字节序列 - 如果数据是二进制而不是文本则很难做到。)< / p>