【发布时间】:2016-02-17 16:38:57
【问题描述】:
我即将编写一个服务器应用程序,它应该能够从多个来源接收大文件(像所有其他 FTP 客户端/服务器应用程序一样安静)。
但我不确定最好的方法是什么,需要一些建议。
客户端会将 XML 数据发送到服务器,如下所示:
<Data xmlns="http://schemas.datacontract.org/2004/07/DataFiles" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Category>General</Category>
<Files>
<DataFile>
<Extension>.txt</Extension>
<Filename>test4</Filename>
<Bytes>"Some binary data"</Bytes>
</DataFile>
</Files>
</Data>
我开始创建一个 HTTPListener 作为我的服务器,但它似乎在服务器端的大文件上遇到了很多困难(基本上因为上下文是作为一个没有分段的数据包接收的,并且当服务器确实对接收到的 XML 数据进行反序列化,并将其加载到内存中,这对于大文件来说是不行的。
然后我转到 TcpListener 以降低一层,这似乎适用于大文件,因为它们是分段发送的,但让我做很多工作来附加收到请求时在服务器端打包。
我也有可能很快就跳过了 WCF,但我缺乏这项技术的经验,让我再次放弃了这种方法。
你会做什么?你会使用伟大的 .NET 工具箱中的哪个 .NET 工具来创建 FTP 服务器/客户端?
有很多关于 TcpListeners 等的线程,这不是我在这里寻求的。我需要关于我应该采用哪种方法以及最佳做法的建议。
编辑: 忘了提到它背后的想法更像是一个FTP代理(客户端发送文件到服务器>服务器在本地存储文件>服务器将其发送到第三部分位置>服务器在将文件成功发送到第三部分位置时清除本地存储的文件完成)。
编辑 17-11-15:
这是我如何使用 HTTP 服务器的示例代码:
public class HttpServer
{
protected readonly HttpListener HttpListener = new HttpListener();
protected HttpServer(IEnumerable<string> prefixes)
{
HttpListener.Prefixes.Add(prefix);
}
public void Start()
{
while (HttpListener.IsListening && Running)
{
var result = HttpListener.BeginGetContext(ContextReceived, HttpListener);
if (WaitHandle.WaitAny(new[] {result.AsyncWaitHandle, _shutdown}) == 0)
return;
}
}
protected object ReadRequest(HttpListenerRequest request)
{
using (var input = request.InputStream)
using (var reader = new StreamReader(input, request.ContentEncoding))
{
var data = reader.ReadToEnd();
return data;
}
}
protected void ContextReceived(IAsyncResult ar)
{
HttpListenerContext context = null;
HttpListenerResponse response = null;
try
{
var listener = ar.AsyncState as HttpListener;
if (listener == null) throw new InvalidCastException("ar");
context = listener.EndGetContext(ar);
response = context.Response;
switch (context.Request.HttpMethod)
{
case WebRequestMethods.Http.Post:
// Parsing XML data with file at LARGE byte[] as one of the parameter, seems to struggle here...
break;
default:
//Send MethodNotAllowed response..
break;
}
response.Close();
}
catch(Exception ex)
{
//Do some properly exception handling!!
}
finally
{
if (context != null)
{
context.Response.Close();
}
if (response != null)
response.Close();
}
}
}
客户正在使用:
using (var client = new WebClient())
{
GetExtensionHeaders(client.Headers);
client.Encoding = Encoding.UTF8;
client.UploadFileAsync(host, fileDialog.FileName ?? "Test");
client.UploadFileCompleted += ClientOnUploadFileCompleted;
client.UploadProgressChanged += ClientOnUploadProgressChanged;
}
请注意,客户端应该将数据(作为 XML)发送到服务器,服务器将反序列化接收到的数据(使用文件流服务器端),如前所述。
这是我的 TcpServer 示例:
public class TcpServer
{
protected TcpListener Listener;
private bool _running;
public TcpServer(int port)
{
Listener = new TcpListener(IPAddress.Any, port);
Console.WriteLine("Listener started @ {0}:{1}", ((IPEndPoint)Listener.LocalEndpoint).Address, ((IPEndPoint)Listener.LocalEndpoint).Port);
_running = true;
}
protected readonly ManualResetEvent TcpClientConnected = new ManualResetEvent(false);
public void Start()
{
while (_running)
{
TcpClientConnected.Reset();
Listener.BeginAcceptTcpClient(AcceptTcpClientCallback, Listener);
TcpClientConnected.WaitOne(TimeSpan.FromSeconds(5));
}
}
protected void AcceptTcpClientCallback(IAsyncResult ar)
{
try
{
var listener = ar.AsyncState as TcpListener;
if (listener == null) return;
using (var client = listener.EndAcceptTcpClient(ar))
{
using (var stream = client.GetStream())
{
//Append or create to file stream
}
}
//Parse XML data received?
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
TcpClientConnected.Set();
}
}
}
【问题讨论】:
-
我在以下“因为上下文是作为一个未分段的数据包接收的”向您发出呼吁。你能具体解释一下这是什么意思吗?
-
嗯,我注意到的是,当客户端向服务器发送文件时(使用 TcpClient 作为客户端),根据 Wireshark 跟踪,很多 tcp 包被发送到主机/服务器。但是在发送完整的 HTTP/XML 包之前,上下文不存在等待句柄。
-
你确定吗?我已经使用 HttpListener 从客户端读取了非常大的 HTTP 流,而无需等待整个有效负载到达服务器。让我们看一些代码。我认为你做错了。
-
很可能我做错了什么。当我再次回到有线连接时(今天下午),我将发布我的一些代码。
-
Http 使用 TCP 作为传输层。因此,在 tcp 完全传输之前,您不会在 wireshark 上看到 http。确实没有发送任何http,它是tcp。 Wiresharp 在获得所有 tcp 之前不会对 http 进行解码。 FTP 也使用 TCP 作为传输层。 FTP 只是一个在 tcp 上运行的应用程序。一个 tcp 数据包最大约为 1500 字节,并且使用多个 tcp 发送大消息。我可能会对使用最少开销的大文件使用 TCP。
标签: c# .net tcp tcplistener httplistener