简体   繁体   English

C# 套接字发送超过 2 GB 的大文件

[英]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#.我正在尝试通过 C# 中的套接字发送大文件(大于 2 GB)。 I am sending it in chunks of 10 MB.我以 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.它在本地工作正常,但通过网络在服务器端的 .receive() 方法上出现超时异常。

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).相反,只需将文件作为FileStream并将其直接复制到NetworkStream中,您可以使用TcpClient (以及服务器的TcpListener )来获取它。

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.您可能需要编写自己的FileStream派生类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM