Bha01
Bha01

Reputation: 50

C# socket sending large file over than 2 GB

I am trying to send the large file(greater than 2 GB) via Sockets in C#. I am sending it in chunks of 10 MB. I will explain what is my approach. From client side, I will send the chunk and will wait for confirmation from server(if that chunk has been received or not). After receving confirmation, I will send another chunk and will again wait for confimation from server for that chunk. The loop will continue till all file data has been sent to. After successful sent operation of file, will close the connection from client side.

Client Side Code:

            try
        {
            DateTime startdate = DateTime.Now;
            string ori_text = btn_Send_Files.Text;
            btn_Send_Files.Text = "Do not click already sending...";
            btn_Send_Files.Refresh();
            IPAddress[] ipAddress = Dns.GetHostAddresses("localhost");
            IPEndPoint ipEnd = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 5656);
            Socket clientSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);
            clientSock.Connect(ipEnd);

            label1.Text = label1.Text + "\nConnected to:" + ipEnd.Address.ToString();
            label1.Refresh();

            string fileName = "CCCCCC.zip"; //It has size greater than 2 GB
            string filePath = @"C:\Users\CCCCCC\Downloads\";
            byte[] fileNameByte = Encoding.ASCII.GetBytes(fileName);

            label1.Text = label1.Text + "\nSending the file:" + fileName;
            label1.Refresh();
            FileInfo fi = new FileInfo(filePath + fileName);
            
            int max = 1024 * 1024 * 10;
            long start = 0, bytesRead = 0;
            FileStream fileStream = new FileStream(filePath + fileName, FileMode.Open, FileAccess.Read);
            max = max - (4 + fileNameByte.Length);
            byte[] tmpserver = new byte[100];

            using (fileStream)
                {
                    int i = 0;
                    while (start < fi.Length)
                    {
                        i += 1;

                        label1.Text = label1.Text + "\nSending " + i.ToString() + " block of file:" + fileName;
                        label1.Refresh();

                        fileStream.Seek(start, SeekOrigin.Begin);
                        if (max > (fi.Length - start))
                            max = Convert.ToInt32(fi.Length - start);

                        byte[] buffer = new byte[max];
                        byte[] clientData = new byte[4 + fileNameByte.Length + max];
                        byte[] fileNameLen = BitConverter.GetBytes(fileNameByte.Length);
                        fileNameLen.CopyTo(clientData, 0);
                        fileNameByte.CopyTo(clientData, 4);

                        bytesRead = fileStream.Read(buffer, 0, max);
                        start += max;
                        buffer.CopyTo(clientData, 4 + fileNameByte.Length);
                        clientSock.Send(clientData);
                        
                        Array.Clear(clientData, 0, clientData.Length);
                        Array.Clear(buffer, 0, buffer.Length);
                        Array.Clear(fileNameLen, 0, fileNameLen.Length);
                        Array.Clear(clientData, 0, clientData.Length);
                        clientData = null;
                        buffer = null;
                        fileNameLen = null;
                        GC.Collect();

                        label1.Text = label1.Text + "\nWaiting for confirmation of " + i.ToString() + " block of file:" + fileName;

                        int serverdata = clientSock.Receive(tmpserver);
                        if (serverdata == 0)
                        {
                            clientSock.Shutdown(SocketShutdown.Both);
                            clientSock.Close();
                            break;
                        }
                        else
                        {
                            label1.Text = label1.Text + "\nReceived confirmation for " + i.ToString() + " block of file:" + fileName;
                            label1.Refresh();
                        }
                    }
                }
               
            
            clientSock.Close();
            DateTime enddate = DateTime.Now;
            TimeSpan diff = startdate - enddate;
            label1.Text = label1.Text + "\nFile "+ fileName + " has been sent to:"+ ipEnd.Address.ToString();
            label1.Text = label1.Text + "\nTime Taken to sent the file(seconds):" + Math.Abs(diff.Seconds).ToString();
            label1.Refresh();
            btn_Send_Files.Text = ori_text;
            btn_Send_Files.Refresh();

        }
        catch (Exception ex)
        {
            MessageBox.Show("File Sending fail." + ex.ToString());
        }

Server Side Code:

            try
        {
            label1.Text = label_text;
            label1.Refresh();
            string ori_btn_text = btn_Start_Server.Text;
            btn_Start_Server.Text = "Do not click already waiting...";
            btn_Start_Server.Refresh();
            label1.Text = label1.Text + "\nWaiting for client";
            label1.Refresh();
            string receivedPath = ConfigurationManager.AppSettings["Location"];
            Socket clientSock = sock.Accept();
            label1.Text = label1.Text + "\nServer Started and connected to client" + clientSock.LocalEndPoint + "\nWaiting for the file to receive";
            label1.Refresh();
            int max = 1024*1024*10;
            
            string logfile = receivedPath + "\\serverlog.log";
            if (File.Exists(logfile))
            {
                File.Delete(logfile);
            }

            FileStream fs = File.Create(logfile);
            byte[] serverdata = new byte[100];
            string fileName = "";
            int fileNameLen = 0, fileDataLen = 0;
            int i = 0;
            BinaryWriter bWrite = null;

            while (true)
                {
                    i += 1;
                    
                    byte[] clientData = new byte[max];

                    int receivedBytesLen = clientSock.Receive(clientData);
                    if (receivedBytesLen <= 0)
                        break;
                    
                    string tmp = "\nStarted:" + i;
                    fs.Write(Encoding.ASCII.GetBytes(tmp), 0, Encoding.ASCII.GetBytes(tmp).Length);
                    if (i == 1)
                    {
                        fileNameLen = BitConverter.ToInt32(clientData, 0);
                        fileName = Encoding.ASCII.GetString(clientData, 4, fileNameLen);
                        fileDataLen = BitConverter.ToInt32(clientData, 4);
                        if (File.Exists(receivedPath + fileName))
                        {
                            File.Delete(receivedPath + fileName);
                        }
                        bWrite = new BinaryWriter(File.Open(receivedPath + fileName, FileMode.Create));
                        label1.Text = label1.Text + "\nReceiving File:" + fileName;                            
                    }

                    label1.Text = label1.Text + "\nReceived " + i.ToString() + " block of file:" + fileName;
                    label1.Refresh();
                    bWrite.Write(clientData, 4 + fileNameLen, receivedBytesLen - 4 - fileNameLen);
                    Array.Clear(clientData, 0, clientData.Length);
                    clientData = null;
                    GC.Collect();
            
                    clientSock.Send(serverdata);
                    label1.Text = label1.Text + "\nSent confirmation for " + i.ToString() + " block of file:" + fileName;
                    label1.Refresh();
                }
                if (bWrite != null)
                {
                    bWrite.Flush();
                    bWrite.Dispose();
                    bWrite.Close();
                }
                fs.Close();
                label1.Text = label1.Text + "\nFile has been received:" + fileName;
                label1.Refresh();
                MessageBox.Show("File has been received:" + fileName);
                btn_Start_Server.Text = ori_btn_text;
                btn_Start_Server.Refresh();            
        }
        catch (Exception ex)
        {
            MessageBox.Show("File Receiving fail." + ex.ToString());
        }

It works fine on locally but over the network getting timeout exception on .receive() method on server side.

Upvotes: 1

Views: 1299

Answers (1)

Charlieface
Charlieface

Reputation: 72298

I strongly suggest you don't try to reinvent the wheel. There is no need to mess around with sockets and chunks of files.

Instead just open the file as a FileStream and copy it straight into a NetworkStream, which you can get using TcpClient (and TcpListener for the server).

You should also convert this code to be fully async.

public async void btn_Send_Files_Click(object sender, EventArgs e)
{
    DateTime startdate = DateTime.Now;
    try
    {
        string ori_text = btn_Send_Files.Text;
        btn_Send_Files.Text = "Do not click already sending...";
        btn_Send_Files.Enabled = false;

        await SendFileAsync();

        label1.Text += $"\nFile {fileName} has been sent to: {ipEnd.Address}\nTime Taken to sent the file(seconds):{Math.Abs(diff.Seconds)}";
    }
    catch (Exception ex)
    {
        MessageBox.Show("File Sending fail." + ex.ToString());
    }
    finally
    {
        DateTime enddate = DateTime.Now;
        TimeSpan diff = startdate - enddate;
        btn_Send_Files.Text = ori_text;
        btn_Send_Files.Enabled = true;
    }
}

private async Task SendFiles()
{
    string fileName = "CCCCCC.zip"; //It has size greater than 2 GB
    string filePath = @"C:\Users\CCCCCC\Downloads\";

    using (var fileStream = new FileStream(Path.Combine(filePath, fileName), FileMode.Open, FileAccess.Read))
    using (var client = new TcpClient())
    {
        await client.ConnectAsync(IPAddress.Any, 5656); // replace with remote IP
        label1.Text += $"\nConnected to:{client.Client.RemoteEndPoint}";
        label1.Refresh();
        using (var netStream = client.GetStream())
        {
            await fileStream.CopyToAsync(netStream);
        }
    }
}

Server-side code

public async void btn_Start_Server_Click(object sender, EventArgs e)
{
    try
    {
        label1.Text = label_text;
        string ori_btn_text = btn_Start_Server.Text;
        btn_Start_Server.Text = "Do not click already waiting...";
        btn_Start_Server.Enabled = false;
        label1.Text = label1.Text + "\nWaiting for client";
        label1.Refresh();

        await WaitForFile("SomeFilename");

        label1.Text = label1.Text + "\nFile has been received:" + fileName;
        MessageBox.Show("File has been received:" + fileName);
    }
    catch (Exception ex)
    {
        MessageBox.Show("File Receiving fail." + ex.ToString());
    }
    finally
    {
        btn_Start_Server.Text = ori_btn_text;
        btn_Start_Server.Enabled = true;
   }
}


private Task WaitForFile(string fileName)
{
    string receivedPath = ConfigurationManager.AppSettings["Location"];
    var listener = new TcpListener(IPAddress.Any, 5656);  // local IP to listen at, do not change
    try
    {
        listener.Start();

        using (var client = await listener.AcceptTcpClientAsync())
        using (var networkStream = client.GetStream())
        using (var fileStream = new FileStream(Path.Combine(receivedPath, fileName), FileMode.Create, FileAccess.Write))
        {
            label1.Text = $"{label1.Text}\nServer Started and connected to client {client.Client.RemoteEndPoint}\nWaiting for the file to receive";
            label1.Refresh();

            await networkStream.CopyToAsync(fileStream);
        }
    }
    finally
    {
        if (listener.Active)
            listener.Stop();
    }
}

Your existing code also passes the filename through the stream. I'm sure you will manage to adapt the above to do the same.

Also, if you want to show progress, it could get more complicated. You may need to write your own FileStream-derived class.

Upvotes: 3

Related Questions