1

I'm using BeginSendFile(filePath, socket) to send files to a remote host, it worked before but now visiting the project again whenever I try to send a large file (tested with > 3 GB files) I get this SocketException :

System.Net.Sockets.SocketException: 'The parameter is incorrect'

However, small files doesn't seem to trigger the exception (tested with < 1.3 GB).

Extra info : Windows Defender and Firewall are disabled and no AVs on the machine.

Here is the code I'm using :

Sender :

public static void SendFile(string dstIp, string filePath)
{
    // Establish the local endpoint for the socket.
    IPAddress ipAddr = IPAddress.Parse(dstIp);
    IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 39993);

    // Create a TCP socket.
    Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

    // Connect the socket to the remote endpoint.
    client.Connect(ipEndPoint);

    // Send the file
    client.BeginSendFile(@"C:\Users\username\Desktop\file.ext", SentFileCallback, client);
}

private static void SentFileCallback(IAsyncResult ar)
{
    var client = ar.AsyncState as Socket;

    // Complete sending the data to the remote device.
    client.EndSendFile(ar);

    // Release the socket.
    client.Shutdown(SocketShutdown.Both);
    client.Close();
}

Receiver :

public static async Task StartFileListener()
{
    try
    {
        //Start listening for incoming data
        var listener = new TcpListener(IPAddress.Any, 39993);
        listener.Start();

        while (true)
        {
            using (var client = await listener.AcceptTcpClientAsync())
            {
                using (var stream = client.GetStream())
                {
                    Directory.CreateDirectory(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\test2");

                    using (var output = File.Create(Environment.GetFolderPath(Environment.SpecialFolder.Desktop) + "\\test\\" + "test2.dat"))
                    {
                        //Read the file in chunks of 10MB
                        var buffer = new byte[1024 * 1024 * 10];
                        int bytesRead;
                        while ((bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
                        {
                            await output.WriteAsync(buffer, 0, bytesRead);
                        }
                    }
                }
            }
        }
    }
    catch (Exception e)
    {
        throw e;
    }
}
Majx
  • 131
  • 14
  • is the exception generated on the client side or on the server? – snus74 Aug 13 '20 at 13:38
  • Server side, when I try to send a file, I get the exception not the receiver. – Majx Aug 13 '20 at 13:41
  • ok, perhaps you should cut the file into chunks and get multiple calls to your callback instead of just 1 – snus74 Aug 14 '20 at 07:50
  • The BeginSendFile() method already does this for me, so I don't have to do it by myself. If I have to chunk the file then I have to use Socket.SendTo() since it's designed for sending buffers. – Majx Aug 14 '20 at 13:00
  • Can you please try the code on destination IP of 127.0.0.1 (so you dont have to open a port in router) and confirm the bug when sending a large file? I'm sure there is nothing wrong in the code, but it's either a change in windows which triggers this problem or something interfering with it somehow. – Majx Aug 14 '20 at 13:25
  • I can't try your code atm but here is a nice example of async data sendinc through TCP with more control over completion : https://stackoverflow.com/questions/9170126/how-to-use-socket-sendasync-to-send-large-data – snus74 Aug 14 '20 at 13:53
  • One of the things that made me choose BeginSendFile over the other options is the flexibility of it, it can adjust the buffer size based on the download speed of the receiver by itself. Sometimes more buffer is good sometimes not but BeginSendFile takes care of it. It seems like big files causes an instant crash in BeginSendFile. Microsoft documentation doesn't mention anything about file size limits but the max file size I tried before was around 1.3 GB and it worked but now trying again with a larger file size 3 GB leads to this exception. – Majx Aug 14 '20 at 14:51
  • Hmm maybe it is indeed a bug, try opening a ticket to reach support from microsoft – snus74 Aug 15 '20 at 15:07
  • Yes, I actually did open a ticket yesterday and waiting for their answer. The ticket can be found here : https://developercommunity.visualstudio.com/content/problem/1151057/socketbeginsendfile-throws-socketexception-the-par.html It will take some time until they respond. Thanks for following along and helping. – Majx Aug 15 '20 at 20:06

1 Answers1

1

This answer points out that SendFile uses WinSock TransmitFile underneath. Its documentation states that

The maximum number of bytes that can be transmitted using a single call to the TransmitFile function is 2,147,483,646

As a solution, I ended up using DotNetZip to compress and split-zip the big file (as suggested here) before sending:

int segmentsCreated;
using (ZipFile zip = new ZipFile())
{
   zip.AddDirectory(filePath);
   zip.MaxOutputSegmentSize = int.MaxValue - 1;
   zip.Save("bigFile.zip");

   segmentsCreated = zip.NumberOfSegmentsForMostRecentSave;
}

This generates bigFile.zip, bigFile.z01, bigFile.z02, ... which can then be safely, one by one, sent to the client using the SendFile method.

Riko
  • 433
  • 1
  • 6
  • 20
  • 1
    It is pretty weird that BeginSendFile's documentation does not mention the maximum file size at all and requires a deeper look at its implementation to know the limits. – Majx Aug 05 '21 at 23:20
  • 1
    Agreed, not sure why it's not mentioned in the documentation. I've read that this limitation is only present on Windows and that it works just fine on other operating systems. There should still be a note about that though, especially when the error message on its own doesn't give much useful information. – Riko Aug 09 '21 at 08:05