Sending a file using TCPClient and NetworkStream in C #
I am trying to send a file from a client to a server application using the TCPClient class in C #. Before I send the actual data, I am sending additional information such as the exact file size and file name, so the server application knows how much to read. The funny thing is that everything was fine when I tested it for 127.0.0.1 - as soon as I replaced the IP address with the actual one, the server could only read 1.5 kilobytes of data that was sent. It still gets the filename and file size, but it still fetches the actual data.
For testing purposes, I replaced the image I was about to send with a simple string and the transfer went fine, so I suppose there is a problem sending and receiving blocks of data, but I am not getting any client side exceptions.
Anyone got an idea? Hooray!
Edit:
Thanks, so far, this is what I got by code. For client:
IPAddress ipAddress = IPAddress.Parse("xx.xx.xx.xx");
int port = 3003;
int bufferSize = 1024;
TcpClient client = new TcpClient();
NetworkStream netStream;
// Connect to server
try
{
client.Connect(new IPEndPoint(ipAddress, port));
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
netStream = client.GetStream();
// Read bytes from image
byte[] data = File.ReadAllBytes("C:\\Users\\Dan\\Desktop\\asdf.jpg");
// Build the package
byte[] dataLength = BitConverter.GetBytes(data.Length);
byte[] package = new byte[4 + data.Length];
dataLength.CopyTo(package, 0);
data.CopyTo(package, 4);
// Send to server
int bytesSent = 0;
int bytesLeft = package.Length;
while (bytesLeft > 0)
{
int nextPacketSize = (bytesLeft > bufferSize) ? bufferSize : bytesLeft;
netStream.Write(package, bytesSent, nextPacketSize);
bytesSent += nextPacketSize;
bytesLeft -= nextPacketSize;
}
// Clean up
netStream.Close();
client.Close();
And the server:
TcpListener listen = new TcpListener(3003);
TcpClient client;
int bufferSize = 1024;
NetworkStream netStream;
int bytesRead = 0;
int allBytesRead = 0;
// Start listening
listen.Start();
// Accept client
client = listen.AcceptTcpClient();
netStream = client.GetStream();
// Read length of incoming data
byte[] length = new byte[4];
bytesRead = netStream.Read(length, 0, 4);
int dataLength = BitConverter.ToInt32(length,0);
// Read the data
int bytesLeft = dataLength;
byte[] data = new byte[dataLength];
while (bytesLeft > 0)
{
int nextPacketSize = (bytesLeft > bufferSize) ? bufferSize : bytesLeft;
bytesRead = netStream.Read(data, allBytesRead, nextPacketSize);
allBytesRead += bytesRead;
bytesLeft -= bytesRead;
}
// Save image to desktop
File.WriteAllBytes("C:\\Users\\Dan\\Desktop\\tcpimage.jpg", data);
// Clean up
netStream.Close();
client.Close();
source to share
About 1.5 KiB sounds like 1500 bytes, "the largest allowed Ethernet at the network layer." This is the maximum transfer unit (mtu) that forces your networking stack to split your file into several small packets.
You need to call NetworkStream.Read in a loop to read each packet received. Here's a sample code of this on MSDN.
Combine this with the default .NET behavior; consolidating smaller packets to reduce the number of packets sent, and you will also see this behavior when sending smaller packets. This can be controlled with ServicePointManager.UseNagleAlgorithm or with smaller socket options.
source to share
I used some of your code for a testnet project I am working on. I backed up a couple of things to fit the requirements of my project, but one change I needed to make to the source code was to add "-4" to the line that was setting the bytesLeft variable. After that, the code worked. My test file is 52KB and uploaded successfully.
// Read the data
int bytesLeft = dataLength-4;
byte[] data = new byte[dataLength];
source to share
Ok, dont know what I am doing here, but in case someone is using this as a link.
I got rid of unnecessary copying and made a very important improvement. Your calc way is nextPacketSize
not complete as there may be less data than 1024 available and you will end up with extra zeros between these chunks (I had a lot of headache with this, now so happy to find out)
I made them as a function needed for multiple files, so here's the code:
customer
this one is almost the same
public void sendData(byte[] data, NetworkStream stream)
{
int bufferSize = 1024;
byte[] dataLength = BitConverter.GetBytes(data.Length);
stream.Write(dataLength, 0, 4);
int bytesSent = 0;
int bytesLeft = data.Length;
while (bytesLeft > 0)
{
int curDataSize = Math.Min(bufferSize, bytesLeft);
stream.Write(data, bytesSent, curDataSize);
bytesSent += curDataSize;
bytesLeft -= curDataSize;
}
}
Server
public byte[] getData(TcpClient client)
{
NetworkStream stream = client.GetStream();
byte[] fileSizeBytes = new byte[4];
int bytes = stream.Read(fileSizeBytes, 0, 4);
int dataLength = BitConverter.ToInt32(fileSizeBytes, 0);
int bytesLeft = dataLength;
byte[] data = new byte[dataLength];
int bufferSize = 1024;
int bytesRead = 0;
while (bytesLeft > 0)
{
int curDataSize = Math.Min(bufferSize, bytesLeft);
if (client.Available < curDataSize)
curDataSize = client.Available; //This saved me
bytes = stream.Read(data, bytesRead, curDataSize);
bytesRead += curDataSize;
bytesLeft -= curDataSize;
}
return data;
}
source to share