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();

      

+3


source to share


3 answers


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.

+1


source


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

      

0


source


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

      

0


source







All Articles