I'm programing an application where I need to make file transfers.
Most of the communication in my application is TCP and works just fine. But when I try to do a file transfer, I seem to lose some bytes at the start and/or end of the file.
Here is the piece of code that is supposed to do the file transfer:
Thread sendFile = new Thread(new ThreadStart(() =>
{
TcpClient tcpClient = new TcpClient(ip, 3);
tcpClient.Client.DontFragment = true;
FileStream fileStream = new FileInfo(FilePath).OpenRead();
Thread.Sleep(1000);
fileStream.CopyTo(tcpClient.GetStream());
fileStream.Close();
tcpClient.Close();
}));
sendFile.SetApartmentState(ApartmentState.STA);
sendFile.Start();
sendFile.Join();
I have searched and tried a bunch of methodes of sending a filestream through a networkstream (WriteAsync, byte[] buffer, flushing the buffers,...) but all had similar results: some bytes at the start of the file and about every 128kb disappear.
I got the best results when running the transfer in a STA thread with some delay before starting.
Client code:
FileStream fileStream = File.Create(path);
Thread receiveFile = new Thread(new ThreadStart(() =>
{
tcpClient.GetStream().CopyTo(fileStream);
}));
receiveFile.SetApartmentState(ApartmentState.STA);
receiveFile.Start();
receiveFile.Join();
fileStream.Close();
I tried it on different computers and routers connected with LAN cables to make sure those weren't the problems.
I'm using .Net Core 5.0
Update
I've tried a few things and it made it better, but still not perfect.
Server code:
Thread sendFile = new Thread(new ThreadStart(() =>
{
TcpClient tcpClient = new TcpClient(ip, 3);
FileStream fileStream = new FileInfo(FilePath).OpenRead();
NetworkStream networkStream = tcpClient.GetStream();
Thread.Sleep(1000);
byte[] bytes = new byte[1024];
int read = -1;
while (read != 0)
{
read = fileStream.Read(bytes);
networkStream.Write(bytes, 0, read);
}
filestream.Flush();
fileStream.Close();
tcpClient.Close();
}));
sendFile.SetApartmentState(ApartmentState.STA);
sendFile.Start();
sendFile.Join();
Client code:
FileStream fileStream = File.Create(path);
BufferedStream networkStream = new BufferedStream(client.GetStream());
Thread receiveFile = new Thread(new ThreadStart(() =>
{
byte[] bytes = new byte[2048];
int read = -1;
while (read != 0)
{
read = networkStream.Read(bytes, 0, bytes.Length);
using (MemoryStream memoryStream = new MemoryStream(bytes))
{
using (BinaryReader binaryReader = new BinaryReader(memoryStream))
{
fileStream.Write(binaryReader.ReadBytes(read));
}
}
}
fileStream.Flush();
}));
receiveFile.SetApartmentState(ApartmentState.STA);
receiveFile.Start();
receiveFile.Join();
fileStream.Close();
As I had assumed the problem lies on the TcpClient. When using normal sockets everything works as it should: no data loss.
Client code:
Thread receiveFile = new Thread(new ThreadStart(() =>
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
socket.Bind(new IPEndPoint(IPAddress.Any, 33));
socket.Listen();
socket = socket.Accept();
FileStream fileStream = File.Create(path);
NetworkStream networkStream = new NetworkStream(socket);
networkStream.CopyTo(fileStream);
fileStream.Flush();
fileStream.Close();
socket.Close();
socket.Dispose();
}));
receiveFile.SetApartmentState(ApartmentState.STA);
receiveFile.Start();
receiveFile.Join();
GC.Collect();
Server code:
Thread sendFile = new Thread(new ThreadStart(() =>
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
socket.Connect(new IPEndPoint(IPAddress.Parse(client.Host), 33));
FileStream fileStream = new FileInfo(FilePath).OpenRead();
NetworkStream networkStream = new NetworkStream(socket);
Thread.Sleep(1000);
fileStream.CopyTo(networkStream);
fileStream.Flush();
fileStream.Close();
socket.Close();
socket.Dispose();
}));
sendFile.SetApartmentState(ApartmentState.STA);
sendFile.Start();
sendFile.Join();
GC.Collect();
I forced the collector to make sure the sockets are disposed, so they can be used again.