I'm working on file transferring application based on TCP (SuperSimpleTCP). The application should allow file transfer between computers within LAN. And it partially does so - problem is, the received file is partially corrupted (see the screen shots: https://imgur.com/a/OnIovIr).
The top of the image is always saved correctly, however the rest of the image is corrupted at random points - can be half way down or just few pixels below the top. I don't quite understand why is that happening.
I though that the problem might lay either in way received packages are assembled or data packages not getting to the server at all (despite TCP).
So I tried different methods of assembling 'em at the server site, like collecting them into an List and then rewriting to a byte[] and finally saving. Also I made the client sent "ack" message when the last pieces of file were sent, allowing the server to save the file.
However, none of mentioned above changed the way the file is behaving.
This is the code in client class, responsible for sending the file:
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.");
}
This is the code in server class responsible for receiving and saving the file.
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);
}
}
Thank you in advance for any help.
** EDIT: posting new version of code at the request of @RowanSmith **
The SendFile method:
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.");
}
The server's receiving code:
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);
}
}
CodePudding user response:
This issue is occuring because you are receiving data while the you are already receiving data. The event:
private void EventsOnDataReceived(object? sender, DataReceivedEventArgs e)
Is being thrown while the previous event is still writing data to the disk. This is because the disk access is slower then the memory access.
To solve this you need to read the file into memory and then save the file to disk.
The following full example works and it only works because the memory is faster than the reading from the disk. If your disk because faster than memory, (unlikely), then this would also break.
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");
}
}