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.
Related
I'm creating a file transfer app which works with TcpListener and TcpClient. My app works perfectly, I'm just wondering how I can show the download/upload speed while data is transferring. I also want the app to show how many MBs are downloaded or uploaded.
Server code:
IPAddress ip = IPAddress.Parse("192.168.1.2");
int port = 9999;
TcpListener server = new TcpListener(ip, port);
server.Start();
Console.WriteLine("Waiting for client...");
TcpClient tcpClient = server.AcceptTcpClient();
Console.WriteLine("Client connected!");
NetworkStream networkStream = tcpClient.GetStream();
string path = string.Empty;
string fileName = string.Empty;
Console.Write("File:");
path = Console.ReadLine();
fileName = Path.GetFileName(path);
byte[] bytes = File.ReadAllBytes(path);
BinaryWriter bwFileName = new BinaryWriter(networkStream);
bwFileName.Write(fileName);
BinaryWriter bw = new BinaryWriter(networkStream);
bw.Write(bytes.Length);
networkStream.Write(bytes, 0, bytes.Length);
Console.ReadLine();
Client code:
IPAddress ip = IPAddress.Parse("192.168.1.2");
int port = 9999;
TcpClient client = new TcpClient();
client.Connect(ip, port);
while (true)
{
NetworkStream networkStream = client.GetStream();
int byteLenght;
string fileName = string.Empty;
BinaryReader brFilename = new BinaryReader(networkStream);
fileName = brFilename.ReadString();
BinaryReader br = new BinaryReader(networkStream);
byteLenght = br.ReadInt32();
byte[] bytes = new byte[byteLenght];
networkStream.Read(bytes, 0, bytes.Length);
File.WriteAllBytes("C:\\Users\\XANDRO\\Desktop\\client\\" + fileName,bytes);
break;
}
Console.ReadLine();
the general approach would be to split the download/upload into chunks. I.e. instead of writing the entire byte array in one go, write chunks using the offset/length parameters.
This gives an opportunity to measure the time and report the progress after each chunk. You might also consider using a file stream and write/read chunks from this as well. Since this will limit memory usage.
Edit: Example code:
public static void Copy(Stream from, Stream to, IProgress<int> progress, int bufferSize = 4096)
{
var bytesCopied = 0;
var buffer = new byte[bufferSize];
int bytesRead;
do
{
bytesRead = from.Read(buffer, 0, buffer.Length);
to.Write(buffer, 0, bytesRead);
bytesCopied += bytesRead;
progress.Report(bytesCopied);
} while (bytesRead != 0);
}
I have Client layer and Server Layer. Client send converted to byte array integer varible.
I am using TcpClient and TcpListener, and use NetworkStream to write and read data. For some reason Server do not read data from NetworkStream, inspite that i use advice from this question: How to get all data from NetworkStream
Client Layer:
TcpClient sender1 = new TcpClient();
sender1.Connect(ip, port);
using (NetworkStream stream1 = client.GetStream())
{
int isCorrect = 1;
byte[] data = BitConverter.GetBytes(isCorrect);
stream1.Write(data, 0, data.Length);
}
sender1.Close();
Server Layer
TcpListener server = new TcpListener(IPAddress.Parse(ip), serverPort);
server.Start();
while (true)
{
TcpClient enterWaiter = server.AcceptTcpClient();
using (NetworkStream stream1 = enterWaiter.GetStream())
{
byte[] buffer = new byte[4];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = stream1.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
Console.WriteLine($"{ms.ToArray()}");
}
}
server.Stop();
break;
}
It is because you are closing the server prior to receiving your data, move or remove the Server.Stop and break lines, such as:
TcpListener server = new TcpListener(IPAddress.Parse(ip), serverPort);
server.Start();
while (true)
{
TcpClient enterWaiter = server.AcceptTcpClient();
using (NetworkStream stream1 = enterWaiter.GetStream())
{
byte[] buffer = new byte[4];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = stream1.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
Console.WriteLine($"{ms.ToArray()}");
}
}
//server.Stop(); <-- (Re)move
//break; <-- (Re)move
}
Here is my server code for reading a mp4 file and sending to the client
Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream,ProtocolType.Tcp);
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 3400);
sock.Bind(ep);
sock.Listen(10);
sock = sock.Accept();
FileStream fs = new FileStream(#"E:\Entertainment\Songs\Video song\song.mp4",FileMode.Open);
BinaryReader br = new BinaryReader(fs);
byte[] data = new byte[fs.Length];
br.Read(data, 0, data.Length);
sock.Send(data);
fs.Close();
sock.Close();
Here is the client code
sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 3400);
sock.Connect(ep);
MemoryStream ms = new MemoryStream();
int size = 3190551; // I know the file size is about 30 mb
int rec;
while (size > 0)
{
byte[] buffer;
if (size < sock.ReceiveBufferSize)
{
buffer = new byte[size];
}
else
{
buffer = new byte[sock.ReceiveBufferSize];
}
rec = sock.Receive(buffer, 0, buffer.Length, 0);
size = size - rec;
ms.Write(buffer, 0, buffer.Length);
}
byte[] data = ms.ToArray();
FileStream fs = new FileStream("E:/song.mp4",FileMode.Create);
BinaryWriter bw = new BinaryWriter(fs)
bw.Write(data);
fs.Close();
sock.Close();
**At the end i just get the data in between 3 to 4 mb.... im new to socket programming and I don't know where the problem is... whether its sending side or receiving !!!! it looks like I just receive a single chunk of data from the server side **
I think the problem is here
int size = 3190551; // I know the file size is about 30 mb
you are reading just 3190551 byte which is 3.04mb not 30mb.
try to send length of your file at the beginning of your message so client will know how many bytes it should get from server.
To put my toe in the water of Network programming, I wrote a little Console App to send a png file to a server (another console app). The file being written by the server is slightly bigger than the source png file. And it will not open.
The code for the client app is:
private static void SendFile()
{
using (TcpClient tcpClient = new TcpClient("localhost", 6576))
{
using (NetworkStream networkStream = tcpClient.GetStream())
{
//FileStream fileStream = File.Open(#"E:\carry on baggage.PNG", FileMode.Open);
byte[] dataToSend = File.ReadAllBytes(#"E:\carry on baggage.PNG");
networkStream.Write(dataToSend, 0, dataToSend.Length);
networkStream.Flush();
}
}
}
The code for the Server app is :
private static void Main(string[] args)
{
Thread thread = new Thread(new ThreadStart(Listen));
thread.Start();
Console.WriteLine("Listening...");
Console.ReadLine();
}
private static void Listen()
{
IPAddress localAddress = IPAddress.Parse("127.0.0.1");
int port = 6576;
TcpListener tcpListener = new TcpListener(localAddress, port);
tcpListener.Start();
using (TcpClient tcpClient = tcpListener.AcceptTcpClient())
{
using (NetworkStream networkStream = tcpClient.GetStream())
{
using (Stream stream = new FileStream(#"D:\carry on baggage.PNG", FileMode.Create, FileAccess.ReadWrite))
{
// Buffer for reading data
Byte[] bytes = new Byte[1024];
var data = new List<byte>();
int length;
while ((length = networkStream.Read(bytes, 0, bytes.Length)) != 0)
{
var copy = new byte[length];
Array.Copy(bytes, 0, copy, 0, length);
data.AddRange(copy);
}
BinaryFormatter binaryFormatter = new BinaryFormatter();
stream.Position = 0;
binaryFormatter.Serialize(stream, data.ToArray());
}
}
}
tcpListener.Stop();
The size of the written file is 24,103Kb, whereas the source file is only 24,079Kb.
Is it apparent to anyone why this operation is failing?
Cheers
You are writing your output using a BinaryFormatter. I'm pretty sure that this will add some bytes at the start of the output to indicate the type that you're outputting (in this case System.Byte[]).
Just write the bytes out directly to the file without using the formatter:
using (Stream stream = new FileStream(#"D:\carry on baggage.PNG", FileMode.Create, FileAccess.ReadWrite))
{
// Buffer for reading data
Byte[] bytes = new Byte[1024];
int length;
while ((length = networkStream.Read(bytes, 0, bytes.Length)) != 0)
{
stream.Write(bytes, 0, length);
}
}
I am trying to receive the image form Server and want to display it into a Picturebox in WM Application. I am successfully receiving the Image Stream and I don’t find any way to display it into a PictureBox. In windows program we have a method in Image class that is FromStream (Image.FromStream) but this function is not available in Compact Framework 3.5. I also tried the following code to do so:
private void button1_Click(object sender, EventArgs e)
{
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress IP = IPAddress.Parse("192.168.1.2");
IPEndPoint IPE = new IPEndPoint(IP, 4321);
s.Connect(IPE);
byte[] buffer = new byte[55296];
s.Receive(buffer, buffer.Length, SocketFlags.None);
MemoryStream ms = new MemoryStream(buffer);
Image im = new Bitmap(ms); //EXCEPTION
pictureBox1.Image = im;
}
But it gives an Exception. No detail is provided with the exception and VS is only displaying a dialogbox with the text "Exception".
Does your image size is less than the size of the buffer? If not all the excess data is lost and an exception is thrown.
Also could you try without the buffer length specified.
using (Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
IPAddress IP = IPAddress.Parse("192.168.1.2");
IPEndPoint IPE = new IPEndPoint(IP, 4321);
s.Connect(IPE);
byte[] buffer = new byte[55296];
int rec = s.Receive(buffer, SocketFlags.None);
using (MemoryStream ms = new MemoryStream(buffer, 0, rec))
{
Image im = new Bitmap(ms);
pictureBox1.Image = im;
}
}
You do have to check the return value of the socket's Receive method in order to determine
how large your bitmap buffer is:
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress IP = IPAddress.Parse("192.168.1.2");
IPEndPoint IPE = new IPEndPoint(IP, 4321);
s.Connect(IPE);
byte[] buffer = new byte[55296];
int rec = s.Receive(buffer, buffer.Length, SocketFlags.None);
MemoryStream ms = new MemoryStream(buffer, 0, rec);
Image im = new Bitmap(ms);
pictureBox1.Image = im;
Hope, this helps.