首页 > 解决方案 > C# SuperSimpleTCP:接收到的文件部分损坏

问题描述

我正在开发基于 TCP (SuperSimpleTCP) 的文件传输应用程序。该应用程序应允许在 LAN 内的计算机之间传输文件。它部分这样做 - 问题是,接收到的文件部分损坏(参见屏幕截图:https ://imgur.com/a/OnIovIr )。

图像的顶部始终正确保存,但是图像的其余部分在随机点处损坏 - 可能在一半以下或仅在顶部以下几个像素。我不太明白为什么会这样。

我认为问题可能在于接收包的组装方式或数据包根本没有到达服务器(尽管有 TCP)。

所以我尝试了在服务器站点组装它们的不同方法,比如将它们收集到一个列表中,然后重写为一个字节 [],最后保存。我还让客户端在发送最后一段文件时发送“ack”消息,允许服务器保存文件。

但是,上面提到的都没有改变文件的行为方式。

这是客户端类中的代码,负责发送文件:

 public async void SendFile(string path)
            {
               
                var size = new FileInfo(path).Length;
               
                SendMessage($"FILE:{Path.GetFileName(path)}:{size}");
                Thread.Sleep(200);
                
                using (FileStream fs = new FileStream(path, FileMode.Open))
                {
                    await client.SendAsync(size, fs); // SendAsync is a SSTCP method
                }
                Console.WriteLine("File sent.");
            }

这是服务器类中负责接收和保存文件的代码。

private void EventsOnDataReceived(object? sender, DataReceivedEventArgs e)
        {
            Console.WriteLine("New content received: " + e.Data.Length + " Bytes.");
            string message;
            var content = e.Data;
            if (content.Length <= 128) // messages are shorter than 128 bytes
            {
                message = Encoding.UTF8.GetString(content);
                if (message.Contains("FILE:"))
                {
                    _filename = message.Split(":")[1]; // class field containing filename
                    _size = long.Parse(message.Split(":")[2]); // class field con. size
                    return;
                }
            }

            // data bigger than 128 bytes is considered a file (for now)
            using (FileStream fs = new FileStream(_filename, FileMode.Append))
            {
                fs.Write(content);
            }
        }

预先感谢您的任何帮助。

** 编辑:应@RowanSmith 的要求发布新版本的代码**

SendFile 方法:

public async Task SendFile(string path)
            {
                var size = new FileInfo(path).Length;
                using (FileStream fs = new FileStream(path, FileMode.Open))
                {
                    await client.SendAsync(size, fs);
                }
                Console.WriteLine("File sent.");
            }

服务器的接收代码:

private void EventsOnDataReceived(object? sender, DataReceivedEventArgs e)
        {
            Console.WriteLine("New content received: " + e.Data.Length + " Bytes.");

            using (FileStream fs = new FileStream("file.png", FileMode.Append))
            {
                fs.Write(e.Data);
            }  
        }

标签: c#tcp

解决方案


出现此问题是因为您正在接收数据,而您已经在接收数据。事件:

private void EventsOnDataReceived(object? sender, DataReceivedEventArgs e)

在前一个事件仍在将数据写入磁盘时被抛出。这是因为磁盘访问比内存访问慢。

要解决此问题,您需要将文件读入内存,然后将文件保存到磁盘。

下面的完整示例有效,它只是因为内存比从磁盘读取快。如果您的磁盘因为比内存快(不太可能),那么这也会中断。

class Program
{

    static async Task Main(string[] args)
    {
        var server = StartServer();
        await Task.Delay(1000);
        _ = StartClient();

        await server;
        Console.WriteLine("Server exited");
    }

    static MemoryStream memoryStream = new();
    static private void EventsOnDataReceived(object sender, DataReceivedEventArgs e)
    {
        memoryStream.Capacity = memoryStream.Capacity + e.Data.Length;
        memoryStream.Write(e.Data);
    }

    static async Task StartServer()
    {
        await Task.Yield();
        var server = new SimpleTcpServer("127.0.0.1:9999");
        server.Events.DataReceived += EventsOnDataReceived;
        server.Start();

        Console.WriteLine("Press any key to quit the server - but wait for the client to finish!");
        Console.ReadKey();

        using FileStream fs = new FileStream("outfile.png", FileMode.Create);
        fs.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
    }

    static async Task StartClient()
    {
        await Task.Yield();
        using (SimpleTcpClient client = new SimpleTcpClient("127.0.0.1:9999"))
        {
            using FileStream fs = new FileStream("startfile.png", FileMode.Open);
            client.Connect();
            client.Send(fs.Length, fs);
            client.Disconnect();
        }
        Console.WriteLine("Client finished");
    }
}

推荐阅读