在 C# 中通过网络发送大文件的好方法?

发布于 2024-07-13 04:20:49 字数 220 浏览 7 评论 0原文

我正在尝试构建一个应用程序,该应用程序可以从网络中另一台计算机上运行的服务请求文件。 这些文件可能相当大(有时 500mb 以上)。 我正在考虑通过 TCP 发送它,但我担心它可能需要将整个文件存储在内存中。

可能只有一个客户。 复制到共享目录也是不可接受的。 唯一需要的通信是客户端说“gimme xyz”,然后服务器发送它(以及确保正确发生的一切)。

有什么建议么?

I am trying to build an application that can request files from a service running on another machine in the network. These files can be fairly large (500mb + at times). I was looking into sending it via TCP but I'm worried that it may require that the entire file be stored in memory.

There will probably only be one client. Copying to a shared directory isn't acceptable either. The only communication required is for the client to say "gimme xyz" and the server to send it (and whatever it takes to ensure this happens correctly).

Any suggestions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

盛装女皇 2024-07-20 04:20:49

这是一个更简单的方法。 使用 BITS(后台智能传输服务)。 它已经内置于 WinXP 和 Vista 中。 它基本上是驱动 Windows 更新的动力。

http://blogs. msdn.com/powershell/archive/2009/01/11/transferring-large-files-using-bits.aspx

http://blogs.msdn.com/jamesfi/archive/2006/12/23/how-to -use-bits-to-transfer-files.aspx

这是某人编写的一个很好的托管 BITS 包装器以及如何使用它。

http://www.codeproject.com/KB/cs/Managed_BITS.aspx

Here is an easier way. Using BITS (Background Intelligent Transfer Service). Its already built into WinXP and Vista. Its basically what drives Windows Updates.

http://blogs.msdn.com/powershell/archive/2009/01/11/transferring-large-files-using-bits.aspx

http://blogs.msdn.com/jamesfi/archive/2006/12/23/how-to-use-bits-to-transfer-files.aspx

Here is a nice managed BITS wrapper someone wrote and how to use it.

http://www.codeproject.com/KB/cs/Managed_BITS.aspx

明天过后 2024-07-20 04:20:49

您可以在.NET 中使用套接字来传输文件和数据。

You can use sockets in .NET to transfer files and data.

浪荡不羁 2024-07-20 04:20:49

您可能需要考虑WCF 流

You might want to consider WCF streaming.

沙沙粒小 2024-07-20 04:20:49

这篇文章可能会对您有所帮助。 它是关于在 .NET 中发送大文件。
检查链接:

http://codetechnic.blogspot。 com/2009/02/sending-large-files-over-tcpip.html

This article may help you. It is about sending large files in .NET.
Check the link:

http://codetechnic.blogspot.com/2009/02/sending-large-files-over-tcpip.html

灯角 2024-07-20 04:20:49

小心 BITS。 这是一个非常好的协议,但不是 Windows 更新程序的关键部分。 我们发现几乎没有任何企业客户允许将 BITS 更新到他们的计算机上; 因此我们无法构建依赖它的应用程序。

Be careful with BITS. It is a very good protocol but not a critical part of the windows update program. We found that hardly any of our corporate clients allowed the BITS update onto their machines; therefore we couldn't build an app that relied on it.

奢华的一滴泪 2024-07-20 04:20:49

如果 FTP 是一种选择,那么为了简单起见我会选择它。 否则您就会进入 TCP/IP 套接字编程的世界。

If FTP was an option then I'd go with that for the sake of simplicity. Otherwise you're into a world of TCP/IP socket programming.

遥远的她 2024-07-20 04:20:49

通过开源 edtFTPnet 库使用 FTP。 快速而简单。

Use FTP via the open source edtFTPnet library. Fast and simple.

乜一 2024-07-20 04:20:49

使用TransmitFile(这是一个Win32 函数;也许它也是.NET 库的一个方法)。

Use TransmitFile (which is a Win32 function; perhaps it's a method of the .NET library as well).

无可置疑 2024-07-20 04:20:49

如果文件物理存在于计算机上,为什么不将它们放在一个文件夹中,使该文件夹成为 IIS 中的虚拟目录,并使用基于内容的路由和/或 URL 重写将请求路由到它们。

If files exist physically on the machine why not just put them in a folder, make that folder a virtual directory in IIS, and use Content-Based Routing and/or URL Rewriting to route the requests to them.

反差帅 2024-07-20 04:20:49

就我个人而言,我会寻求能够平衡速度、可靠性和经济代码的东西,因此我会将其基于 TCP 网络流。 代码的客户端将如下所示:

internal class Client
{
    private FileStream _fs;     
    private long _expectedLength;

    public void GetFileFromServer(string localFilename)
    {            
        if (File.Exists(localFilename))
            File.Delete(localFilename);

        _fs = new FileStream(localFilename, FileMode.Append);

        var ipEndpointServer = new IPEndPoint(IPAddress.Parse({serverIp}), {serverPort});

        // an object that wraps tcp client
        var client = new TcpClientWrapper(ipEndpointServer, "");
        client.DataReceived += DataReceived;
    }

    private void DataReceived(object sender, DataReceivedEventArgs e)
    {
        var data = e.Data;

        // first packet starts with 4 bytes dedicated to the length of the file
        if (_expectedLength == 0)
        {
            var headerBytes = new byte[4];
            Array.Copy(e.Data, 0, headerBytes, 0, 4);
            _expectedLength = BitConverter.ToInt32(headerBytes, 0);
            data = new byte[e.Data.Length - 4];
            Array.Copy(e.Data, 4, data, 0, data.Length);
        }

        _fs.WriteAsync(e.Data, 0, e.Data.Length);

        if (_fs.Length >= _expectedLength)
        {                                
            // transfer has finished
        }
    }
}

然后有一个服务器类来提供该文件。 请注意,整个文件并未加载到内存中,而是从 FileStream 中分块读取。

internal class Server
{
    private TcpServer _tcpServer;
    private NetworkStream _stream;        

    public void StartServer()
    {
        // fire up a simple Tcp server
        _tcpServer = new TcpServer({serverPort}, "test");
        _tcpServer.ClientConnected += ClientConnected;
    }

    private void ClientConnected(object sender, TcpClientConnectedEventArgs e)
    {            
        // an incoming client has been detected ... send the file to that client!
        _stream = e.Client.GetStream();
        SendFileToClient({pathToFile});
    }

    private void SendFileToClient(string pathToFile)
    {
        // open the file as a stream and send in chunks
        using (var fs = new FileStream(pathToFile, FileMode.Open))
        {
            // send header which is file length
            var headerBytes = new byte[4];
            Buffer.BlockCopy(BitConverter.GetBytes(fs.Length + 4), 0, headerBytes, 0, 4);
            _stream.Write(headerBytes, 0, 4);

            // send file in block sizes of your choosing
            var buffer = new byte[100000];
            int bytesRead = 0;
            while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
            {
                _stream.Write(buffer, 0, bytesRead);
            }
            _stream.Flush();
        }
    }
}

TcpClientWrapper 几乎是带有 System.Net.Sockets.TcpClient 对象和底层 NetworkStream 对象的样板代码。 我实际上也不需要发布这个,但只是为了给出一些指示,它们的构造将包含类似这样的内容:

_tcp = new Net.TcpClient();
_tcp.Connect(remoteEp);
_stream = _tcp.GetStream();
_stream.BeginRead(_receivedData, 0, _receivedData.Length, DataReceivedAsync, null);

并且 DataReceivedAsync 方法是样板套接字数据处理,并且会引发一个事件 o 共享接收到的数据返回给消费者(在本例中为客户端):

private void DataReceivedAsync(IAsyncResult ar)
{
    var receivedBytes = _stream.EndRead(ar);

    if (receivedBytes > 0)
    {
        var data = new byte[receivedBytes];
        Array.Copy(_receivedData, 0, data, 0, receivedBytes);
        DataReceived?.Invoke(this, new DataReceivedEventArgs(data));

        _receivedData = new byte[ReceiveBufferSize];
        _stream.BeginRead(_receivedData, 0, _receivedData.Length, DataReceivedAsync, null);
    }       
}   

将数据从包装器发送回客户端的事件:

public EventHandler<DataReceivedEventArgs> DataReceived;
public class DataReceivedEventArgs : EventArgs
{
    public DataReceivedEventArgs(byte[] data) { Data = data; }
    public byte[] Data { get; }
}

Personally I'd go for something that balances speed, reliability and economical code, so I'd base it on a TCP network stream. The client-side of the code would look like this:

internal class Client
{
    private FileStream _fs;     
    private long _expectedLength;

    public void GetFileFromServer(string localFilename)
    {            
        if (File.Exists(localFilename))
            File.Delete(localFilename);

        _fs = new FileStream(localFilename, FileMode.Append);

        var ipEndpointServer = new IPEndPoint(IPAddress.Parse({serverIp}), {serverPort});

        // an object that wraps tcp client
        var client = new TcpClientWrapper(ipEndpointServer, "");
        client.DataReceived += DataReceived;
    }

    private void DataReceived(object sender, DataReceivedEventArgs e)
    {
        var data = e.Data;

        // first packet starts with 4 bytes dedicated to the length of the file
        if (_expectedLength == 0)
        {
            var headerBytes = new byte[4];
            Array.Copy(e.Data, 0, headerBytes, 0, 4);
            _expectedLength = BitConverter.ToInt32(headerBytes, 0);
            data = new byte[e.Data.Length - 4];
            Array.Copy(e.Data, 4, data, 0, data.Length);
        }

        _fs.WriteAsync(e.Data, 0, e.Data.Length);

        if (_fs.Length >= _expectedLength)
        {                                
            // transfer has finished
        }
    }
}

Then have a server class to serve the file. Note how the whole file isn't loaded into memory, instead it is read in chunks from a FileStream.

internal class Server
{
    private TcpServer _tcpServer;
    private NetworkStream _stream;        

    public void StartServer()
    {
        // fire up a simple Tcp server
        _tcpServer = new TcpServer({serverPort}, "test");
        _tcpServer.ClientConnected += ClientConnected;
    }

    private void ClientConnected(object sender, TcpClientConnectedEventArgs e)
    {            
        // an incoming client has been detected ... send the file to that client!
        _stream = e.Client.GetStream();
        SendFileToClient({pathToFile});
    }

    private void SendFileToClient(string pathToFile)
    {
        // open the file as a stream and send in chunks
        using (var fs = new FileStream(pathToFile, FileMode.Open))
        {
            // send header which is file length
            var headerBytes = new byte[4];
            Buffer.BlockCopy(BitConverter.GetBytes(fs.Length + 4), 0, headerBytes, 0, 4);
            _stream.Write(headerBytes, 0, 4);

            // send file in block sizes of your choosing
            var buffer = new byte[100000];
            int bytesRead = 0;
            while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
            {
                _stream.Write(buffer, 0, bytesRead);
            }
            _stream.Flush();
        }
    }
}

The TcpClientWrapper is pretty much boiler plate code with the System.Net.Sockets.TcpClient object and the underlying NetworkStream object. I don't really need to post this as well, but just to give some pointers ther construction would contain something like this:

_tcp = new Net.TcpClient();
_tcp.Connect(remoteEp);
_stream = _tcp.GetStream();
_stream.BeginRead(_receivedData, 0, _receivedData.Length, DataReceivedAsync, null);

and the DataReceivedAsync method is boilerplate socket data handling and would raise an event o share the received data back to the consumer (the client in this case):

private void DataReceivedAsync(IAsyncResult ar)
{
    var receivedBytes = _stream.EndRead(ar);

    if (receivedBytes > 0)
    {
        var data = new byte[receivedBytes];
        Array.Copy(_receivedData, 0, data, 0, receivedBytes);
        DataReceived?.Invoke(this, new DataReceivedEventArgs(data));

        _receivedData = new byte[ReceiveBufferSize];
        _stream.BeginRead(_receivedData, 0, _receivedData.Length, DataReceivedAsync, null);
    }       
}   

The event to ship data from the wrapper back to the client:

public EventHandler<DataReceivedEventArgs> DataReceived;
public class DataReceivedEventArgs : EventArgs
{
    public DataReceivedEventArgs(byte[] data) { Data = data; }
    public byte[] Data { get; }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文