gbr
gbr

Reputation: 41

Sending a file using TCPClient and NetworkStream in C#

I've been 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 send some additional information like the exact file size and the file name, so the server application knows how much there is to read. The funny thing is everything was fine when I tested it on 127.0.0.1 - as soon as I replaced the IP address with the actual one, the server could only read about 1,5 KByte of the data that was sent. It still gets the filename and the file size, but theres no way it's retrieving the actual data.

For testing purposes, I replaced the image I was going to send with a simple string and the transmission went alright, so I suppose there is a problem with sending and receiving the data chunks, but I'm not getting any exceptions on the client side either.

Anyone got an idea? Cheers!

Edit:

Thanks so far, this is what I have got codewise. For the 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();

Upvotes: 4

Views: 24339

Answers (3)

Evilkeeper
Evilkeeper

Reputation: 92

Ok, don't know what I'm doing here, but in case of anyone uses this as referrence.

I got rid of unnecessary copying and done a very important improvement. Your way to calc nextPacketSize is not complete, since there could be less data, than 1024 avaliable, and you'll get extra nulls in between these chunks (I had a lot of headache with that, now so happy to figure out)

I made them as function, needed for several files, so here's code:

client

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;
}

Upvotes: 1

Mr Smith
Mr Smith

Reputation: 3486

I used some of your code for a test networking project I'm working on. I tweaked a couple things to fit the requirements of my project, but one alteration I needed to make to the original code is to add a "-4" to the line that was setting the bytesLeft variable. After that the code worked. My test file is 52KB & was transmitted successfully.

      // Read the data
    int bytesLeft = dataLength-4;
    byte[] data = new byte[dataLength];

Upvotes: 0

sisve
sisve

Reputation: 19781

About 1.5 KiB sounds like 1500 bytes, "the largest allowed by Ethernet at the network layer". This is the maximum transmission unit (mtu) forcing your network stack to split your file into several small packets.

You need to call the NetworkStream.Read in a loop to read every packet arrived. There's example code of this at MSDN.

Combine this with the default behavior of .NET; consolidating smaller packets to reduce the amount of packets sent, and you'll also see this behavior when sending smaller packets. This can be controlled with ServicePointManager.UseNagleAlgorithm or by using smaller scoped socket options.

Upvotes: 2

Related Questions