【发布时间】:2021-11-16 10:00:26
【问题描述】:
我正在开发基于 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);
}
}
【问题讨论】:
-
您如何发出传输完成的信号?您如何向服务器发送文件长度信号?
-
@RowanSmith 他们通过
SendMessage($"FILE:{Path.GetFileName(path)}:{size}");在Thread.Sleep( 200 );之前发送尺寸。至少我认为SendMessage是这样做的。 -
除非您设置 NoDelay,否则 200 毫秒可能不足以等待。即便如此,这也是一种糟糕的做法。创建适当的成帧协议来承载流量的报头信息。认识到 TCP 是一个流,它看起来就像磁盘上的一个文件,一直读取到没有数据为止,然后继续尝试读取特定的超时时间,当超时到期时,将其视为磁盘上的文件磁盘损坏并被提前截断。
-
使用
async Task代替 async void。使用await Task.Delay而不是 Thread.Sleep() - 但实际上你不应该拖延。 -
@RowanSmith 呈现的版本被剥夺了传输完成信号功能。这是因为 - 正如我在帖子中提到的 - 它对文件没有影响,所以我删除了它并简化了代码。现在服务器接收到它保存的那一刻包(行:使用(FileStream fs = new FileStream(_filename,FileMode.Append)))我应该在我的帖子中对此进行说明,对此感到抱歉。