我正在尝试构建一个应用程序,可以从网络上运行的另一台计算机的服务请求文件。这些文件可能相当大(有时超过500mb)。我考虑通过TCP发送它,但担心这可能需要将整个文件存储在内存中。
可能只会有一个客户端。复制到共享目录也不可接受。唯一需要通信的是客户端说“给我xyz”,服务器就会发送它(以及确保正确执行所需的任何内容)。
有什么建议吗?
可能只会有一个客户端。复制到共享目录也不可接受。唯一需要通信的是客户端说“给我xyz”,服务器就会发送它(以及确保正确执行所需的任何内容)。
有什么建议吗?
http://codetechnic.blogspot.com/2009/02/sending-large-files-over-tcpip.html
请注意 BITS 协议。它是一种非常好的协议,但不是 Windows 更新程序的关键部分。我们发现,我们的公司客户中几乎没有人允许 BITS 更新到他们的计算机上;因此,我们无法构建一个依赖于它的应用程序。
如果FTP是一种选择的话,基于简单考虑,我会选择FTP。否则你需要了解TCP/IP套接字编程。
使用TransmitFile
(这是一个Win32函数;也可能是.NET库的方法)。
就我个人而言,我会选择平衡速度、可靠性和经济性的代码,因此我会以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();
}
}
}
_tcp = new Net.TcpClient();
_tcp.Connect(remoteEp);
_stream = _tcp.GetStream();
_stream.BeginRead(_receivedData, 0, _receivedData.Length, DataReceivedAsync, null);
DataReceivedAsync
方法是样板套路的套接字数据处理,会触发一个事件将接收到的数据返回给消费者(在这种情况下是客户端):
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; }
}